diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..e1dce34a
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,14 @@
+module.exports = {
+ "env": {
+ "browser": true,
+ "jquery": true,
+ "es6": true
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaVersion": 6
+ },
+ "rules": {
+ "no-unused-vars": ["error", { "vars": "local" }]
+ }
+}
diff --git a/.rubocop.yml b/.rubocop.yml
index 42be2b6c..9a529428 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -3,6 +3,8 @@ inherit_from: .rubocop_todo.yml
require:
- rubocop-rails
- rubocop-rspec
+ - rubocop-capybara
+ - rubocop-factory_bot
AllCops:
NewCops: enable
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 9401bdbd..1d011fa1 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,37 +1,60 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
-# on 2022-02-05 12:00:00 UTC using RuboCop version 1.25.1.
+# on 2024-03-15 10:33:02 UTC using RuboCop version 1.60.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
-# Cop supports --auto-correct.
-# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
-# Include: **/*.gemfile, **/Gemfile, **/gems.rb
-Bundler/OrderedGems:
+# Offense count: 13
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: link_or_button, strict
+Capybara/ClickLinkOrButtonStyle:
Exclude:
- - 'Gemfile'
+ - 'spec/integration/balancing_spec.rb'
-# Offense count: 9
-# Cop supports --auto-correct.
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: DefaultSelector.
+Capybara/RSpec/HaveSelector:
+ Exclude:
+ - 'spec/integration/product_distribution_example_spec.rb'
+
+# Offense count: 4
+Capybara/SpecificMatcher:
+ Exclude:
+ - 'spec/integration/login_spec.rb'
+ - 'spec/integration/session_spec.rb'
+
+# Offense count: 5
# Configuration parameters: Include.
-# Include: **/*.gemspec
-Gemspec/RequireMFA:
+# Include: **/*_spec.rb, **/spec/**/*, **/test/**/*, **/features/support/factories/**/*.rb
+FactoryBot/FactoryAssociationWithStrategy:
+ Exclude:
+ - 'spec/factories/group_order.rb'
+ - 'spec/factories/invoice.rb'
+ - 'spec/factories/order.rb'
+
+# Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Include.
+# Include: **/*_spec.rb, **/spec/**/*, **/test/**/*, **/features/support/factories/**/*.rb
+FactoryBot/SyntaxMethods:
+ Exclude:
+ - 'spec/integration/articles_spec.rb'
+ - 'spec/models/article_spec.rb'
+
+# Offense count: 2
+# Configuration parameters: EnforcedStyle, AllowedGems, Include.
+# SupportedStyles: Gemfile, gems.rb, gemspec
+# Include: **/*.gemspec, **/Gemfile, **/gems.rb
+Gemspec/DevelopmentDependencies:
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:
@@ -45,69 +68,200 @@ Gemspec/RequiredRubyVersion:
- 'plugins/uservoice/foodsoft_uservoice.gemspec'
- 'plugins/wiki/foodsoft_wiki.gemspec'
-# Offense count: 8
-# Configuration parameters: IgnoredMethods.
-Lint/AmbiguousBlockAssociation:
+# Offense count: 379
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: with_first_argument, with_fixed_indentation
+Layout/ArgumentAlignment:
+ Enabled: false
+
+# Offense count: 7
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: with_first_element, with_fixed_indentation
+Layout/ArrayAlignment:
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'
+ - 'app/controllers/articles_controller.rb'
+ - 'app/controllers/stockit_controller.rb'
+ - 'app/helpers/group_orders_helper.rb'
+ - 'app/models/article.rb'
+ - 'app/models/order.rb'
+ - 'plugins/wiki/app/models/page.rb'
-# Offense count: 4
-# Cop supports --auto-correct.
-Lint/AmbiguousOperator:
+# Offense count: 7
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: IndentationWidth.
+Layout/AssignmentIndentation:
Exclude:
- 'app/mailers/mailer.rb'
- - 'spec/models/order_article_spec.rb'
+ - 'config/environments/production.rb'
+ - 'lib/article_units_lib.rb'
+ - 'spec/models/article_spec.rb'
+
+# Offense count: 54
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleAlignWith.
+# SupportedStylesAlignWith: either, start_of_block, start_of_line
+Layout/BlockAlignment:
+ Enabled: false
+
+# Offense count: 54
+# This cop supports safe autocorrection (--autocorrect).
+Layout/BlockEndNewline:
+ Enabled: false
+
+# Offense count: 9
+# This cop supports safe autocorrection (--autocorrect).
+Layout/ClosingParenthesisIndentation:
+ Exclude:
+ - 'app/documents/order_by_articles.rb'
+ - 'app/models/group_order.rb'
+ - 'app/models/group_order_article.rb'
+ - 'lib/article_units_lib.rb'
+ - 'lib/order_pdf.rb'
# Offense count: 4
-# Cop supports --auto-correct.
-Lint/AmbiguousOperatorPrecedence:
+# This cop supports safe autocorrection (--autocorrect).
+Layout/ElseAlignment:
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/helpers/group_order_articles_helper.rb'
+ - 'app/helpers/orders_helper.rb'
+ - 'app/models/group_order.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLineAfterGuardClause:
+ Exclude:
+ - 'app/models/article_category.rb'
+ - 'app/models/invoice.rb'
+ - 'app/models/order.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLines:
+ Exclude:
+ - 'app/models/invoice.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLinesAroundMethodBody:
+ Exclude:
+ - 'app/models/article_category.rb'
+ - 'app/models/invoice.rb'
+ - 'app/models/order.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleAlignWith, Severity.
+# SupportedStylesAlignWith: keyword, variable, start_of_line
+Layout/EndAlignment:
+ Exclude:
+ - 'app/helpers/group_order_articles_helper.rb'
+ - 'app/helpers/orders_helper.rb'
+ - 'app/models/group_order.rb'
+
+# Offense count: 9
+# 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/documents/order_by_articles.rb'
+ - 'app/models/group_order.rb'
+ - 'app/models/group_order_article.rb'
+ - 'lib/article_units_lib.rb'
+ - 'lib/order_pdf.rb'
# Offense count: 2
-# Cop supports --auto-correct.
-Lint/AmbiguousRegexpLiteral:
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_brackets
+Layout/FirstArrayElementIndentation:
+ Exclude:
+ - 'app/controllers/articles_controller.rb'
+
+# Offense count: 421
+# 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:
+ Enabled: false
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: normal, indented_internal_methods
+Layout/IndentationConsistency:
Exclude:
- 'app/models/article_category.rb'
- - 'lib/foodsoft/expansion_variables.rb'
+ - 'app/models/order.rb'
+
+# Offense count: 115
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Width, AllowedPatterns.
+Layout/IndentationWidth:
+ Enabled: false
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: symmetrical, new_line, same_line
+Layout/MultilineArrayBraceLayout:
+ Exclude:
+ - 'app/controllers/articles_controller.rb'
+
+# Offense count: 8
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: symmetrical, new_line, same_line
+Layout/MultilineMethodCallBraceLayout:
+ Exclude:
+ - 'app/documents/order_by_articles.rb'
+ - 'app/models/group_order.rb'
+ - 'app/models/group_order_article.rb'
+ - 'lib/article_units_lib.rb'
+ - 'lib/order_pdf.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: with_first_parameter, with_fixed_indentation
+Layout/ParameterAlignment:
+ Exclude:
+ - 'app/helpers/orders_helper.rb'
+
+# Offense count: 320
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowInHeredoc.
+Layout/TrailingWhitespace:
+ Enabled: false
# Offense count: 40
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Enabled: false
-# Offense count: 3
-# Cop supports --auto-correct.
-Lint/BigDecimalNew:
+# Offense count: 1
+Lint/BinaryOperatorWithIdenticalOperands:
Exclude:
- - 'app/models/group_order.rb'
- - 'config/initializers/extensions.rb'
+ - 'app/helpers/orders_helper.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/BooleanSymbol:
Exclude:
- 'app/models/delivery.rb'
-# Offense count: 2
-# Cop supports --auto-correct.
-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'
+ - 'plugins/wiki/app/controllers/pages_controller.rb'
# Offense count: 3
Lint/DuplicateMethods:
@@ -123,25 +277,35 @@ Lint/EmptyBlock:
- 'spec/factories/group_order_article_quantity.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# Configuration parameters: AllowComments.
+Lint/EmptyFile:
+ Exclude:
+ - 'db_shared/seeds.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
Lint/EnsureReturn:
Exclude:
- 'app/controllers/finance/bank_accounts_controller.rb'
-# Offense count: 2
+# Offense count: 1
+Lint/FloatComparison:
+ Exclude:
+ - 'lib/article_units_lib.rb'
+
+# Offense count: 1
Lint/IneffectiveAccessModifier:
Exclude:
- 'lib/foodsoft_mail_receiver.rb'
- - 'lib/token_verifier.rb'
# Offense count: 1
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/InterpolationCheck:
Exclude:
- 'db/migrate/007_create_article_prices.rb'
# Offense count: 1
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/Loop:
Exclude:
- 'app/models/concerns/mark_as_deleted_with_name.rb'
@@ -152,47 +316,22 @@ Lint/MixedRegexpCaptureTypes:
- 'lib/bank_transaction_reference.rb'
- 'lib/foodsoft_mail_receiver.rb'
-# Offense count: 1
-# Cop supports --auto-correct-all.
+# Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/NonDeterministicRequireOrder:
Exclude:
+ - 'lib/tasks/seeds.rake'
- 'spec/spec_helper.rb'
-# Offense count: 5
-# Cop supports --auto-correct.
-Lint/ParenthesesAsGroupedExpression:
- Exclude:
- - 'spec/integration/articles_spec.rb'
- - 'spec/lib/token_verifier_spec.rb'
- - 'spec/models/order_article_spec.rb'
-
# Offense count: 1
Lint/ReturnInVoidContext:
Exclude:
- 'lib/foodsoft_config.rb'
-# Offense count: 8
-# Cop supports --auto-correct.
-Lint/ScriptPermission:
- Exclude:
- - 'Rakefile'
- - 'plugins/discourse/Rakefile'
- - 'plugins/documents/Rakefile'
- - 'plugins/links/Rakefile'
- - 'plugins/messages/Rakefile'
- - 'plugins/polls/Rakefile'
- - 'plugins/printer/Rakefile'
- - 'plugins/wiki/Rakefile'
-
-# Offense count: 7
-# Cop supports --auto-correct.
-Lint/SendWithMixinArgument:
+# Offense count: 1
+Lint/SelfAssignment:
Exclude:
- - 'config/initializers/doorkeeper.rb'
- - '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'
+ - 'db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb'
# Offense count: 1
# Configuration parameters: IgnoreImplicitReferences.
@@ -200,7 +339,7 @@ Lint/ShadowedArgument:
Exclude:
- 'app/helpers/deliveries_helper.rb'
-# Offense count: 8
+# Offense count: 6
Lint/ShadowingOuterLocalVariable:
Exclude:
- 'app/documents/order_matrix.rb'
@@ -209,9 +348,8 @@ Lint/ShadowingOuterLocalVariable:
- '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'
-# Offense count: 2
+# Offense count: 3
# Configuration parameters: AllowComments, AllowNil.
Lint/SuppressedException:
Exclude:
@@ -225,57 +363,14 @@ Lint/UnderscorePrefixedVariableName:
Exclude:
- 'app/models/order_article.rb'
-# Offense count: 16
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
-Lint/UselessAccessModifier:
- Exclude:
- - 'lib/token_verifier.rb'
- - 'plugins/messages/app/models/messagegroup.rb'
-
-# Offense count: 14
+# Offense count: 17
+# This cop supports unsafe autocorrection (--autocorrect-all).
Lint/UselessAssignment:
Exclude:
- 'app/controllers/admin/ordergroups_controller.rb'
- 'app/helpers/admin/configs_helper.rb'
- 'app/inputs/date_picker_time_input.rb'
+ - 'app/models/article.rb'
- 'app/models/order_article.rb'
- 'db/migrate/003_create_suppliers.rb'
- 'db/migrate/004_create_article_meta.rb'
@@ -285,56 +380,51 @@ Lint/UselessAssignment:
- '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'
+ - 'spec/models/article_spec.rb'
-# Offense count: 1
-# Configuration parameters: CheckForMethodsWithNoSideEffects.
-Lint/Void:
- Exclude:
- - 'lib/foodsoft_config.rb'
-
-# Offense count: 160
-# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
+# Offense count: 183
+# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
- Max: 143
+ Max: 145
-# Offense count: 73
-# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
-# IgnoredMethods: refine
+# Offense count: 27
+# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
+# AllowedMethods: refine
Metrics/BlockLength:
- Max: 374
+ Max: 222
# Offense count: 6
# Configuration parameters: CountBlocks.
Metrics/BlockNesting:
- Max: 5
+ Max: 4
-# Offense count: 18
+# Offense count: 21
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
- Max: 288
+ Max: 335
-# Offense count: 51
-# Configuration parameters: IgnoredMethods.
+# Offense count: 63
+# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/CyclomaticComplexity:
Max: 22
-# Offense count: 158
-# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
+# Offense count: 187
+# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
Metrics/MethodLength:
- Max: 112
+ Max: 115
# Offense count: 4
# Configuration parameters: CountComments, CountAsOne.
Metrics/ModuleLength:
- Max: 190
+ Max: 194
# Offense count: 1
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
Metrics/ParameterLists:
Max: 6
-# Offense count: 36
-# Configuration parameters: IgnoredMethods.
+# Offense count: 48
+# Configuration parameters: AllowedMethods, AllowedPatterns.
Metrics/PerceivedComplexity:
Max: 23
@@ -353,15 +443,16 @@ Naming/BlockParameterName:
- 'db/migrate/008_create_orders.rb'
# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyleForLeadingUnderscores.
# SupportedStylesForLeadingUnderscores: disallowed, required, optional
Naming/MemoizedInstanceVariableName:
Exclude:
- 'plugins/messages/app/models/message.rb'
-# Offense count: 19
+# Offense count: 18
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
-# AllowedNames: 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'
@@ -374,7 +465,6 @@ Naming/MethodParameterName:
- '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.
@@ -394,14 +484,8 @@ Naming/PredicateName:
- 'app/serializers/order_serializer.rb'
- 'plugins/messages/app/models/message.rb'
-# Offense count: 45
-# Cop supports --auto-correct.
-# Configuration parameters: PreferredName.
-Naming/RescuedExceptionsVariableName:
- Enabled: false
-
# Offense count: 22
-# Configuration parameters: EnforcedStyle, AllowedIdentifiers.
+# Configuration parameters: EnforcedStyle, AllowedIdentifiers, AllowedPatterns.
# SupportedStyles: snake_case, camelCase
Naming/VariableName:
Exclude:
@@ -411,9 +495,9 @@ Naming/VariableName:
- 'lib/bank_account_information_importer.rb'
# Offense count: 23
-# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers.
+# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
# SupportedStyles: snake_case, normalcase, non_integer
-# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339
+# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
Naming/VariableNumber:
Exclude:
- 'app/documents/order_matrix.rb'
@@ -433,7 +517,7 @@ RSpec/BeforeAfterAll:
- 'spec/lib/foodsoft_mail_receiver_spec.rb'
# Offense count: 9
-# Cop supports --auto-correct.
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnabledMethods.
RSpec/Capybara/FeatureMethods:
Exclude:
@@ -448,7 +532,7 @@ RSpec/Capybara/FeatureMethods:
- 'spec/integration/supplier_spec.rb'
# Offense count: 27
-# Configuration parameters: Prefixes.
+# Configuration parameters: Prefixes, AllowedPatterns.
# Prefixes: when, with, without
RSpec/ContextWording:
Exclude:
@@ -457,18 +541,25 @@ RSpec/ContextWording:
- '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/product_distribution_example_spec.rb'
+ - 'spec/integration/receive_spec.rb'
+ - 'spec/integration/session_spec.rb'
+ - 'spec/integration/supplier_spec.rb'
-# Offense count: 101
-# Cop supports --auto-correct.
+# Offense count: 103
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SkipBlocks, EnforcedStyle.
# SupportedStyles: described_class, explicit
RSpec/DescribedClass:
Exclude:
+ - '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'
@@ -479,113 +570,51 @@ RSpec/DescribedClass:
- 'spec/models/ordergroup_spec.rb'
- 'spec/models/user_spec.rb'
-# Offense count: 9
-# Cop supports --auto-correct.
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowConsecutiveOneLiners.
RSpec/EmptyLineAfterExample:
Exclude:
- - 'spec/integration/session_spec.rb'
- - 'spec/models/user_spec.rb'
+ - 'spec/api/v1/swagger_spec.rb'
-# Offense count: 6
-# Cop supports --auto-correct.
-RSpec/EmptyLineAfterExampleGroup:
- Exclude:
- - 'spec/models/order_article_spec.rb'
+# Offense count: 71
+# Configuration parameters: CountAsOne.
+RSpec/ExampleLength:
+ Max: 81
-# Offense count: 30
-# Cop supports --auto-correct.
-RSpec/EmptyLineAfterFinalLet:
+# Offense count: 7
+# Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly.
+# Include: **/*_spec*rb*, **/spec/**/*
+RSpec/FilePath:
Exclude:
- 'spec/api/v1/order_articles_spec.rb'
- - 'spec/api/v1/swagger_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/balancing_spec.rb'
- 'spec/integration/login_spec.rb'
- - 'spec/integration/supplier_spec.rb'
- - 'spec/models/order_article_spec.rb'
- - 'spec/models/order_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/support/api_helper.rb'
+ - 'spec/lib/bank_account_information_importer_spec.rb'
-# Offense count: 17
-# Cop supports --auto-correct.
-RSpec/EmptyLineAfterHook:
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: single_line_only, single_statement_only, disallow, require_implicit
+RSpec/ImplicitSubject:
+ Exclude:
+ - 'spec/api/v1/swagger_spec.rb'
+
+# Offense count: 59
+# Configuration parameters: Max, AllowedIdentifiers, AllowedPatterns.
+RSpec/IndexedLet:
Exclude:
- 'spec/api/v1/swagger_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/balancing_spec.rb'
- - 'spec/integration/login_spec.rb'
+ - 'spec/integration/receive_spec.rb'
+ - 'spec/models/bank_transaction_spec.rb'
- 'spec/models/order_article_spec.rb'
- - 'spec/support/api_oauth.rb'
-
-# Offense count: 59
-# Configuration parameters: CountAsOne.
-RSpec/ExampleLength:
- Max: 81
-
-# Offense count: 7
-# Cop supports --auto-correct.
-# Configuration parameters: CustomTransform, IgnoredWords.
-RSpec/ExampleWording:
- Exclude:
- - 'spec/models/order_spec.rb'
-
-# Offense count: 5
-# Cop supports --auto-correct.
-# Configuration parameters: Include.
-# Include: spec/factories.rb, spec/factories/**/*.rb, features/support/factories/**/*.rb
-RSpec/FactoryBot/FactoryClassName:
- Exclude:
- - 'spec/factories/article.rb'
- - 'spec/factories/doorkeeper.rb'
- - 'spec/factories/supplier.rb'
-
-# Offense count: 2
-# Cop supports --auto-correct.
-RSpec/FactoryBot/SyntaxMethods:
- Exclude:
- - 'spec/integration/articles_spec.rb'
- - 'spec/models/article_spec.rb'
-
-# Offense count: 7
-# 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
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: implicit, each, example
-RSpec/HookArgument:
- Exclude:
- - 'spec/spec_helper.rb'
-
-# Offense count: 3
-# Cop supports --auto-correct.
-RSpec/HooksBeforeExamples:
- Exclude:
- - 'spec/integration/balancing_spec.rb'
- - 'spec/lib/foodsoft_mail_receiver_spec.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: single_line_only, single_statement_only, disallow
-RSpec/ImplicitSubject:
- Exclude:
- - 'spec/api/v1/swagger_spec.rb'
+ - 'spec/models/ordergroup_spec.rb'
# Offense count: 6
# Configuration parameters: AssignmentOnly.
@@ -599,12 +628,6 @@ RSpec/IteratedExpectation:
- 'spec/models/order_spec.rb'
- 'spec/models/supplier_spec.rb'
-# Offense count: 3
-# Cop supports --auto-correct.
-RSpec/LetBeforeExamples:
- Exclude:
- - 'spec/api/v1/swagger_spec.rb'
-
# Offense count: 13
RSpec/LetSetup:
Exclude:
@@ -622,38 +645,29 @@ RSpec/MissingExampleGroupArgument:
- 'spec/models/group_order_spec.rb'
- 'spec/models/user_spec.rb'
-# Offense count: 87
+# Offense count: 89
RSpec/MultipleExpectations:
Max: 22
-# Offense count: 80
+# Offense count: 82
# Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers:
Max: 17
+# Offense count: 7
+# Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
+# SupportedStyles: always, named_only
+RSpec/NamedSubject:
+ Exclude:
+ - 'spec/api/v1/swagger_spec.rb'
+
# Offense count: 29
+# Configuration parameters: AllowedGroups.
RSpec/NestedGroups:
Max: 6
-# Offense count: 31
-# Cop supports --auto-correct.
-# 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'
-
# Offense count: 8
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers.
# SupportedStyles: inflected, explicit
RSpec/PredicateMatcher:
@@ -661,6 +675,24 @@ RSpec/PredicateMatcher:
- 'spec/models/article_spec.rb'
- 'spec/models/user_spec.rb'
+# Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
+RSpec/Rails/HaveHttpStatus:
+ Exclude:
+ - 'spec/api/v1/user/financial_transactions_spec.rb'
+ - 'spec/api/v1/user/group_order_articles_spec.rb'
+
+# Offense count: 7
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: not_to, be_invalid
+RSpec/Rails/NegationBeValid:
+ Exclude:
+ - 'spec/models/article_spec.rb'
+ - 'spec/models/order_spec.rb'
+ - 'spec/models/supplier_spec.rb'
+ - 'spec/models/user_spec.rb'
+
# Offense count: 6
RSpec/RepeatedDescription:
Exclude:
@@ -674,20 +706,18 @@ RSpec/RepeatedExample:
- 'spec/lib/bank_transaction_reference_spec.rb'
- 'spec/lib/foodsoft_mail_receiver_spec.rb'
-# Offense count: 9
-# Cop supports --auto-correct.
-RSpec/ScatteredLet:
- Exclude:
- - 'spec/api/v1/swagger_spec.rb'
- - 'spec/api/v1/user/group_order_articles_spec.rb'
- - 'spec/api/v1/user/ordergroup_spec.rb'
-
# Offense count: 7
-RSpec/ScatteredSetup:
+# Configuration parameters: Include, CustomTransform, IgnoreMethods, IgnoreMetadata.
+# Include: **/*_spec.rb
+RSpec/SpecFilePathFormat:
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/balancing_spec.rb'
+ - 'spec/integration/articles_spec.rb'
- 'spec/integration/login_spec.rb'
+ - 'spec/lib/bank_account_information_importer_spec.rb'
# Offense count: 1
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
@@ -695,37 +725,19 @@ RSpec/VerifiedDoubles:
Exclude:
- 'spec/support/api_oauth.rb'
-# Offense count: 45
-# Cop supports --auto-correct.
+# Offense count: 44
+# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/ActiveRecordAliases:
Enabled: false
-# Offense count: 3
-# Cop supports --auto-correct.
-# 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'
-
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: Include.
-# Include: db/migrate/*.rb
-Rails/AddColumnIndex:
- Exclude:
- - 'db/migrate/20171002000000_create_financial_links.rb'
-
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/ApplicationMailer:
Exclude:
- 'app/mailers/mailer.rb'
# Offense count: 20
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/ApplicationRecord:
Exclude:
- 'app/models/supplier_category.rb'
@@ -744,26 +756,43 @@ Rails/ApplicationRecord:
- 'plugins/printer/app/models/printer_job.rb'
- 'plugins/printer/app/models/printer_job_update.rb'
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent.
-Rails/Blank:
- Exclude:
- - 'app/controllers/api/v1/base_controller.rb'
+# Offense count: 33
+# Configuration parameters: Database, Include.
+# SupportedDatabases: mysql, postgresql
+# Include: db/**/*.rb
+Rails/BulkChangeTable:
+ Exclude:
+ - 'db/migrate/20130624085246_remove_weekly_task_from_groups.rb'
+ - 'db/migrate/20130930132511_add_quantities_to_order_article.rb'
+ - 'db/migrate/20150227161931_add_replyto_and_groupid_to_messages.rb'
+ - 'db/migrate/20160217164552_add_break_to_group.rb'
+ - 'db/migrate/20160218151041_add_attachment_to_invoice.rb'
+ - 'db/migrate/20160226000000_add_email_to_message.foodsoft_messages_engine.rb'
+ - 'db/migrate/20160309153440_create_doorkeeper_tables.rb'
+ - 'db/migrate/20171002000000_create_financial_links.rb'
+ - 'db/migrate/20171201000000_add_name_short_to_financial_transaction_type.rb'
+ - 'db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb'
+ - 'db/migrate/20181201000200_add_deleted_to_financial_transaction_type.rb'
+ - 'db/migrate/20221026102300_alter_articles_add_versioning.rb'
+ - 'db/migrate/20221026102301_alter_articles_add_more_unit_logic.rb'
+ - 'db/migrate/20230414122017_alter_suppliers_sharing_fields.rb'
-# Offense count: 1
-# Cop supports --auto-correct.
-Rails/ContentTag:
+# Offense count: 4
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/CompactBlank:
Exclude:
- - 'plugins/wiki/app/helpers/pages_helper.rb'
+ - 'app/controllers/orders_controller.rb'
+ - 'app/helpers/order_articles_helper.rb'
+ - 'spec/integration/config_spec.rb'
-# Offense count: 33
+# Offense count: 35
# Configuration parameters: Include.
-# Include: db/migrate/*.rb
+# Include: db/**/*.rb
Rails/CreateTableWithTimestamps:
Enabled: false
# Offense count: 5
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle, AllowToTime.
# SupportedStyles: strict, flexible
Rails/Date:
@@ -774,47 +803,19 @@ Rails/Date:
- 'spec/integration/order_spec.rb'
- 'spec/models/order_spec.rb'
-# Offense count: 66
-# Cop supports --auto-correct-all.
+# Offense count: 70
+# 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
-# Cop supports --auto-correct.
-Rails/EagerEvaluationLogMessage:
- Exclude:
- - 'db/migrate/20130718183101_migrate_user_settings.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: Include.
-# Include: app/models/**/*.rb
-Rails/EnumHash:
- Exclude:
- - 'app/models/order.rb'
-
-# Offense count: 9
-# 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
-# Cop supports --auto-correct.
-# Configuration parameters: Include, IgnoredMethods.
-# Include: app/models/**/*.rb
-# IgnoredMethods: order, limit, select, lock
+# Offense count: 19
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+# AllowedMethods: order, limit, select, lock
Rails/FindEach:
Exclude:
- 'app/models/bank_account.rb'
@@ -822,21 +823,28 @@ Rails/FindEach:
- 'app/models/ordergroup.rb'
- 'app/models/periodic_task_group.rb'
- 'app/models/task.rb'
+ - 'db/migrate/20090120184410_road_to_version_three.rb'
+ - 'db/migrate/20090731132547_add_stats_to_groups.rb'
+ - 'db/migrate/20130622095040_move_weekly_tasks.rb'
+ - 'db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb'
+ - 'db/migrate/20210205090257_introduce_received_state_in_orders.rb'
+ - 'db/seeds/seed_helper.rb'
+ - 'lib/financial_transactions_csv.rb'
+ - 'lib/order_txt.rb'
-# Offense count: 26
+# Offense count: 24
# 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/article_version.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'
@@ -852,27 +860,20 @@ Rails/HelperInstanceVariable:
- 'app/helpers/application_helper.rb'
- 'app/helpers/orders_helper.rb'
-# Offense count: 14
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: numeric, symbolic
-Rails/HttpStatus:
+# Offense count: 1
+Rails/I18nLocaleTexts:
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'
+ - 'plugins/messages/app/controllers/messages_controller.rb'
-# Offense count: 2
-# Cop supports --auto-correct.
-Rails/IndexBy:
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+Rails/IndexWith:
Exclude:
- - 'app/models/order.rb'
- - 'spec/api/v1/user/ordergroup_spec.rb'
+ - 'app/helpers/application_helper.rb'
+ - 'lib/foodsoft_config.rb'
+ - 'plugins/wiki/app/controllers/pages_controller.rb'
-# Offense count: 23
+# Offense count: 20
# Configuration parameters: IgnoreScopes, Include.
# Include: app/models/**/*.rb
Rails/InverseOf:
@@ -884,8 +885,6 @@ Rails/InverseOf:
- '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'
@@ -894,28 +893,21 @@ Rails/InverseOf:
# Offense count: 2
# Configuration parameters: Include.
-# Include: app/controllers/**/*.rb
+# Include: app/controllers/**/*.rb, app/mailers/**/*.rb
Rails/LexicallyScopedActionFilter:
Exclude:
- 'app/controllers/group_orders_controller.rb'
- 'app/controllers/suppliers_controller.rb'
-# Offense count: 1
-# Cop supports --auto-correct.
-Rails/LinkToBlank:
- Exclude:
- - 'app/helpers/application_helper.rb'
-
-# Offense count: 3
-# Cop supports --auto-correct-all.
+# Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/NegateInclude:
Exclude:
- 'app/helpers/application_helper.rb'
- - 'app/models/supplier.rb'
- 'lib/tasks/foodsoft_setup.rake'
# Offense count: 34
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Include.
# Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb
Rails/Output:
@@ -934,7 +926,7 @@ Rails/Output:
- 'db/migrate/20090120184410_road_to_version_three.rb'
- 'db/migrate/20130622095040_move_weekly_tasks.rb'
-# Offense count: 28
+# Offense count: 25
Rails/OutputSafety:
Exclude:
- 'app/helpers/admin/configs_helper.rb'
@@ -946,147 +938,169 @@ Rails/OutputSafety:
- 'plugins/wiki/app/helpers/pages_helper.rb'
# Offense count: 1
-# Cop supports --auto-correct.
-Rails/Pluck:
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/Pick:
Exclude:
- - 'lib/ordergroups_csv.rb'
+ - 'app/controllers/finance/financial_links_controller.rb'
-# Offense count: 2
-# Cop supports --auto-correct.
-Rails/PluralizationGrammar:
+# Offense count: 3
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/Pluck:
Exclude:
- - 'app/controllers/application_controller.rb'
- - 'lib/tasks/foodsoft.rake'
+ - 'app/controllers/articles_controller.rb'
+ - 'lib/ordergroups_csv.rb'
+ - 'spec/api/v1/order_articles_spec.rb'
# Offense count: 1
-# Cop supports --auto-correct.
-Rails/Presence:
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: conservative, aggressive
+Rails/PluckInWhere:
Exclude:
- - 'db/migrate/021_remove_table_article_prices.rb'
-
-# Offense count: 36
-# Cop supports --auto-correct.
-# Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank.
-Rails/Present:
- Enabled: false
+ - 'db/migrate/20140921104907_remove_stale_memberships.rb'
-# Offense count: 6
-# Cop supports --auto-correct-all.
+# Offense count: 9
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Include.
# Include: **/Rakefile, **/*.rake
Rails/RakeEnvironment:
Exclude:
+ - 'lib/tasks/db_shared.rake'
- 'lib/tasks/foodsoft_setup.rake'
- 'lib/tasks/resque.rake'
-# Offense count: 3
-# Cop supports --auto-correct.
-Rails/RedundantForeignKey:
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowedReceivers.
+# AllowedReceivers: ActionMailer::Preview, ActiveSupport::TimeZone
+Rails/RedundantActiveRecordAllMethod:
Exclude:
- - 'app/models/financial_transaction.rb'
- - 'plugins/messages/app/models/message.rb'
+ - 'db/migrate/20181201000100_create_message_recipients.foodsoft_messages.rb'
-# Offense count: 1
-# Cop supports --auto-correct.
+# Offense count: 14
+# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/RedundantPresenceValidationOnBelongsTo:
Exclude:
+ - 'app/models/article_version.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: 4
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/RootPathnameMethods:
+ Exclude:
+ - 'config/application.rb'
+ - 'lib/tasks/foodsoft_setup.rake'
+ - 'lib/tasks/seeds.rake'
# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: ConvertTry.
-Rails/SafeNavigation:
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/SelectMap:
Exclude:
- - 'app/models/group_order_article.rb'
+ - 'lib/foodsoft/expansion_variables.rb'
-# Offense count: 63
+# Offense count: 71
# 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:
Enabled: false
# Offense count: 3
-# Cop supports --auto-correct.
+# 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'
-# Offense count: 34
-# Cop supports --auto-correct.
+# Offense count: 10
+# Configuration parameters: Include.
+# Include: db/**/*.rb
+Rails/ThreeStateBooleanColumn:
+ Exclude:
+ - 'db/migrate/014_create_tasks.rb'
+ - 'db/migrate/20090120184410_road_to_version_three.rb'
+ - 'db/migrate/20090811144901_add_weekly_to_tasks.rb'
+ - 'db/migrate/20120929155541_add_ignore_apple_restriction_to_groups.rb'
+ - 'db/migrate/20121216180646_remove_assigned_from_tasks.rb'
+ - 'db/migrate/20130624084223_remove_weekly_from_tasks.rb'
+ - 'db/migrate/20130624085246_remove_weekly_task_from_groups.rb'
+
+# Offense count: 39
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: strict, flexible
Rails/TimeZone:
Enabled: false
-# Offense count: 3
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/ToSWithArgument:
+ Exclude:
+ - 'plugins/wiki/app/views/pages/all.rss.builder'
+
+# Offense count: 1
+# Configuration parameters: TransactionMethods.
+Rails/TransactionExitStatement:
+ Exclude:
+ - 'app/models/bank_transaction.rb'
+
+# Offense count: 8
# Configuration parameters: Include.
# Include: app/models/**/*.rb
Rails/UniqueValidationWithoutIndex:
Exclude:
- '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.
+# Configuration parameters: Severity, Environments.
# Environments: development, test, production
Rails/UnknownEnv:
Exclude:
- 'config/initializers/gaffe.rb'
- 'config/initializers/secret_token.rb'
-# Offense count: 68
-# Cop supports --auto-correct.
-# Configuration parameters: Include.
-# Include: app/models/**/*.rb
-Rails/Validation:
- Enabled: false
-
# Offense count: 2
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Rails/WhereEquals:
Exclude:
- 'app/controllers/finance/invoices_controller.rb'
- 'app/models/financial_transaction.rb'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: exists, where
Rails/WhereExists:
Exclude:
- 'app/models/concerns/mark_as_deleted_with_name.rb'
-# Offense count: 2
-# Cop supports --auto-correct.
-Rails/WhereNot:
- Exclude:
- - 'db/migrate/20140921104907_remove_stale_memberships.rb'
- - 'db/migrate/20210205090257_introduce_received_state_in_orders.rb'
-
-# Offense count: 5
-# Cop supports --auto-correct.
+# 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'
-
-# Offense count: 3
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, conditionals
Style/AndOr:
@@ -1096,68 +1110,30 @@ Style/AndOr:
- 'plugins/documents/app/controllers/documents_controller.rb'
- 'spec/support/coverage.rb'
-# Offense count: 19
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
+# Offense count: 50
+# This cop supports safe autocorrection (--autocorrect).
+# 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
-# IgnoredMethods: lambda, proc, it
+# 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'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: AllowOnConstant.
-Style/CaseEquality:
+ Enabled: false
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowOnConstant, AllowOnSelfClass.
+Style/CaseEquality:
Exclude:
- 'lib/tasks/foodsoft_setup.rake'
-# Offense count: 7
-# Cop supports --auto-correct-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'
-
-# Offense count: 53
-# Cop supports --auto-correct.
+# Offense count: 55
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Enabled: false
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: is_a?, kind_of?
-Style/ClassCheck:
- Exclude:
- - 'app/helpers/orders_helper.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: IgnoredMethods.
-# IgnoredMethods: ==, equal?, eql?
-Style/ClassEqualityComparison:
- Exclude:
- - 'spec/factories/supplier.rb'
-
# Offense count: 3
Style/ClassVars:
Exclude:
@@ -1165,39 +1141,8 @@ Style/ClassVars:
- 'lib/foodsoft/expansion_variables.rb'
- 'lib/foodsoft_mail_receiver.rb'
-# Offense count: 3
-# Cop supports --auto-correct.
-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
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, AllowInnerBackticks.
-# SupportedStyles: backticks, percent_x, mixed
-Style/CommandLiteral:
- Exclude:
- - 'lib/tasks/foodsoft_setup.rake'
-
-# Offense count: 10
-# Cop supports --auto-correct.
-# 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'
-
# Offense count: 12
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/CommentedKeyword:
Exclude:
- 'app/controllers/deliveries_controller.rb'
@@ -1208,106 +1153,29 @@ Style/CommentedKeyword:
- 'config/routes.rb'
- 'db/migrate/20090120184410_road_to_version_three.rb'
-# Offense count: 13
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-Style/DefWithParentheses:
- Exclude:
- - 'app/models/user.rb'
-
-# Offense count: 314
+# Offense count: 348
# Configuration parameters: AllowedConstants.
Style/Documentation:
Enabled: false
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: allowed_in_returns, forbidden
Style/DoubleNegation:
Exclude:
- 'app/controllers/tasks_controller.rb'
-# Offense count: 5
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
+# Offense count: 1
+# 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
-# Cop supports --auto-correct.
-Style/EmptyLiteral:
- Exclude:
- - 'plugins/wiki/app/helpers/pages_helper.rb'
-
-# Offense count: 14
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-Style/ExpandPathArguments:
- Enabled: false
-
-# Offense count: 7
-# Cop supports --auto-correct.
-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'
-
-# Offense count: 4
-# Cop supports --auto-correct.
-Style/FileWrite:
- Exclude:
- - 'config/initializers/secret_token.rb'
- - 'lib/order_txt.rb'
- - 'lib/render_csv.rb'
- - 'lib/render_pdf.rb'
+ - 'app/models/group_order.rb'
# Offense count: 1
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv
Style/FloatDivision:
@@ -1315,7 +1183,7 @@ Style/FloatDivision:
- 'app/models/ordergroup.rb'
# Offense count: 18
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: each, for
Style/For:
@@ -1331,59 +1199,61 @@ Style/For:
- 'plugins/messages/app/mail_receivers/messages_mail_receiver.rb'
- 'plugins/wiki/app/views/pages/all.rss.builder'
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: format, sprintf, percent
-Style/FormatString:
- Exclude:
- - 'lib/order_txt.rb'
-
# Offense count: 6
-# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, IgnoredMethods.
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
# SupportedStyles: annotated, template, unannotated
+# AllowedMethods: redirect
Style/FormatStringToken:
EnforcedStyle: unannotated
-# Offense count: 490
-# Cop supports --auto-correct.
+# Offense count: 515
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Enabled: false
# Offense count: 3
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/GlobalStdStream:
Exclude:
- 'config/environments/production.rb'
- 'lib/tasks/foodsoft.rake'
- 'lib/tasks/foodsoft_setup.rake'
-# Offense count: 61
-# Configuration parameters: MinBodyLength.
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
Style/GuardClause:
- Enabled: false
-
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: braces, no_braces
-Style/HashAsLastArrayItem:
Exclude:
+ - 'app/models/article_category.rb'
- 'app/models/order.rb'
+ - 'plugins/wiki/app/controllers/pages_controller.rb'
-# Offense count: 6
-# Cop supports --auto-correct.
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowSplatArgument.
Style/HashConversion:
Exclude:
- - 'app/helpers/application_helper.rb'
- - 'app/models/article.rb'
- - 'app/models/order.rb'
- 'lib/bank_account_information_importer.rb'
- - 'plugins/wiki/app/controllers/pages_controller.rb'
- - 'spec/api/v1/user/ordergroup_spec.rb'
+
+# Offense count: 3
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowedReceivers.
+# AllowedReceivers: Thread.current
+Style/HashEachMethods:
+ Exclude:
+ - 'app/controllers/articles_controller.rb'
+ - 'app/models/group_order.rb'
+ - 'spec/integration/config_spec.rb'
+
+# Offense count: 3
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/HashExcept:
+ Exclude:
+ - 'plugins/uservoice/lib/foodsoft_uservoice.rb'
+ - 'spec/models/article_spec.rb'
# Offense count: 8
# Configuration parameters: MinBranchesCount.
@@ -1398,46 +1268,22 @@ Style/HashLikeCase:
- 'plugins/documents/app/controllers/documents_controller.rb'
- 'plugins/wiki/app/controllers/pages_controller.rb'
-# Offense count: 3904
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
-# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
-# SupportedShorthandSyntax: always, never, either
-Style/HashSyntax:
- Enabled: false
-
-# Offense count: 5
-# Cop supports --auto-correct.
-# 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: 60
-# Cop supports --auto-correct.
-Style/IfUnlessModifier:
- Enabled: false
-
-# Offense count: 2
-# Cop supports --auto-correct.
+# 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
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/InfiniteLoop:
Exclude:
- 'lib/order_pdf.rb'
# Offense count: 3
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: InverseMethods, InverseBlocks.
Style/InverseMethods:
Exclude:
@@ -1445,473 +1291,182 @@ Style/InverseMethods:
- 'app/helpers/deliveries_helper.rb'
- 'spec/support/coverage.rb'
-# Offense count: 4
-# Cop supports --auto-correct.
+# Offense count: 6
+# 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/models/article.rb'
+ - 'app/models/financial_transaction.rb'
+ - 'app/models/order_article.rb'
+ - 'app/models/supplier.rb'
+ - 'spec/api/v1/swagger_spec.rb'
# Offense count: 5
-# Cop supports --auto-correct.
+# 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'
# Offense count: 1
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/MapToHash:
Exclude:
- - 'lib/foodsoft_config.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-# Configuration parameters: IgnoredMethods.
-Style/MethodCallWithoutArgsParentheses:
- Exclude:
- - 'plugins/discourse/app/controllers/discourse_login_controller.rb'
+ - 'app/models/article_version.rb'
-# Offense count: 5
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline
-Style/MethodDefParentheses:
+# Offense count: 3
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/MinMaxComparison:
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/group_order_article.rb'
+ - 'lib/apple_bar.rb'
# Offense count: 1
Style/MixinUsage:
Exclude:
- 'lib/tasks/foodsoft_setup.rake'
-# Offense count: 2
+# Offense count: 3
Style/MultilineBlockChain:
Exclude:
- 'app/helpers/group_orders_helper.rb'
- 'app/models/order.rb'
- 'config/initializers/rails6_backports.rb'
-# Offense count: 2
-# Cop supports --auto-correct.
+# Offense count: 33
+# 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: 2
-# Cop supports --auto-correct.
-Style/MultilineIfThen:
- Exclude:
- - 'app/controllers/finance/financial_links_controller.rb'
- - 'lib/bank_account_information_importer.rb'
-
-# Offense count: 12
-# Cop supports --auto-correct.
-Style/MultilineWhenThen:
- Exclude:
- - 'app/controllers/finance/balancing_controller.rb'
- - 'app/helpers/application_helper.rb'
- - 'app/helpers/finance/balancing_helper.rb'
- - 'app/models/order.rb'
+ Enabled: false
-# Offense count: 5
-# Cop supports --auto-correct.
-# Configuration parameters: AllowMethodComparison.
-Style/MultipleComparison:
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+Style/MultilineTernaryOperator:
Exclude:
- - 'app/models/order.rb'
- - 'app/models/order_article.rb'
- - 'spec/models/article_spec.rb'
+ - 'app/helpers/group_order_articles_helper.rb'
+ - 'app/helpers/orders_helper.rb'
+ - 'app/models/group_order.rb'
# Offense count: 24
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: literals, strict
Style/MutableConstant:
Enabled: false
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: both, prefix, postfix
-Style/NegatedIf:
- Exclude:
- - 'app/controllers/orders_controller.rb'
- - 'app/helpers/articles_helper.rb'
-
-# Offense count: 4
-# Cop supports --auto-correct.
-Style/NegatedIfElseCondition:
- Exclude:
- - 'app/controllers/articles_controller.rb'
- - 'app/controllers/concerns/auth.rb'
- - 'app/models/article.rb'
-
-# Offense count: 8
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-# 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'
-
-# Offense count: 2
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
+# Offense count: 65
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
# SupportedStyles: predicate, comparison
-Style/NilComparison:
- Exclude:
- - 'app/controllers/application_controller.rb'
- - 'plugins/wiki/app/helpers/pages_helper.rb'
+Style/NumericPredicate:
+ Enabled: false
-# Offense count: 10
-# Cop supports --auto-correct.
-Style/Not:
+# Offense count: 1
+Style/OpenStructUse:
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'
+ - 'app/controllers/suppliers_controller.rb'
# Offense count: 6
-# Cop supports --auto-correct.
-# Configuration parameters: Strict, AllowedNumbers.
-Style/NumericLiterals:
- MinDigits: 7
-
-# Offense count: 60
-# Cop supports --auto-correct-all.
-# Configuration parameters: EnforcedStyle, IgnoredMethods.
-# SupportedStyles: predicate, comparison
-Style/NumericPredicate:
- Enabled: false
-
-# Offense count: 5
# Configuration parameters: AllowedMethods.
# AllowedMethods: respond_to_missing?
Style/OptionalBooleanParameter:
Exclude:
- 'app/helpers/application_helper.rb'
+ - 'app/helpers/group_order_articles_helper.rb'
- 'app/helpers/orders_helper.rb'
- 'app/models/order_article.rb'
- 'lib/tasks/foodsoft_setup.rake'
# Offense count: 1
-# Cop supports --auto-correct.
-Style/OrAssignment:
- Exclude:
- - 'app/controllers/articles_controller.rb'
-
-# Offense count: 8
-# Cop supports --auto-correct.
-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
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-# Configuration parameters: PreferredDelimiters.
-Style/PercentLiteralDelimiters:
- Enabled: false
-
-# Offense count: 5
-# Cop supports --auto-correct.
-Style/PerlBackrefs:
- Exclude:
- - 'lib/foodsoft/expansion_variables.rb'
- - 'plugins/wiki/app/helpers/pages_helper.rb'
-
-# Offense count: 2
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: short, verbose
Style/PreferredHashMethods:
Exclude:
- 'app/helpers/admin/configs_helper.rb'
- - 'app/helpers/articles_helper.rb'
-
-# Offense count: 14
-# Cop supports --auto-correct.
-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
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-Style/RandomWithOffset:
- Exclude:
- - 'db/migrate/007_create_article_prices.rb'
- - 'db/migrate/008_create_orders.rb'
- - 'db/seeds/seed_helper.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct-all.
-# Configuration parameters: Methods.
-Style/RedundantArgument:
- Exclude:
- - 'app/controllers/articles_controller.rb'
-
-# Offense count: 8
-# Cop supports --auto-correct.
-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
-# Cop supports --auto-correct.
-Style/RedundantConditional:
- Exclude:
- - 'app/models/task.rb'
# Offense count: 3
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: SafeForConstants.
Style/RedundantFetchBlock:
Exclude:
- 'config/puma.rb'
-# Offense count: 2
-# Cop supports --auto-correct.
-Style/RedundantFileExtensionInRequire:
- Exclude:
- - 'db/seeds/small.en.seeds.rb'
- - 'db/seeds/small.nl.seeds.rb'
-
-# Offense count: 5
-# Cop supports --auto-correct.
+# Offense count: 8
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/RedundantInterpolation:
Exclude:
- 'db/migrate/20130718183101_migrate_user_settings.rb'
- - 'lib/order_pdf.rb'
+ - 'lib/order_txt.rb'
+ - 'lib/render_csv.rb'
+ - 'lib/render_pdf.rb'
+ - 'plugins/current_orders/app/documents/multiple_orders_by_groups.rb'
- 'spec/i18n_spec.rb'
- 'spec/models/user_spec.rb'
# Offense count: 1
-# Cop supports --auto-correct.
-Style/RedundantRegexpCharacterClass:
- Exclude:
- - 'plugins/wiki/app/helpers/pages_helper.rb'
-
-# Offense count: 7
-# Cop supports --auto-correct.
-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
-# Cop supports --auto-correct.
-# 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: 82
-# Cop supports --auto-correct.
-Style/RedundantSelf:
- Enabled: false
-
-# Offense count: 1
-# Cop supports --auto-correct.
-Style/RedundantSelfAssignmentBranch:
- Exclude:
- - 'db/migrate/20130718183101_migrate_user_settings.rb'
-
-# Offense count: 1
-# Cop supports --auto-correct-all.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/RedundantSort:
Exclude:
- 'app/models/article_category.rb'
-# Offense count: 3
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-Style/RescueModifier:
+# Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+Style/ReturnNilInPredicateMethodDefinition:
Exclude:
- - 'app/controllers/invites_controller.rb'
+ - 'app/controllers/group_orders_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: 50
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: implicit, explicit
-Style/RescueStandardError:
- Enabled: false
-
-# Offense count: 9
-# Cop supports --auto-correct.
-# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods.
+# Offense count: 11
+# 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/concerns/locale.rb'
- 'app/controllers/group_order_articles_controller.rb'
+ - 'app/models/article.rb'
- 'app/models/article_category.rb'
+ - 'app/models/bank_account.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'
# Offense count: 1
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
Style/SelectByRegexp:
Exclude:
- 'lib/foodsoft_config.rb'
-# Offense count: 1
-# Cop supports --auto-correct.
-Style/SelfAssignment:
- Exclude:
- - 'app/helpers/application_helper.rb'
-
-# Offense count: 16
-# Cop supports --auto-correct.
-# 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'
-
# Offense count: 5
-# Cop supports --auto-correct-all.
+# 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
-# Cop supports --auto-correct.
-# Configuration parameters: AllowModifier.
-Style/SoleNestedConditional:
- Exclude:
- - 'app/controllers/articles_controller.rb'
- - 'app/controllers/concerns/auth.rb'
-
# Offense count: 9
-# Cop supports --auto-correct.
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: RequireEnglish.
-# SupportedStyles: use_perl_names, use_english_names
+# SupportedStyles: use_perl_names, use_english_names, use_builtin_english_names
Style/SpecialGlobalVars:
EnforcedStyle: use_perl_names
-# Offense count: 32
-# Cop supports --auto-correct-all.
+# Offense count: 35
+# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: Mode.
Style/StringConcatenation:
Enabled: false
-# Offense count: 1799
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
-# SupportedStyles: single_quotes, double_quotes
-Style/StringLiterals:
- Enabled: false
-
-# Offense count: 80
-# Cop supports --auto-correct.
-# Configuration parameters: MinSize.
-# SupportedStyles: percent, brackets
-Style/SymbolArray:
- EnforcedStyle: brackets
-
# Offense count: 19
-# Cop supports --auto-correct-all.
-# Configuration parameters: AllowMethodsWithArguments, IgnoredMethods.
-# IgnoredMethods: respond_to, define_method
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
+# AllowedMethods: define_method, mail, respond_to
Style/SymbolProc:
Exclude:
- 'app/controllers/pickups_controller.rb'
@@ -1927,87 +1482,17 @@ Style/SymbolProc:
- 'db/migrate/20090731132547_add_stats_to_groups.rb'
- 'spec/factories/order.rb'
-# Offense count: 4
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-# 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: 3
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyleForMultiline.
-# SupportedStylesForMultiline: comma, consistent_comma, no_comma
-Style/TrailingCommaInHashLiteral:
- Exclude:
- - 'app/controllers/finance/financial_transactions_controller.rb'
- - 'config/initializers/exception_notification.rb'
- - 'lib/bank_account_information_importer.rb'
-
-# Offense count: 8
-# Cop supports --auto-correct.
-# 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
-# Cop supports --auto-correct.
-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'
-
-# Offense count: 1
-# Cop supports --auto-correct.
-Style/WhileUntilModifier:
- Exclude:
- - 'app/models/periodic_task_group.rb'
-
-# Offense count: 11
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, MinSize, 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'
-
# Offense count: 3
-# Cop supports --auto-correct-all.
+# 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'
-# Offense count: 448
-# Cop supports --auto-correct.
-# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
+# Offense count: 805
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
# URISchemes: http, https
Layout/LineLength:
- Max: 420
+ Max: 311
diff --git a/Dockerfile-dev b/Dockerfile-dev
index 255a8ac4..ca7865a5 100644
--- a/Dockerfile-dev
+++ b/Dockerfile-dev
@@ -1,7 +1,7 @@
FROM ruby:2.6
# Install dependencies
-RUN deps='libmagic-dev chromium' && \
+RUN deps='libmagic-dev chromium nodejs' && \
apt-get update && \
apt-get install --no-install-recommends -y $deps && \
rm -Rf /var/lib/apt/lists/* /var/cache/apt/*
diff --git a/Gemfile b/Gemfile
index 1a56b334..0bf0d832 100644
--- a/Gemfile
+++ b/Gemfile
@@ -75,6 +75,7 @@ group :development do
gem 'mailcatcher'
gem 'web-console'
gem 'listen'
+ gem 'byebug'
# Better error output
gem 'better_errors'
diff --git a/Gemfile.lock b/Gemfile.lock
index 90d7272d..4c2d9a37 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -144,6 +144,7 @@ GEM
bullet (7.0.1)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
+ byebug (11.1.3)
capybara (3.36.0)
addressable
matrix
@@ -577,6 +578,7 @@ DEPENDENCIES
bootsnap
bootstrap-datepicker-rails
bullet
+ byebug
capybara
connection_pool
daemons
diff --git a/HACKATHON_TODOS.md b/HACKATHON_TODOS.md
new file mode 100644
index 00000000..87af52de
--- /dev/null
+++ b/HACKATHON_TODOS.md
@@ -0,0 +1,12 @@
+# TODO-no-upstream - whole file
+- Replace all remaining occurrences of `.unit_quantity`
+- Receive orders page:
+ * Determine +/- button functionality for very small or non-existant pack sizes
+ * BE validation of order amount
+ * Investigate total_balance hidden field meaning (old issue)
+ * Formatting: Alignment breaks with numbers that have more than one digit (old issue; but renewed importance due to floats)
+ * Article (version) Field translations of new / moved fields
+
+# POST-merge:
+
+* Close https://github.com/foodcoops/foodsoft/issues/396
diff --git a/Procfile b/Procfile
index ee18cb40..a90d4ede 100644
--- a/Procfile
+++ b/Procfile
@@ -1,4 +1,5 @@
-web: bundle exec rails server --binding=0.0.0.0 --port=$PORT
+# TODO-no-upstream:
+web: bundle exec rails server --binding=0.0.0.0 --port=$PORT; tail -f log/development.log
worker: QUEUE=* bundle exec rake resque:work
mail: bundle exec rake foodsoft:reply_email_smtp_server
cron: supercronic crontab
diff --git a/Rakefile b/Rakefile
old mode 100644
new mode 100755
index 835180b2..e59b186f
--- 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.js
index ebe63685..c07a73f9 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -15,7 +15,13 @@
//= require i18n
//= require i18n/translations
//= require_self
-//= require ordering
+//= require big
+//= require units-converter
+//= require migrate-units-form
+//= require article-form
+//= require unit-conversion-field
+//= require group-order-form
+//= require receive-order-form
//= require stupidtable
//= require touchclick
//= require delta_input
@@ -23,6 +29,18 @@
$.fn.select2.defaults.set('theme', 'bootstrap');
+// on first focus (bubbles up to document), open the menu
+$(document).on('focus', '.select2-selection.select2-selection--single', function () {
+ $(this).closest(".select2-container").siblings('select:enabled').select2('open');
+});
+
+// steal focus during close - only capture once and stop propogation
+$('select.select2').on('select2:closing', function (e) {
+ $(e.target).data("select2").$selection.one('focus focusin', function (e) {
+ e.stopPropagation();
+ });
+});
+
// Load following statements, when DOM is ready
$(function() {
@@ -94,6 +112,10 @@ $(function() {
// trigger timeout to submit form when value was changed
clearTimeout(input.data('submit-timeout-id'));
input.data('submit-timeout-id', setTimeout(function() {
+ if (input.data('multiply-before-submit')) {
+ input.parents('form').find(`input[type="hidden"][name="${input.attr('name')}"]`).remove();
+ input.parents('form').append(``);
+ }
if (input.val() != input.data('old-value')) input.parents('form').submit();
input.removeData('submit-timeout-id');
input.removeData('old-value');
@@ -121,6 +143,10 @@ $(function() {
// Handle ajax errors
// render json: {error: "can't except this!"}, status: :unprocessable_entity
$(document).ajaxError(function(ev, xhr, settings, exception) {
+ if (xhr.statusText === 'abort') {
+ return;
+ }
+
try {
msg = xhr.responseJSON.error;
} catch(err) {
diff --git a/app/assets/javascripts/article-form.js b/app/assets/javascripts/article-form.js
new file mode 100644
index 00000000..88ff935f
--- /dev/null
+++ b/app/assets/javascripts/article-form.js
@@ -0,0 +1,592 @@
+class ArticleForm {
+ constructor(articleUnitRatioTemplate$, articleForm$, units, priceMarkup, multiForm$, unitFieldsIdPrefix, unitFieldsNamePrefix) {
+ try {
+ this.units = units;
+ this.priceMarkup = priceMarkup;
+ this.unitFieldsIdPrefix = unitFieldsIdPrefix === undefined ? 'article_version' : unitFieldsIdPrefix;
+ this.unitFieldsNamePrefix = unitFieldsNamePrefix === undefined ? this.unitFieldsIdPrefix : unitFieldsNamePrefix;
+ this.articleUnitRatioTemplate$ = articleUnitRatioTemplate$;
+ this.articleForm$ = articleForm$;
+ this.unitConversionPopoverTemplate$ = $('#unit_conversion_popover_content_template');
+ this.unit$ = $(`#${this.unitFieldsIdPrefix}_unit`, this.articleForm$);
+ this.customUnitWarning$ = $('.icon-warning-sign', this.articleForm$);
+ this.supplierUnitSelect$ = $(`#${this.unitFieldsIdPrefix}_supplier_order_unit`, this.articleForm$);
+ this.unitRatiosTable$ = $('#fc_base_price', this.articleForm$);
+ this.minimumOrderQuantity$ = $(`#${this.unitFieldsIdPrefix}_minimum_order_quantity`, this.articleForm$);
+ this.billingUnit$ = $(`#${this.unitFieldsIdPrefix}_billing_unit`, this.articleForm$);
+ this.groupOrderGranularity$ = $(`#${this.unitFieldsIdPrefix}_group_order_granularity`, this.articleForm$);
+ this.groupOrderUnit$ = $(`#${this.unitFieldsIdPrefix}_group_order_unit`, this.articleForm$);
+ this.price$ = $(`#${this.unitFieldsIdPrefix}_price`, this.articleForm$);
+ this.priceUnit$ = $(`#${this.unitFieldsIdPrefix}_price_unit`, this.articleForm$);
+ this.tax$ = $(`#${this.unitFieldsIdPrefix}_tax`, this.articleForm$);
+ this.deposit$ = $(`#${this.unitFieldsIdPrefix}_deposit`, this.articleForm$);
+ this.fcPrice$ = $(`#article_fc_price`, this.articleForm$);
+ this.unitsToOrder$ = $('#order_article_units_to_order', this.articleForm$);
+ this.unitsReceived$ = $('#order_article_units_received', this.articleForm$);
+ this.toggleExtraUnitsButton$ = $('.toggle-extra-units', this.articleForm$);
+ this.extraUnits$ = $('.extra-unit-fields', this.articleForm$);
+ this.multiForm$ = multiForm$;
+ const selectContainer$ = this.articleForm$.parents('#modalContainer');
+ this.select2Config = {
+ dropdownParent: selectContainer$.length === 0 ? undefined : selectContainer$
+ };
+
+ this.loadAvailableUnits();
+ this.initializeRegularFormFields();
+
+ this.initializeRatioRows();
+ this.bindAddRatioButton();
+
+ this.setFieldVisibility();
+
+ this.loadRatios();
+ this.prepareRatioDataForSequentialRepresentation();
+ this.convertPriceToPriceUnit();
+ this.initializePriceDisplay();
+ this.initializeOrderedAndReceivedUnits();
+ this.convertOrderedAndReceivedUnits(this.supplierUnitSelect$.val(), this.billingUnit$.val());
+ this.initializeFormSubmitListener();
+ this.initializeToggleExtraUnitsButton();
+ } catch (e) {
+ console.log('Could not initialize article form', e, 'articleUnitRatioTemplate$', articleUnitRatioTemplate$, 'articleForm$', articleForm$, 'units', units, 'priceMarkup', priceMarkup, 'multiForm$', multiForm$, 'unitFieldsIdPrefix', unitFieldsIdPrefix, 'unitFieldsNamePrefix', unitFieldsNamePrefix);
+ }
+ }
+
+ initializePriceDisplay() {
+ mergeJQueryObjects([this.price$, this.priceUnit$]).on('change keyup', () => {
+ const price = parseFloat(this.price$.val());
+ const tax = parseFloat(this.tax$.val());
+ const deposit = parseFloat(this.deposit$.val());
+ const grossPrice = (price + deposit) * (tax / 100 + 1);
+ const fcPrice = grossPrice * (this.priceMarkup / 100 + 1);
+ const priceUnitLabel = this.getUnitLabel(this.priceUnit$.val());
+ this.fcPrice$.find('.price_value').text(isNaN(fcPrice) ? '?' : I18n.l('currency', fcPrice));
+ this.fcPrice$.find('.price_per_text').toggle(priceUnitLabel.trim() !== '');
+ this.fcPrice$.find('.price_unit').text(priceUnitLabel);
+ });
+
+ this.price$.trigger('change');
+ }
+
+ getUnitLabel(unitKey) {
+ if (unitKey === '') {
+ return this.unit$.val();
+ }
+ const unit = this.availableUnits.find((availableUnit) => availableUnit.key === unitKey);
+ if (unit === undefined) {
+ return '?';
+ }
+ return unit.symbol != null ? unit.symbol : unit.label;
+ }
+
+ initializeFormSubmitListener() {
+ (this.multiForm$ === undefined ? this.articleForm$ : this.multiForm$).submit((e) => {
+ try {
+ this.undoSequentialRatioDataRepresentation();
+ this.loadRatios();
+ this.undoPriceConversion();
+ this.undoOrderAndReceivedUnitsConversion();
+ } catch(err) {
+ e.preventDefault();
+ throw err;
+ }
+ });
+ }
+
+ initializeToggleExtraUnitsButton() {
+ if (this.toggleExtraUnitsButton$.length > 0) {
+ this.setExtraUnitsButtonStatus();
+
+ this.toggleExtraUnitsButton$.on('click', (e) => {
+ this.toggleExtraUnits();
+ });
+
+ this.supplierUnitSelect$.on('change', () => this.setExtraUnitsButtonStatus());
+ }
+ }
+
+ setExtraUnitsButtonStatus() {
+ if (this.hasDeviatingExtraUnits()) {
+ this.toggleExtraUnitsButton$.removeClass('default-values');
+ } else {
+ this.toggleExtraUnitsButton$.addClass('default-values');
+ }
+ }
+
+ hasDeviatingExtraUnits() {
+ if ($(`input[name^="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes]"][name$="[quantity]"]`).length > 0) {
+ return true;
+ }
+
+ const supplierOrderUnit = this.supplierUnitSelect$.val();
+ if (supplierOrderUnit !== this.groupOrderUnit$.val() || supplierOrderUnit !== this.billingUnit$.val()) {
+ return true;
+ }
+
+ if (this.minimumOrderQuantity$.val().trim() !== '' || parseFloat(this.groupOrderGranularity$.val().trim()) !== 1) {
+ return true;
+ }
+
+ return false;
+ }
+
+ toggleExtraUnits() {
+ this.setExtraUnitsButtonStatus();
+ $(document).off('mousedown.extra-units');
+ this.extraUnits$.toggleClass('show');
+ this.toggleExtraUnitsButton$.toggleClass('show');
+
+ if (this.extraUnits$.hasClass('show')) {
+ $(document).on('mousedown.extra-units', (e) => {
+ if ($(e.target).parents(this.extraUnits$.selector).length !== 0 || e.target === this.extraUnits$[0] || e.target === this.toggleExtraUnitsButton$[0]) {
+ return;
+ }
+
+ this.toggleExtraUnits();
+ });
+ }
+ }
+
+ getUnitsConverter() {
+ return new UnitsConverter(this.units, this.ratios, this.supplierUnitSelect$.val());
+ }
+
+ getUnitRatio(quantity, inputUnit, outputUnit) {
+ const converter = this.getUnitsConverter();
+ return converter.getUnitRatio(quantity, inputUnit, outputUnit);
+ }
+
+ undoPriceConversion() {
+ const relativePrice = this.price$.val();
+ const priceUnit = this.priceUnit$.val();
+ if (priceUnit === undefined) {
+ // TODO-article-version: StockArticles don't have a price unit?
+ return;
+ }
+ const ratio = this.getUnitRatio(1, priceUnit, this.supplierUnitSelect$.val());
+ const supplierUnitPrice = relativePrice / ratio;
+ const hiddenPriceField$ = $(``);
+ this.articleForm$.append(hiddenPriceField$);
+ }
+
+ undoOrderAndReceivedUnitsConversion() {
+ this.convertOrderedAndReceivedUnits(this.billingUnit$.val(), this.supplierUnitSelect$.val());
+ }
+
+ loadAvailableUnits() {
+ this.availableUnits = Object.entries(this.units)
+ .filter(([, unit]) => unit.visible)
+ .map(([code, unit]) => ({ key: code, label: unit.name, baseUnit: unit.baseUnit, symbol: unit.symbol, aliases: unit.aliases ? unit.aliases : [] }));
+
+ $(`#${this.unitFieldsIdPrefix}_supplier_order_unit`, this.articleForm$).select2(this.select2Config);
+ }
+
+ initializeRegularFormFields() {
+ this.unit$.change(() => {
+ this.setMinimumOrderUnitDisplay();
+ this.updateAvailableBillingAndGroupOrderUnits();
+ this.updateUnitMultiplierLabels();
+ this.updateCustomUnitWarning();
+ });
+ this.updateCustomUnitWarning();
+ this.unit$.keyup(() => this.unit$.trigger('change'));
+
+
+ this.supplierUnitSelect$.change(() => {
+ this.onSupplierUnitChanged();
+ this.updateCustomUnitWarning();
+ });
+ this.onSupplierUnitChanged();
+ }
+
+ updateCustomUnitWarning() {
+ const supplierUnitValueChosen = this.supplierUnitSelect$.val() !== undefined && this.supplierUnitSelect$.val().trim() !== '';
+ if (supplierUnitValueChosen) {
+ this.customUnitWarning$.hide();
+ return;
+ }
+
+ const unitVal = this.unit$.val().trim().toLowerCase();
+ if (unitVal !== '' && (unitVal.match(/[0-9]/) || this.availableUnits.some((unit) => (unit.symbol != null && unit.symbol.toLowerCase() === unitVal) || unit.label.toLowerCase() === unitVal || unit.aliases.some((alias) => alias.toLowerCase() === unitVal)))) {
+ this.customUnitWarning$.show();
+ } else {
+ this.customUnitWarning$.hide();
+ }
+ }
+
+ onSupplierUnitChanged() {
+ const valueChosen = this.supplierUnitSelect$.val() !== undefined && this.supplierUnitSelect$.val().trim() !== '';
+ this.unit$.prop('disabled', valueChosen);
+ this.unit$.toggle(!valueChosen);
+ this.filterAvailableRatioUnits();
+ this.setMinimumOrderUnitDisplay();
+ this.updateAvailableBillingAndGroupOrderUnits();
+ this.updateUnitMultiplierLabels();
+ }
+
+ setMinimumOrderUnitDisplay() {
+ const chosenOptionLabel = this.supplierUnitSelect$.val() !== ''
+ ? $(`option[value="${this.supplierUnitSelect$.val()}"]`, this.supplierUnitSelect$).text()
+ : undefined;
+ const unitVal = $(`#${this.unitFieldsIdPrefix}_unit`).val();
+ this.minimumOrderQuantity$
+ .parents('.input-append')
+ .find('.add-on')
+ .text(chosenOptionLabel !== undefined ? chosenOptionLabel : unitVal);
+
+ const converter = this.getUnitsConverter();
+ if (converter.isUnitSiConversible(this.supplierUnitSelect$.val())) {
+ this.minimumOrderQuantity$.removeAttr('step');
+ } else {
+ this.minimumOrderQuantity$.attr('step', 1);
+ }
+ }
+
+ bindAddRatioButton() {
+ $('*[data-add-ratio]', this.articleForm$).on('click', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ this.onAddRatioClicked();
+ });
+ }
+
+ onAddRatioClicked() {
+ const newRow$ = this.articleUnitRatioTemplate$.clone();
+ $('tbody', this.unitRatiosTable$).append(newRow$);
+
+ const index = $(`input[name^="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes]"][name$="[sort]"]`, this.articleForm$).length
+ + $(`input[name^="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes]"][name$="[_destroy]"]`, this.articleForm$).length;
+
+ const sortField$ = $('[name$="[sort]"]', newRow$);
+ sortField$.val(index);
+
+ const ratioAttributeFields$ = $(`[id^="${this.unitFieldsIdPrefix}_article_unit_ratios_attributes_0_"]`, newRow$);
+ ratioAttributeFields$.each((_, field) => {
+ $(field).attr('name', $(field).attr('name').replace('[0]', `[${index}]`));
+ $(field).attr('id', $(field).attr('id').replace(`${this.unitFieldsIdPrefix}_article_unit_ratios_attributes_0_`, `${this.unitFieldsIdPrefix}_article_unit_ratios_attributes_${index}_`));
+ });
+
+ this.setFieldVisibility();
+
+ this.initializeRatioRows();
+ }
+
+ initializeRatioRows() {
+ $('tr', this.unitRatiosTable$).each((_, row) => {
+ this.initializeRatioRow($(row));
+ });
+
+ this.updateUnitMultiplierLabels();
+ this.filterAvailableRatioUnits();
+ }
+
+ initializeRatioRow(row$) {
+ $('*[data-remove-ratio]', row$)
+ .off('click.article_form_ratio_row')
+ .on('click.article_form_ratio_row', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ this.removeRatioRow($(e.target).closest('tr'));
+ });
+
+ const select$ = $('select[name$="[unit]"]', row$);
+ select$.change(() => {
+ this.filterAvailableRatioUnits(row$)
+ this.updateUnitMultiplierLabels();
+ });
+ select$.select2(this.select2Config);
+ }
+
+ updateUnitMultiplierLabels() {
+ $('tr', this.unitRatiosTable$).each((_, row) => {
+ const row$ = $(row);
+ const aboveUnit = this.findAboveUnit(row$);
+ $('.unit_multiplier', row$).text(aboveUnit);
+ });
+ }
+
+ removeRatioRow(row$) {
+ const index = row$.index() + 1;
+ const id = $(`[name="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes][${index}][id]"]`, this.articleForm$).val();
+ row$.remove();
+
+ $(this.unitRatiosTable$).after($(``));
+ $(this.unitRatiosTable$).after($(``));
+ this.filterAvailableRatioUnits();
+ this.updateUnitMultiplierLabels();
+ this.setFieldVisibility();
+ }
+
+ filterAvailableRatioUnits() {
+ const isUnitOrBaseUnitSelected = (unit, select$) => {
+ const code = select$.val();
+ const selectedUnit = this.units[code];
+ return unit.key !== code && (!unit.baseUnit || !selectedUnit || !selectedUnit.baseUnit || unit.baseUnit !== selectedUnit.baseUnit);
+ };
+
+ let remainingAvailableUnits = this.availableUnits.filter(unit => isUnitOrBaseUnitSelected(unit, this.supplierUnitSelect$));
+
+ $('tr select[name$="[unit]"]', this.unitRatiosTable$).each((_, unitSelect) => {
+ $('option[value!=""]' + remainingAvailableUnits.map(unit => `[value!="${unit.key}"]`).join(''), unitSelect).remove();
+ const missingUnits = remainingAvailableUnits.filter(unit => $(`option[value="${unit.key}"]`, unitSelect).length === 0);
+ for (const missingUnit of missingUnits) {
+ $(unitSelect).append($(``));
+ }
+ remainingAvailableUnits = remainingAvailableUnits.filter(unit => isUnitOrBaseUnitSelected(unit, $(unitSelect)));
+ });
+
+ this.updateAvailableBillingAndGroupOrderUnits();
+ }
+
+ findAboveUnit(row$) {
+ const previousRow$ = row$.prev();
+ if (previousRow$.length > 0) {
+ const unitKey = previousRow$.find('select[name$="[unit]"]').val();
+ const unit = this.availableUnits.find(availableUnit => availableUnit.key === unitKey);
+ if (!unit) {
+ return '?';
+ }
+ return unit.label;
+ } else {
+ const unitKey = this.supplierUnitSelect$.val();
+ if (unitKey !== '') {
+ const unit = this.availableUnits.find(availableUnit => availableUnit.key === unitKey);
+ if (!unit) {
+ return '?';
+ }
+ return unit.label;
+ } else {
+ const unitVal = this.unit$.val();
+ return unitVal ? unitVal : '?';
+ }
+ }
+ }
+
+ updateAvailableBillingAndGroupOrderUnits() {
+ const unitsSelectedAbove = [];
+ if (this.supplierUnitSelect$.val() != '') {
+ const chosenOption$ = $(`option[value="${this.supplierUnitSelect$.val()}"]`, this.supplierUnitSelect$);
+ unitsSelectedAbove.push({ key: chosenOption$.val(), label: chosenOption$.text() });
+ } else {
+ const unitVal = this.unit$.val();
+ unitsSelectedAbove.push({ key: '', label: unitVal ? unitVal : '?' });
+ }
+
+ const selectedRatioUnits = $('tr select[name$="[unit]"]', this.unitRatiosTable$).map((_, ratioSelect) => ({
+ key: $(ratioSelect).val(),
+ label: $(`option[value="${$(ratioSelect).val()}"]`, ratioSelect).text()
+ }))
+ .get()
+ .filter(option => option.key !== '');
+
+ unitsSelectedAbove.push(...selectedRatioUnits);
+
+ const availableUnits = [];
+ for (const unitSelectedAbove of unitsSelectedAbove) {
+ availableUnits.push(unitSelectedAbove, ...this.availableUnits.filter(availableUnit => {
+ if (availableUnit.key === unitSelectedAbove.key) {
+ return false;
+ }
+
+ const otherUnit = this.availableUnits.find(unit => unit.key === unitSelectedAbove.key);
+ return otherUnit !== undefined && otherUnit.baseUnit !== null && availableUnit.baseUnit === otherUnit.baseUnit;
+ }));
+ }
+
+ this.updateUnitsInSelect(availableUnits, this.billingUnit$);
+ this.billingUnit$.parents('.fold-line').css('display', availableUnits.length > 1 ? 'block' : 'none');
+ this.updateUnitsInSelect(availableUnits, this.groupOrderUnit$);
+ this.updateUnitsInSelect(availableUnits, this.priceUnit$);
+ }
+
+ updateUnitsInSelect(units, unitSelect$) {
+ const valueBeforeUpdate = unitSelect$.val();
+
+ unitSelect$.empty();
+ for (const unit of units) {
+ unitSelect$.append($(``));
+ }
+
+ const initialValue = unitSelect$.attr('data-initial-value');
+ if (initialValue) {
+ unitSelect$.val(initialValue);
+ unitSelect$.removeAttr('data-initial-value');
+ } else {
+ if (unitSelect$.find(`option[value="${valueBeforeUpdate}"]`).length > 0) {
+ unitSelect$.val(valueBeforeUpdate);
+ } else {
+ unitSelect$.val(unitSelect$.find('option:first').val());
+ }
+ }
+
+ unitSelect$.trigger('change');
+
+ unitSelect$.parents('.control-group').find('.immutable_unit_label').remove();
+ if (units.length === 1) {
+ unitSelect$.hide();
+ unitSelect$.parents('.control-group').append($(`
${units[0].label}
`))
+ } else {
+ unitSelect$.show();
+ }
+ }
+
+ setFieldVisibility() {
+ const firstUnitRatioQuantity$ = $('tr input[name$="[quantity]"]:first', this.unitRatiosTable$);
+ const firstUnitRatioUnit$ = $('tr select[name$="[unit]"]:first', this.unitRatiosTable$);
+
+ const supplierOrderUnitSet = !!this.unit$.val() || !!this.supplierUnitSelect$.val();
+ const unitRatiosVisible = supplierOrderUnitSet || this.unitRatiosTable$.find('tbody tr').length > 0;
+ this.unitRatiosTable$.parents('.fold-line').toggle(unitRatiosVisible);
+
+ if (!unitRatiosVisible) {
+ $('tbody tr', this.unitRatiosTable$).remove();
+ }
+
+ mergeJQueryObjects([
+ this.unit$,
+ this.supplierUnitSelect$,
+ firstUnitRatioQuantity$,
+ firstUnitRatioUnit$
+ ]).off('change.article_form_visibility')
+ .on('change.article_form_visibility', () =>
+ this.setFieldVisibility()
+ );
+
+ firstUnitRatioQuantity$
+ .off('keyup.article_form_visibility')
+ .on('keyup.article_form_visibility', () => firstUnitRatioQuantity$.trigger('change'));
+ }
+
+ prepareRatioDataForSequentialRepresentation() {
+ const numberOfRatios = $(`input[name^="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes]"][name$="[quantity]"]`).length;
+
+ for (let i = numberOfRatios; i > 1; i--) {
+ const currentField$ = $(`input[name="${this.ratioQuantityFieldNameByIndex(i)}"]`, this.articleForm$);
+ const currentValue = currentField$.val();
+ const previousValue = $(`input[name="${this.ratioQuantityFieldNameByIndex(i - 1)}"]:last`, this.articleForm$).val();
+ currentField$.val(round(currentValue / previousValue));
+ }
+ }
+
+ convertPriceToPriceUnit() {
+ const supplierUnitPrice = this.price$.val();
+ const priceUnit = this.priceUnit$.val();
+ if (priceUnit === undefined) {
+ // TODO-article-version: StockArticles don't have a price unit?
+ return;
+ }
+ const ratio = this.getUnitRatio(1, priceUnit, this.supplierUnitSelect$.val());
+ const relativePrice = round(supplierUnitPrice * ratio);
+ this.price$.val(relativePrice);
+ }
+
+ initializeOrderedAndReceivedUnits() {
+ this.billingUnit$.change(() => {
+ this.updateOrderedAndReceivedUnits();
+ this.initializeOrderedAndReceivedUnitsConverters();
+ });
+ this.billingUnit$.trigger('change');
+ }
+
+ updateOrderedAndReceivedUnits() {
+ const billingUnitKey = this.billingUnit$.val();
+ const billingUnitLabel = this.getUnitLabel(billingUnitKey);
+ const inputs$ = mergeJQueryObjects([this.unitsToOrder$, this.unitsReceived$]);
+ inputs$.parent().find('.unit_label').remove();
+ if (billingUnitLabel.trim() !== '') {
+ inputs$.after($(`${this.getUnitsConverter().isUnitSiConversible(billingUnitKey) ? '' : 'x '}${billingUnitLabel}`));
+ }
+ if (this.previousBillingUnit !== undefined) {
+ this.convertOrderedAndReceivedUnits(this.previousBillingUnit, billingUnitKey);
+ }
+ this.previousBillingUnit = billingUnitKey;
+ }
+
+ convertOrderedAndReceivedUnits(fromUnit, toUnit) {
+ const inputs$ = mergeJQueryObjects([this.unitsToOrder$, this.unitsReceived$]);
+ inputs$.each((_, input) => {
+ const input$ = $(input);
+ const val = input$.val();
+
+ if (val !== '') {
+ try {
+ const convertedValue = this.getUnitRatio(val, fromUnit, toUnit);
+ input$.val(round(convertedValue));
+ } catch (e) {
+ // In some cases it's impossible to perform this conversion - just leave the original value
+ }
+ }
+ });
+ }
+
+ initializeOrderedAndReceivedUnitsConverters() {
+ this.unitsToOrder$.unitConversionField('destroy');
+ this.unitsReceived$.unitConversionField('destroy');
+
+ const opts = {
+ units: this.units,
+ popoverTemplate$: this.unitConversionPopoverTemplate$,
+ ratios: this.ratios,
+ supplierOrderUnit: this.supplierUnitSelect$.val(),
+ customUnit: this.unit$.val(),
+ defaultUnit: this.billingUnit$.val()
+ };
+ this.unitsToOrder$.unitConversionField(opts);
+ this.unitsReceived$.unitConversionField(opts);
+ }
+
+ loadRatios() {
+ this.ratios = [];
+ this.unitRatiosTable$.find('tbody tr').each((_, element) => {
+ const tr$ = $(element);
+ const unit = tr$.find(`select[name^="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes]"][name$="[unit]"]`).val();
+ const quantity = tr$.find(`input[name^="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes]"][name$="[quantity]"]:last`).val();
+ this.ratios.push({ unit, quantity });
+ });
+ }
+
+ undoSequentialRatioDataRepresentation() {
+ let previousValue;
+ $(`input[name^="${this.unitFieldsNamePrefix}[article_unit_ratios_attributes]"][name$="[quantity]"]`).each((_, field) => {
+ let currentField$ = $(field);
+ let quantity = currentField$.val();
+
+ if (previousValue !== undefined) {
+ const td$ = currentField$.closest('td');
+ const name = currentField$.attr('name');
+ const ratioNameRegex = new RegExp(`${escapeForRegex(this.unitFieldsNamePrefix)}\\[article_unit_ratios_attributes\\]\\[([0-9]+)\\]`);
+ const index = name.match(ratioNameRegex)[1];
+ quantity = quantity * previousValue;
+ currentField$ = $(``);
+ td$.append(currentField$);
+ }
+
+ previousValue = quantity;
+ });
+ }
+
+ ratioQuantityFieldNameByIndex(i) {
+ return `${this.unitFieldsNamePrefix}[article_unit_ratios_attributes][${i}][quantity]`;
+ }
+}
+
+
+// TODO: Move those functions to some global js utils file:
+function mergeJQueryObjects(array_of_jquery_objects) {
+ return $($.map(array_of_jquery_objects, function (el) {
+ return el.get();
+ }));
+}
+
+function round(num, precision) {
+ if (precision === undefined) {
+ precision = 3;
+ }
+ const factor = Math.pow(10, precision);
+ return Math.round((num + Number.EPSILON) * factor) / factor;
+}
+
+function escapeForRegex(str) {
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
diff --git a/app/assets/javascripts/big.js b/app/assets/javascripts/big.js
new file mode 100644
index 00000000..2f2be034
--- /dev/null
+++ b/app/assets/javascripts/big.js
@@ -0,0 +1,1043 @@
+/*
+ * big.js v6.2.1
+ * A small, fast, easy-to-use library for arbitrary-precision decimal arithmetic.
+ * Copyright (c) 2022 Michael Mclaughlin
+ * https://github.com/MikeMcl/big.js/LICENCE.md
+ */
+(function (GLOBAL) {
+ 'use strict';
+ var Big,
+
+
+/************************************** EDITABLE DEFAULTS *****************************************/
+
+
+ // The default values below must be integers within the stated ranges.
+
+ /*
+ * The maximum number of decimal places (DP) of the results of operations involving division:
+ * div and sqrt, and pow with negative exponents.
+ */
+ DP = 20, // 0 to MAX_DP
+
+ /*
+ * The rounding mode (RM) used when rounding to the above decimal places.
+ *
+ * 0 Towards zero (i.e. truncate, no rounding). (ROUND_DOWN)
+ * 1 To nearest neighbour. If equidistant, round up. (ROUND_HALF_UP)
+ * 2 To nearest neighbour. If equidistant, to even. (ROUND_HALF_EVEN)
+ * 3 Away from zero. (ROUND_UP)
+ */
+ RM = 1, // 0, 1, 2 or 3
+
+ // The maximum value of DP and Big.DP.
+ MAX_DP = 1E6, // 0 to 1000000
+
+ // The maximum magnitude of the exponent argument to the pow method.
+ MAX_POWER = 1E6, // 1 to 1000000
+
+ /*
+ * The negative exponent (NE) at and beneath which toString returns exponential notation.
+ * (JavaScript numbers: -7)
+ * -1000000 is the minimum recommended exponent value of a Big.
+ */
+ NE = -7, // 0 to -1000000
+
+ /*
+ * The positive exponent (PE) at and above which toString returns exponential notation.
+ * (JavaScript numbers: 21)
+ * 1000000 is the maximum recommended exponent value of a Big, but this limit is not enforced.
+ */
+ PE = 21, // 0 to 1000000
+
+ /*
+ * When true, an error will be thrown if a primitive number is passed to the Big constructor,
+ * or if valueOf is called, or if toNumber is called on a Big which cannot be converted to a
+ * primitive number without a loss of precision.
+ */
+ STRICT = false, // true or false
+
+
+/**************************************************************************************************/
+
+
+ // Error messages.
+ NAME = '[big.js] ',
+ INVALID = NAME + 'Invalid ',
+ INVALID_DP = INVALID + 'decimal places',
+ INVALID_RM = INVALID + 'rounding mode',
+ DIV_BY_ZERO = NAME + 'Division by zero',
+
+ // The shared prototype object.
+ P = {},
+ UNDEFINED = void 0,
+ NUMERIC = /^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i;
+
+
+ /*
+ * Create and return a Big constructor.
+ */
+ function _Big_() {
+
+ /*
+ * The Big constructor and exported function.
+ * Create and return a new instance of a Big number object.
+ *
+ * n {number|string|Big} A numeric value.
+ */
+ function Big(n) {
+ var x = this;
+
+ // Enable constructor usage without new.
+ if (!(x instanceof Big)) return n === UNDEFINED ? _Big_() : new Big(n);
+
+ // Duplicate.
+ if (n instanceof Big) {
+ x.s = n.s;
+ x.e = n.e;
+ x.c = n.c.slice();
+ } else {
+ if (typeof n !== 'string') {
+ if (Big.strict === true && typeof n !== 'bigint') {
+ throw TypeError(INVALID + 'value');
+ }
+
+ // Minus zero?
+ n = n === 0 && 1 / n < 0 ? '-0' : String(n);
+ }
+
+ parse(x, n);
+ }
+
+ // Retain a reference to this Big constructor.
+ // Shadow Big.prototype.constructor which points to Object.
+ x.constructor = Big;
+ }
+
+ Big.prototype = P;
+ Big.DP = DP;
+ Big.RM = RM;
+ Big.NE = NE;
+ Big.PE = PE;
+ Big.strict = STRICT;
+ Big.roundDown = 0;
+ Big.roundHalfUp = 1;
+ Big.roundHalfEven = 2;
+ Big.roundUp = 3;
+
+ return Big;
+ }
+
+
+ /*
+ * Parse the number or string value passed to a Big constructor.
+ *
+ * x {Big} A Big number instance.
+ * n {number|string} A numeric value.
+ */
+ function parse(x, n) {
+ var e, i, nl;
+
+ if (!NUMERIC.test(n)) {
+ throw Error(INVALID + 'number');
+ }
+
+ // Determine sign.
+ x.s = n.charAt(0) == '-' ? (n = n.slice(1), -1) : 1;
+
+ // Decimal point?
+ if ((e = n.indexOf('.')) > -1) n = n.replace('.', '');
+
+ // Exponential form?
+ if ((i = n.search(/e/i)) > 0) {
+
+ // Determine exponent.
+ if (e < 0) e = i;
+ e += +n.slice(i + 1);
+ n = n.substring(0, i);
+ } else if (e < 0) {
+
+ // Integer.
+ e = n.length;
+ }
+
+ nl = n.length;
+
+ // Determine leading zeros.
+ for (i = 0; i < nl && n.charAt(i) == '0';) ++i;
+
+ if (i == nl) {
+
+ // Zero.
+ x.c = [x.e = 0];
+ } else {
+
+ // Determine trailing zeros.
+ for (; nl > 0 && n.charAt(--nl) == '0';);
+ x.e = e - i - 1;
+ x.c = [];
+
+ // Convert string to array of digits without leading/trailing zeros.
+ for (e = 0; i <= nl;) x.c[e++] = +n.charAt(i++);
+ }
+
+ return x;
+ }
+
+
+ /*
+ * Round Big x to a maximum of sd significant digits using rounding mode rm.
+ *
+ * x {Big} The Big to round.
+ * sd {number} Significant digits: integer, 0 to MAX_DP inclusive.
+ * rm {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
+ * [more] {boolean} Whether the result of division was truncated.
+ */
+ function round(x, sd, rm, more) {
+ var xc = x.c;
+
+ if (rm === UNDEFINED) rm = x.constructor.RM;
+ if (rm !== 0 && rm !== 1 && rm !== 2 && rm !== 3) {
+ throw Error(INVALID_RM);
+ }
+
+ if (sd < 1) {
+ more =
+ rm === 3 && (more || !!xc[0]) || sd === 0 && (
+ rm === 1 && xc[0] >= 5 ||
+ rm === 2 && (xc[0] > 5 || xc[0] === 5 && (more || xc[1] !== UNDEFINED))
+ );
+
+ xc.length = 1;
+
+ if (more) {
+
+ // 1, 0.1, 0.01, 0.001, 0.0001 etc.
+ x.e = x.e - sd + 1;
+ xc[0] = 1;
+ } else {
+
+ // Zero.
+ xc[0] = x.e = 0;
+ }
+ } else if (sd < xc.length) {
+
+ // xc[sd] is the digit after the digit that may be rounded up.
+ more =
+ rm === 1 && xc[sd] >= 5 ||
+ rm === 2 && (xc[sd] > 5 || xc[sd] === 5 &&
+ (more || xc[sd + 1] !== UNDEFINED || xc[sd - 1] & 1)) ||
+ rm === 3 && (more || !!xc[0]);
+
+ // Remove any digits after the required precision.
+ xc.length = sd;
+
+ // Round up?
+ if (more) {
+
+ // Rounding up may mean the previous digit has to be rounded up.
+ for (; ++xc[--sd] > 9;) {
+ xc[sd] = 0;
+ if (sd === 0) {
+ ++x.e;
+ xc.unshift(1);
+ break;
+ }
+ }
+ }
+
+ // Remove trailing zeros.
+ for (sd = xc.length; !xc[--sd];) xc.pop();
+ }
+
+ return x;
+ }
+
+
+ /*
+ * Return a string representing the value of Big x in normal or exponential notation.
+ * Handles P.toExponential, P.toFixed, P.toJSON, P.toPrecision, P.toString and P.valueOf.
+ */
+ function stringify(x, doExponential, isNonzero) {
+ var e = x.e,
+ s = x.c.join(''),
+ n = s.length;
+
+ // Exponential notation?
+ if (doExponential) {
+ s = s.charAt(0) + (n > 1 ? '.' + s.slice(1) : '') + (e < 0 ? 'e' : 'e+') + e;
+
+ // Normal notation.
+ } else if (e < 0) {
+ for (; ++e;) s = '0' + s;
+ s = '0.' + s;
+ } else if (e > 0) {
+ if (++e > n) {
+ for (e -= n; e--;) s += '0';
+ } else if (e < n) {
+ s = s.slice(0, e) + '.' + s.slice(e);
+ }
+ } else if (n > 1) {
+ s = s.charAt(0) + '.' + s.slice(1);
+ }
+
+ return x.s < 0 && isNonzero ? '-' + s : s;
+ }
+
+
+ // Prototype/instance methods
+
+
+ /*
+ * Return a new Big whose value is the absolute value of this Big.
+ */
+ P.abs = function () {
+ var x = new this.constructor(this);
+ x.s = 1;
+ return x;
+ };
+
+
+ /*
+ * Return 1 if the value of this Big is greater than the value of Big y,
+ * -1 if the value of this Big is less than the value of Big y, or
+ * 0 if they have the same value.
+ */
+ P.cmp = function (y) {
+ var isneg,
+ x = this,
+ xc = x.c,
+ yc = (y = new x.constructor(y)).c,
+ i = x.s,
+ j = y.s,
+ k = x.e,
+ l = y.e;
+
+ // Either zero?
+ if (!xc[0] || !yc[0]) return !xc[0] ? !yc[0] ? 0 : -j : i;
+
+ // Signs differ?
+ if (i != j) return i;
+
+ isneg = i < 0;
+
+ // Compare exponents.
+ if (k != l) return k > l ^ isneg ? 1 : -1;
+
+ j = (k = xc.length) < (l = yc.length) ? k : l;
+
+ // Compare digit by digit.
+ for (i = -1; ++i < j;) {
+ if (xc[i] != yc[i]) return xc[i] > yc[i] ^ isneg ? 1 : -1;
+ }
+
+ // Compare lengths.
+ return k == l ? 0 : k > l ^ isneg ? 1 : -1;
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big divided by the value of Big y, rounded,
+ * if necessary, to a maximum of Big.DP decimal places using rounding mode Big.RM.
+ */
+ P.div = function (y) {
+ var x = this,
+ Big = x.constructor,
+ a = x.c, // dividend
+ b = (y = new Big(y)).c, // divisor
+ k = x.s == y.s ? 1 : -1,
+ dp = Big.DP;
+
+ if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
+ throw Error(INVALID_DP);
+ }
+
+ // Divisor is zero?
+ if (!b[0]) {
+ throw Error(DIV_BY_ZERO);
+ }
+
+ // Dividend is 0? Return +-0.
+ if (!a[0]) {
+ y.s = k;
+ y.c = [y.e = 0];
+ return y;
+ }
+
+ var bl, bt, n, cmp, ri,
+ bz = b.slice(),
+ ai = bl = b.length,
+ al = a.length,
+ r = a.slice(0, bl), // remainder
+ rl = r.length,
+ q = y, // quotient
+ qc = q.c = [],
+ qi = 0,
+ p = dp + (q.e = x.e - y.e) + 1; // precision of the result
+
+ q.s = k;
+ k = p < 0 ? 0 : p;
+
+ // Create version of divisor with leading zero.
+ bz.unshift(0);
+
+ // Add zeros to make remainder as long as divisor.
+ for (; rl++ < bl;) r.push(0);
+
+ do {
+
+ // n is how many times the divisor goes into current remainder.
+ for (n = 0; n < 10; n++) {
+
+ // Compare divisor and remainder.
+ if (bl != (rl = r.length)) {
+ cmp = bl > rl ? 1 : -1;
+ } else {
+ for (ri = -1, cmp = 0; ++ri < bl;) {
+ if (b[ri] != r[ri]) {
+ cmp = b[ri] > r[ri] ? 1 : -1;
+ break;
+ }
+ }
+ }
+
+ // If divisor < remainder, subtract divisor from remainder.
+ if (cmp < 0) {
+
+ // Remainder can't be more than 1 digit longer than divisor.
+ // Equalise lengths using divisor with extra leading zero?
+ for (bt = rl == bl ? b : bz; rl;) {
+ if (r[--rl] < bt[rl]) {
+ ri = rl;
+ for (; ri && !r[--ri];) r[ri] = 9;
+ --r[ri];
+ r[rl] += 10;
+ }
+ r[rl] -= bt[rl];
+ }
+
+ for (; !r[0];) r.shift();
+ } else {
+ break;
+ }
+ }
+
+ // Add the digit n to the result array.
+ qc[qi++] = cmp ? n : ++n;
+
+ // Update the remainder.
+ if (r[0] && cmp) r[rl] = a[ai] || 0;
+ else r = [a[ai]];
+
+ } while ((ai++ < al || r[0] !== UNDEFINED) && k--);
+
+ // Leading zero? Do not remove if result is simply zero (qi == 1).
+ if (!qc[0] && qi != 1) {
+
+ // There can't be more than one zero.
+ qc.shift();
+ q.e--;
+ p--;
+ }
+
+ // Round?
+ if (qi > p) round(q, p, Big.RM, r[0] !== UNDEFINED);
+
+ return q;
+ };
+
+
+ /*
+ * Return true if the value of this Big is equal to the value of Big y, otherwise return false.
+ */
+ P.eq = function (y) {
+ return this.cmp(y) === 0;
+ };
+
+
+ /*
+ * Return true if the value of this Big is greater than the value of Big y, otherwise return
+ * false.
+ */
+ P.gt = function (y) {
+ return this.cmp(y) > 0;
+ };
+
+
+ /*
+ * Return true if the value of this Big is greater than or equal to the value of Big y, otherwise
+ * return false.
+ */
+ P.gte = function (y) {
+ return this.cmp(y) > -1;
+ };
+
+
+ /*
+ * Return true if the value of this Big is less than the value of Big y, otherwise return false.
+ */
+ P.lt = function (y) {
+ return this.cmp(y) < 0;
+ };
+
+
+ /*
+ * Return true if the value of this Big is less than or equal to the value of Big y, otherwise
+ * return false.
+ */
+ P.lte = function (y) {
+ return this.cmp(y) < 1;
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big minus the value of Big y.
+ */
+ P.minus = P.sub = function (y) {
+ var i, j, t, xlty,
+ x = this,
+ Big = x.constructor,
+ a = x.s,
+ b = (y = new Big(y)).s;
+
+ // Signs differ?
+ if (a != b) {
+ y.s = -b;
+ return x.plus(y);
+ }
+
+ var xc = x.c.slice(),
+ xe = x.e,
+ yc = y.c,
+ ye = y.e;
+
+ // Either zero?
+ if (!xc[0] || !yc[0]) {
+ if (yc[0]) {
+ y.s = -b;
+ } else if (xc[0]) {
+ y = new Big(x);
+ } else {
+ y.s = 1;
+ }
+ return y;
+ }
+
+ // Determine which is the bigger number. Prepend zeros to equalise exponents.
+ if (a = xe - ye) {
+
+ if (xlty = a < 0) {
+ a = -a;
+ t = xc;
+ } else {
+ ye = xe;
+ t = yc;
+ }
+
+ t.reverse();
+ for (b = a; b--;) t.push(0);
+ t.reverse();
+ } else {
+
+ // Exponents equal. Check digit by digit.
+ j = ((xlty = xc.length < yc.length) ? xc : yc).length;
+
+ for (a = b = 0; b < j; b++) {
+ if (xc[b] != yc[b]) {
+ xlty = xc[b] < yc[b];
+ break;
+ }
+ }
+ }
+
+ // x < y? Point xc to the array of the bigger number.
+ if (xlty) {
+ t = xc;
+ xc = yc;
+ yc = t;
+ y.s = -y.s;
+ }
+
+ /*
+ * Append zeros to xc if shorter. No need to add zeros to yc if shorter as subtraction only
+ * needs to start at yc.length.
+ */
+ if ((b = (j = yc.length) - (i = xc.length)) > 0) for (; b--;) xc[i++] = 0;
+
+ // Subtract yc from xc.
+ for (b = i; j > a;) {
+ if (xc[--j] < yc[j]) {
+ for (i = j; i && !xc[--i];) xc[i] = 9;
+ --xc[i];
+ xc[j] += 10;
+ }
+
+ xc[j] -= yc[j];
+ }
+
+ // Remove trailing zeros.
+ for (; xc[--b] === 0;) xc.pop();
+
+ // Remove leading zeros and adjust exponent accordingly.
+ for (; xc[0] === 0;) {
+ xc.shift();
+ --ye;
+ }
+
+ if (!xc[0]) {
+
+ // n - n = +0
+ y.s = 1;
+
+ // Result must be zero.
+ xc = [ye = 0];
+ }
+
+ y.c = xc;
+ y.e = ye;
+
+ return y;
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big modulo the value of Big y.
+ */
+ P.mod = function (y) {
+ var ygtx,
+ x = this,
+ Big = x.constructor,
+ a = x.s,
+ b = (y = new Big(y)).s;
+
+ if (!y.c[0]) {
+ throw Error(DIV_BY_ZERO);
+ }
+
+ x.s = y.s = 1;
+ ygtx = y.cmp(x) == 1;
+ x.s = a;
+ y.s = b;
+
+ if (ygtx) return new Big(x);
+
+ a = Big.DP;
+ b = Big.RM;
+ Big.DP = Big.RM = 0;
+ x = x.div(y);
+ Big.DP = a;
+ Big.RM = b;
+
+ return this.minus(x.times(y));
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big negated.
+ */
+ P.neg = function () {
+ var x = new this.constructor(this);
+ x.s = -x.s;
+ return x;
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big plus the value of Big y.
+ */
+ P.plus = P.add = function (y) {
+ var e, k, t,
+ x = this,
+ Big = x.constructor;
+
+ y = new Big(y);
+
+ // Signs differ?
+ if (x.s != y.s) {
+ y.s = -y.s;
+ return x.minus(y);
+ }
+
+ var xe = x.e,
+ xc = x.c,
+ ye = y.e,
+ yc = y.c;
+
+ // Either zero?
+ if (!xc[0] || !yc[0]) {
+ if (!yc[0]) {
+ if (xc[0]) {
+ y = new Big(x);
+ } else {
+ y.s = x.s;
+ }
+ }
+ return y;
+ }
+
+ xc = xc.slice();
+
+ // Prepend zeros to equalise exponents.
+ // Note: reverse faster than unshifts.
+ if (e = xe - ye) {
+ if (e > 0) {
+ ye = xe;
+ t = yc;
+ } else {
+ e = -e;
+ t = xc;
+ }
+
+ t.reverse();
+ for (; e--;) t.push(0);
+ t.reverse();
+ }
+
+ // Point xc to the longer array.
+ if (xc.length - yc.length < 0) {
+ t = yc;
+ yc = xc;
+ xc = t;
+ }
+
+ e = yc.length;
+
+ // Only start adding at yc.length - 1 as the further digits of xc can be left as they are.
+ for (k = 0; e; xc[e] %= 10) k = (xc[--e] = xc[e] + yc[e] + k) / 10 | 0;
+
+ // No need to check for zero, as +x + +y != 0 && -x + -y != 0
+
+ if (k) {
+ xc.unshift(k);
+ ++ye;
+ }
+
+ // Remove trailing zeros.
+ for (e = xc.length; xc[--e] === 0;) xc.pop();
+
+ y.c = xc;
+ y.e = ye;
+
+ return y;
+ };
+
+
+ /*
+ * Return a Big whose value is the value of this Big raised to the power n.
+ * If n is negative, round to a maximum of Big.DP decimal places using rounding
+ * mode Big.RM.
+ *
+ * n {number} Integer, -MAX_POWER to MAX_POWER inclusive.
+ */
+ P.pow = function (n) {
+ var x = this,
+ one = new x.constructor('1'),
+ y = one,
+ isneg = n < 0;
+
+ if (n !== ~~n || n < -MAX_POWER || n > MAX_POWER) {
+ throw Error(INVALID + 'exponent');
+ }
+
+ if (isneg) n = -n;
+
+ for (;;) {
+ if (n & 1) y = y.times(x);
+ n >>= 1;
+ if (!n) break;
+ x = x.times(x);
+ }
+
+ return isneg ? one.div(y) : y;
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big rounded to a maximum precision of sd
+ * significant digits using rounding mode rm, or Big.RM if rm is not specified.
+ *
+ * sd {number} Significant digits: integer, 1 to MAX_DP inclusive.
+ * rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
+ */
+ P.prec = function (sd, rm) {
+ if (sd !== ~~sd || sd < 1 || sd > MAX_DP) {
+ throw Error(INVALID + 'precision');
+ }
+ return round(new this.constructor(this), sd, rm);
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big rounded to a maximum of dp decimal places
+ * using rounding mode rm, or Big.RM if rm is not specified.
+ * If dp is negative, round to an integer which is a multiple of 10**-dp.
+ * If dp is not specified, round to 0 decimal places.
+ *
+ * dp? {number} Integer, -MAX_DP to MAX_DP inclusive.
+ * rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
+ */
+ P.round = function (dp, rm) {
+ if (dp === UNDEFINED) dp = 0;
+ else if (dp !== ~~dp || dp < -MAX_DP || dp > MAX_DP) {
+ throw Error(INVALID_DP);
+ }
+ return round(new this.constructor(this), dp + this.e + 1, rm);
+ };
+
+
+ /*
+ * Return a new Big whose value is the square root of the value of this Big, rounded, if
+ * necessary, to a maximum of Big.DP decimal places using rounding mode Big.RM.
+ */
+ P.sqrt = function () {
+ var r, c, t,
+ x = this,
+ Big = x.constructor,
+ s = x.s,
+ e = x.e,
+ half = new Big('0.5');
+
+ // Zero?
+ if (!x.c[0]) return new Big(x);
+
+ // Negative?
+ if (s < 0) {
+ throw Error(NAME + 'No square root');
+ }
+
+ // Estimate.
+ s = Math.sqrt(x + '');
+
+ // Math.sqrt underflow/overflow?
+ // Re-estimate: pass x coefficient to Math.sqrt as integer, then adjust the result exponent.
+ if (s === 0 || s === 1 / 0) {
+ c = x.c.join('');
+ if (!(c.length + e & 1)) c += '0';
+ s = Math.sqrt(c);
+ e = ((e + 1) / 2 | 0) - (e < 0 || e & 1);
+ r = new Big((s == 1 / 0 ? '5e' : (s = s.toExponential()).slice(0, s.indexOf('e') + 1)) + e);
+ } else {
+ r = new Big(s + '');
+ }
+
+ e = r.e + (Big.DP += 4);
+
+ // Newton-Raphson iteration.
+ do {
+ t = r;
+ r = half.times(t.plus(x.div(t)));
+ } while (t.c.slice(0, e).join('') !== r.c.slice(0, e).join(''));
+
+ return round(r, (Big.DP -= 4) + r.e + 1, Big.RM);
+ };
+
+
+ /*
+ * Return a new Big whose value is the value of this Big times the value of Big y.
+ */
+ P.times = P.mul = function (y) {
+ var c,
+ x = this,
+ Big = x.constructor,
+ xc = x.c,
+ yc = (y = new Big(y)).c,
+ a = xc.length,
+ b = yc.length,
+ i = x.e,
+ j = y.e;
+
+ // Determine sign of result.
+ y.s = x.s == y.s ? 1 : -1;
+
+ // Return signed 0 if either 0.
+ if (!xc[0] || !yc[0]) {
+ y.c = [y.e = 0];
+ return y;
+ }
+
+ // Initialise exponent of result as x.e + y.e.
+ y.e = i + j;
+
+ // If array xc has fewer digits than yc, swap xc and yc, and lengths.
+ if (a < b) {
+ c = xc;
+ xc = yc;
+ yc = c;
+ j = a;
+ a = b;
+ b = j;
+ }
+
+ // Initialise coefficient array of result with zeros.
+ for (c = new Array(j = a + b); j--;) c[j] = 0;
+
+ // Multiply.
+
+ // i is initially xc.length.
+ for (i = b; i--;) {
+ b = 0;
+
+ // a is yc.length.
+ for (j = a + i; j > i;) {
+
+ // Current sum of products at this digit position, plus carry.
+ b = c[j] + yc[i] * xc[j - i - 1] + b;
+ c[j--] = b % 10;
+
+ // carry
+ b = b / 10 | 0;
+ }
+
+ c[j] = b;
+ }
+
+ // Increment result exponent if there is a final carry, otherwise remove leading zero.
+ if (b) ++y.e;
+ else c.shift();
+
+ // Remove trailing zeros.
+ for (i = c.length; !c[--i];) c.pop();
+ y.c = c;
+
+ return y;
+ };
+
+
+ /*
+ * Return a string representing the value of this Big in exponential notation rounded to dp fixed
+ * decimal places using rounding mode rm, or Big.RM if rm is not specified.
+ *
+ * dp? {number} Decimal places: integer, 0 to MAX_DP inclusive.
+ * rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
+ */
+ P.toExponential = function (dp, rm) {
+ var x = this,
+ n = x.c[0];
+
+ if (dp !== UNDEFINED) {
+ if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
+ throw Error(INVALID_DP);
+ }
+ x = round(new x.constructor(x), ++dp, rm);
+ for (; x.c.length < dp;) x.c.push(0);
+ }
+
+ return stringify(x, true, !!n);
+ };
+
+
+ /*
+ * Return a string representing the value of this Big in normal notation rounded to dp fixed
+ * decimal places using rounding mode rm, or Big.RM if rm is not specified.
+ *
+ * dp? {number} Decimal places: integer, 0 to MAX_DP inclusive.
+ * rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
+ *
+ * (-0).toFixed(0) is '0', but (-0.1).toFixed(0) is '-0'.
+ * (-0).toFixed(1) is '0.0', but (-0.01).toFixed(1) is '-0.0'.
+ */
+ P.toFixed = function (dp, rm) {
+ var x = this,
+ n = x.c[0];
+
+ if (dp !== UNDEFINED) {
+ if (dp !== ~~dp || dp < 0 || dp > MAX_DP) {
+ throw Error(INVALID_DP);
+ }
+ x = round(new x.constructor(x), dp + x.e + 1, rm);
+
+ // x.e may have changed if the value is rounded up.
+ for (dp = dp + x.e + 1; x.c.length < dp;) x.c.push(0);
+ }
+
+ return stringify(x, false, !!n);
+ };
+
+
+ /*
+ * Return a string representing the value of this Big.
+ * Return exponential notation if this Big has a positive exponent equal to or greater than
+ * Big.PE, or a negative exponent equal to or less than Big.NE.
+ * Omit the sign for negative zero.
+ */
+ P.toJSON = P.toString = function () {
+ var x = this,
+ Big = x.constructor;
+ return stringify(x, x.e <= Big.NE || x.e >= Big.PE, !!x.c[0]);
+ };
+
+
+ /*
+ * Return the value of this Big as a primitve number.
+ */
+ P.toNumber = function () {
+ var n = Number(stringify(this, true, true));
+ if (this.constructor.strict === true && !this.eq(n.toString())) {
+ throw Error(NAME + 'Imprecise conversion');
+ }
+ return n;
+ };
+
+
+ /*
+ * Return a string representing the value of this Big rounded to sd significant digits using
+ * rounding mode rm, or Big.RM if rm is not specified.
+ * Use exponential notation if sd is less than the number of digits necessary to represent
+ * the integer part of the value in normal notation.
+ *
+ * sd {number} Significant digits: integer, 1 to MAX_DP inclusive.
+ * rm? {number} Rounding mode: 0 (down), 1 (half-up), 2 (half-even) or 3 (up).
+ */
+ P.toPrecision = function (sd, rm) {
+ var x = this,
+ Big = x.constructor,
+ n = x.c[0];
+
+ if (sd !== UNDEFINED) {
+ if (sd !== ~~sd || sd < 1 || sd > MAX_DP) {
+ throw Error(INVALID + 'precision');
+ }
+ x = round(new Big(x), sd, rm);
+ for (; x.c.length < sd;) x.c.push(0);
+ }
+
+ return stringify(x, sd <= x.e || x.e <= Big.NE || x.e >= Big.PE, !!n);
+ };
+
+
+ /*
+ * Return a string representing the value of this Big.
+ * Return exponential notation if this Big has a positive exponent equal to or greater than
+ * Big.PE, or a negative exponent equal to or less than Big.NE.
+ * Include the sign for negative zero.
+ */
+ P.valueOf = function () {
+ var x = this,
+ Big = x.constructor;
+ if (Big.strict === true) {
+ throw Error(NAME + 'valueOf disallowed');
+ }
+ return stringify(x, x.e <= Big.NE || x.e >= Big.PE, true);
+ };
+
+
+ // Export
+
+
+ Big = _Big_();
+
+ Big['default'] = Big.Big = Big;
+
+ //AMD.
+ if (typeof define === 'function' && define.amd) {
+ define(function () { return Big; });
+
+ // Node and other CommonJS-like environments that support module.exports.
+ } else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Big;
+
+ //Browser.
+ } else {
+ GLOBAL.Big = Big;
+ }
+})(this);
diff --git a/app/assets/javascripts/delta_input.js b/app/assets/javascripts/delta_input.js
index 0ae7f4a5..5c0da86f 100644
--- a/app/assets/javascripts/delta_input.js
+++ b/app/assets/javascripts/delta_input.js
@@ -19,7 +19,8 @@ function data_delta_update(el, direction) {
var delta = $(el).data('delta');
var granularity = $(el).data('granularity');
- var val = $(el).val();
+ var val = $(el).val().replace(',', '.');
+ const valueContainedColon = $(el).val() != val;
var oldval = $.isNumeric(val) ? Number(val) : 0;
var newval = oldval + delta*direction;
@@ -31,11 +32,16 @@ function data_delta_update(el, direction) {
$('button[data-increment='+id+']').attr('disabled', newval>=max ? 'disabled' : null);
// warn when what was entered is not a number
- $(el).toggleClass('error', val!='' && val!='.' && (!$.isNumeric(val) || val < 0));
+ const erroneousValue = val!='' && val!='.' && (!$.isNumeric(val) || val < 0)
+ $(el).toggleClass('error', erroneousValue);
// update field, unless the user is typing
- if (!$(el).is(':focus')) {
- $(el).val(round_float(newval, granularity));
+ if (!$(el).is(':focus') && !erroneousValue) {
+ let roundedValue = String(round_float(newval, granularity));
+ if (valueContainedColon) {
+ roundedValue = roundedValue.replace('.', ',');
+ }
+ $(el).val(roundedValue);
$(el).trigger('changed');
}
}
diff --git a/app/assets/javascripts/group-order-form.js b/app/assets/javascripts/group-order-form.js
new file mode 100644
index 00000000..8e9c6dcd
--- /dev/null
+++ b/app/assets/javascripts/group-order-form.js
@@ -0,0 +1,232 @@
+class GroupOrderForm {
+ constructor(form$, config) {
+ this.form$ = form$;
+ this.articleRows$ = this.form$.find('tr.order-article');
+ this.totalPrice$ = this.form$.find('#total_price');
+ this.newBalance$ = this.form$.find('#new_balance');
+ this.totalBalance$ = this.form$.find('#total_balance');
+ this.submitButton$ = this.form$.find('#submit_button');
+ this.units = config.units;
+ this.toleranceIsCostly = config.toleranceIsCostly;
+ this.groupBalance = config.groupBalance;
+ this.minimumBalance = config.minimumBalance;
+
+ // TODO: Actually use this!:
+ this.stockit = config.stockit;
+
+ this.initializeIncreaseDecreaseButtons();
+ }
+
+ initializeIncreaseDecreaseButtons() {
+ this.articleRows$.each((_, element) => this.initializeOrderArticleRow($(element)));
+ }
+
+ initializeOrderArticleRow(row$) {
+ const quantity$ = row$.find('.goa-quantity');
+ const tolerance$ = row$.find('.goa-tolerance');
+ // eslint-disable-next-line no-undef
+ const quantityAndTolerance$ = mergeJQueryObjects([quantity$, tolerance$]);
+ // eslint-disable-next-line no-undef
+ quantityAndTolerance$.each((_, element) => $(element).unitConversionField({
+ units: this.units,
+ popoverTemplate$: $('#unit_conversion_popover_content_template'),
+ }));
+ row$.find('.btn-ordering').mousedown((e) => e.preventDefault());
+ row$.find('.btn-ordering.decrease').click((event) => this.increaseOrDecrease($(event.target).parents('.btn-group').find('input.numeric'), false));
+ row$.find('.btn-ordering.increase').click((event) => this.increaseOrDecrease($(event.target).parents('.btn-group').find('input.numeric'), true));
+
+ quantityAndTolerance$.change(() => {
+ this.updateMissingUnits(row$, quantity$);
+ this.updateBalance();
+ });
+ quantityAndTolerance$.keyup(() => quantity$.trigger('change'));
+ }
+
+ updateBalance() {
+ const total = this.articleRows$
+ .toArray()
+ .reduce((acc, row) =>
+ Big(acc).add(parseFloat($(row).find('*[id^="price_"][id$="_display"]').data('price'))).toNumber(),
+ 0
+ );
+
+ this.totalPrice$.text(I18n.l('currency', total));
+ const balance = Big(this.groupBalance).sub(total).toNumber();
+ this.newBalance$.text(I18n.l('currency', balance));
+
+ // TODO: Figure out why this hidden field is required (Should be
+ // calculated in the controller IMO!):
+ this.totalBalance$.val(I18n.l('currency', balance));
+
+ // determine bgcolor and submit button state according to balance
+ var bgcolor = '';
+ if (balance < this.minimumBalance) {
+ bgcolor = '#FF0000';
+ this.submitButton$.attr('disabled', 'disabled')
+ } else {
+ this.submitButton$.removeAttr('disabled')
+ }
+
+ // update bgcolor
+ this.articleRows$.find('*[id^="td_price_"]').css('background-color', bgcolor);
+ }
+
+ increaseOrDecrease(field$, increase) {
+ let step = parseFloat(field$.attr('step'));
+ if (isNaN(step)) {
+ step = 1;
+ }
+ if (!increase) {
+ step *= -1;
+ }
+ let value = parseFloat(field$.val());
+
+ if (isNaN(value)) {
+ value = 0;
+ }
+
+ value = round(value + step);
+ let remainder = round(value % step);
+ if (remainder !== 0) {
+ if (!increase) {
+ remainder *= -1;
+ }
+ value += remainder - step;
+ }
+ const min = field$.attr('min');
+ if (min !== undefined) {
+ value = Math.max(parseFloat(min), value);
+ }
+
+ const max = field$.attr('max');
+ if (max !== undefined) {
+ value = Math.min(parseFloat(max), value);
+ }
+
+ field$.val(value);
+ field$.trigger('change');
+ }
+
+ updateMissingUnits(row$) {
+ const used$ = row$.find('.quantity .used');
+ const unused$ = row$.find('.quantity .unused');
+
+ const quantity$ = row$.find('.goa-quantity');
+ const tolerance$ = row$.find('.goa-tolerance');
+ const usedTolerance$ = row$.find('.tolerance .used');
+ const unusedTolerance$ = row$.find('.tolerance .unused');
+ const totalPacks$ = row$.find('.article-info *[id^="units_"]');
+ const totalQuantity$ = row$.find('.article-info *[id^="q_total_"]');
+ const totalTolerance$ = row$.find('.article-info *[id^="t_total_"]');
+ const totalPrice$ = row$.find('*[id^="price_"][id$="_display"]');
+
+ const missing$ = row$.find('.missing-units');
+
+ let quantity = parseFloat(quantity$.val().trim().replace(',', '.'));
+ if (isNaN(quantity)) {
+ quantity = 0;
+ }
+ const granularity = parseFloat(quantity$.attr('step'));
+ let tolerance = tolerance$.length === 1 ? parseFloat(tolerance$.val().trim().replace(',', '.')) : 0;
+ if (isNaN(tolerance)) {
+ tolerance = 0;
+ }
+ const supplierOrderUnit = quantity$.data('supplier-order-unit');
+ const converter = quantity$.unitConversionField('getConverter');
+ const packSizeDeterminedBySupplierOrderUnit = converter && !converter.isUnitSiConversible(supplierOrderUnit);
+
+ const packSize = packSizeDeterminedBySupplierOrderUnit ? parseFloat(quantity$.data('ratio-group-order-unit-supplier-unit')) : 0.001;
+ const othersQuantity = parseFloat(quantity$.data('others-quantity'));
+ const othersTolerance = parseFloat(quantity$.data('others-tolerance'));
+ const usedQuantity = parseFloat(quantity$.data('used-quantity'));
+ const minimumOrderQuantity = parseFloat(quantity$.data('minimum-order-quantity'));
+ const price = parseFloat(quantity$.data('price'));
+
+ const totalQuantity = Big(quantity).add(othersQuantity).toNumber();
+ const totalTolerance = Big(tolerance).add(othersTolerance).toNumber();
+
+ const totalPacks = this.calculatePacks(packSize, totalQuantity, totalTolerance, minimumOrderQuantity)
+
+ const totalPrice = Big(price).mul(Big(quantity).add(this.toleranceIsCostly ? tolerance : 0)).toNumber();
+
+ // update used/unused quantity
+ const available = Math.max(0, Big(totalPacks).mul(packSize).sub(othersQuantity).toNumber());
+ let used = Math.min(available, quantity);
+ // ensure that at least the amount of items this group has already been allocated is used
+ if (quantity >= usedQuantity && used < usedQuantity) {
+ used = usedQuantity;
+ }
+
+ const unused = Big(quantity).sub(used).toNumber();
+
+ const availableForTolerance = quantity < minimumOrderQuantity ? Big(minimumOrderQuantity).sub(quantity).toNumber() : Math.max(0, Big(available).sub(used).sub(othersTolerance).toNumber());
+ const usedTolerance = Math.min(availableForTolerance, tolerance);
+ const unusedTolerance = Big(tolerance).sub(usedTolerance).toNumber();
+
+ const missing = this.calcMissingItems(packSize, totalQuantity, totalTolerance, minimumOrderQuantity);
+
+ used$.text(round(used));
+ unused$.text(round(unused));
+
+ usedTolerance$.text(round(usedTolerance));
+ unusedTolerance$.text(round(unusedTolerance));
+
+ totalPacks$.text(round(totalPacks));
+
+ totalPacks$.css('color', this.packCompletedFromTolerance(packSize, totalQuantity, totalTolerance) ? 'grey' : 'auto');
+
+ totalQuantity$.text(round(totalQuantity));
+ totalTolerance$.text(round(totalTolerance));
+ totalPrice$.text(I18n.l('currency', round(totalPrice)));
+ totalPrice$.data('price', round(totalPrice));
+
+ missing$.text(round(missing));
+ if (tolerance$.length === 1) {
+ this.setRowStyle(row$, missing, granularity, quantity);
+ }
+ }
+
+ setRowStyle(row$, missing, granularity, quantity) {
+ row$.removeClass('missing-many missing-few missing-none');
+ if (missing === 0) {
+ if (quantity !== 0) {
+ row$.addClass('missing-none');
+ }
+ } else {
+ row$.addClass(missing <= granularity ? 'missing-few' : 'missing-many');
+ }
+ }
+
+ calculatePacks(packSize, quantity, tolerance, minimumOrderQuantity) {
+ if (Big(quantity).add(tolerance).toNumber() < minimumOrderQuantity) {
+ return 0;
+ }
+
+ const used = Big(quantity).div(packSize).round(0, Big.roundDown).toNumber();
+ const remainder = Big(quantity).mod(packSize).toNumber();
+ return Big(used).add((remainder > 0) && (Big(remainder).add(tolerance).toNumber() >= packSize) ? 1 : 0).toNumber();
+ }
+
+ calcMissingItems(packSize, quantity, tolerance, minimumOrderQuantity) {
+ if (quantity !== 0 && Big(quantity).add(tolerance).toNumber() < minimumOrderQuantity) {
+ return Big(minimumOrderQuantity).sub(quantity).sub(tolerance).toNumber();
+ }
+
+ if (isNaN(quantity)) {
+ return quantity;
+ }
+
+ if (isNaN(packSize)) {
+ return packSize;
+ }
+
+ var remainder = Big(quantity).mod(packSize).toNumber();
+ return remainder > 0 && Big(remainder).add(tolerance).toNumber() < packSize ? Big(packSize).sub(remainder).sub(tolerance).toNumber() : 0
+ }
+
+ packCompletedFromTolerance(packSize, quantity, tolerance) {
+ var remainder = Big(quantity).mod(packSize).toNumber();
+ return (remainder > 0 && (Big(remainder).add(tolerance).toNumber() >= packSize));
+ }
+}
+
diff --git a/app/assets/javascripts/migrate-units-form.js b/app/assets/javascripts/migrate-units-form.js
new file mode 100644
index 00000000..87d06da6
--- /dev/null
+++ b/app/assets/javascripts/migrate-units-form.js
@@ -0,0 +1,142 @@
+class MigrateUnitsForm {
+ constructor(articleUnitRatioTemplate$, table$, units) {
+ this.articleUnitRatioTemplate$ = articleUnitRatioTemplate$;
+ this.table$ = table$;
+ this.units = units;
+
+ this.initializeUnitSelects();
+ this.initializeArticlesList();
+ }
+
+ loadAvailableUnits() {
+ this.availableUnits = Object.entries(this.units)
+ .filter(([, unit]) => unit.visible)
+ .map(([code, unit]) => ({ key: code, label: unit.name, baseUnit: unit.baseUnit, symbol: unit.symbol, aliases: unit.aliases ? unit.aliases : [] }));
+ }
+
+ initializeUnitSelects() {
+ this.loadAvailableUnits();
+ this.table$.find('select[name^="samples["]').select2();
+ const form = this;
+ this.table$.find('select[name^="samples["][name$="[supplier_order_unit]"]').each(function() {
+ form.updateUnitsInSelect(form.availableUnits, $(this));
+ form.updateFirstUnitRatioSelect($(this).parents('tr'));
+ form.updateGroupOrderUnitSelect($(this).parents('tr'));
+ form.updateFirstUnitRatioQuantity($(this).parents('tr'));
+ });
+ this.table$.find('select[name^="samples["][name$="[supplier_order_unit]"]').change(function() {
+ form.updateFirstUnitRatioSelect($(this).parents('tr'));
+ form.updateGroupOrderUnitSelect($(this).parents('tr'));
+ form.updateFirstUnitRatioQuantity($(this).parents('tr'));
+ });
+ this.table$.find('select[name^="samples["][name$="[first_ratio_unit]"]').change(function() {
+ form.updateGroupOrderUnitSelect($(this).parents('tr'));
+ form.updateFirstUnitRatioQuantity($(this).parents('tr'));
+ });
+ }
+
+ updateFirstUnitRatioQuantity(row$) {
+ const firstRatioSelect$ = row$.find('select[name$="[first_ratio_unit]"]');
+ const firstRatioQuantity$ = row$.find('input[name$="[first_ratio_quantity]"]');
+ if (firstRatioSelect$.val() === '') {
+ firstRatioQuantity$.val('');
+ firstRatioQuantity$.attr('disabled', 'disabled');
+ } else {
+ firstRatioQuantity$.removeAttr('disabled');
+ }
+ }
+
+ updateGroupOrderUnitSelect(row$) {
+ const supplierUnitSelect$ = row$.find('select[name$="[supplier_order_unit]"]');
+ const firstRatioSelect$ = row$.find('select[name$="[first_ratio_unit]"]');
+ const groupOrderUnit$ = row$.find('select[name$="[group_order_unit]"]');
+
+ const unitsSelectedAbove = [];
+ const chosenSupplierUnitOption$ = supplierUnitSelect$.find(`option[value="${supplierUnitSelect$.val()}"]`);
+ unitsSelectedAbove.push({ key: chosenSupplierUnitOption$.val(), label: chosenSupplierUnitOption$.text() });
+
+ if (firstRatioSelect$.val() != '') {
+ const chosenFirstRatioOption$ = firstRatioSelect$.find(`option[value="${firstRatioSelect$.val()}"]`);
+ unitsSelectedAbove.push({ key: chosenFirstRatioOption$.val(), label: chosenFirstRatioOption$.text() });
+ }
+
+ const availableUnits = [];
+ for (const unitSelectedAbove of unitsSelectedAbove) {
+ availableUnits.push(unitSelectedAbove, ...this.availableUnits.filter(availableUnit => {
+ if (availableUnit.key === unitSelectedAbove.key) {
+ return false;
+ }
+
+ const otherUnit = this.availableUnits.find(unit => unit.key === unitSelectedAbove.key);
+ return otherUnit !== undefined && otherUnit.baseUnit !== null && availableUnit.baseUnit === otherUnit.baseUnit;
+ }));
+ }
+
+ this.updateUnitsInSelect(availableUnits, groupOrderUnit$);
+ }
+
+ updateFirstUnitRatioSelect(row$) {
+ const supplierUnitSelect$ = row$.find('select[name$="[supplier_order_unit]"]');
+ const firstRatioSelect$ = row$.find('select[name$="[first_ratio_unit]"]');
+ const isUnitOrBaseUnitSelected = (unit, select$) => {
+ const code = select$.val();
+ const selectedUnit = this.units[code];
+ return unit.key !== code && (!unit.baseUnit || !selectedUnit || !selectedUnit.baseUnit || unit.baseUnit !== selectedUnit.baseUnit);
+ };
+
+ const remainingAvailableUnits = this.availableUnits.filter(unit => isUnitOrBaseUnitSelected(unit, supplierUnitSelect$));
+ this.updateUnitsInSelect(remainingAvailableUnits, firstRatioSelect$, true);
+ }
+
+ initializeArticlesList() {
+ this.table$.find('.articles-list .expander').click(function () {
+ const currentList$ = $(this).parents('.articles-list');
+ currentList$.find('.expander').addClass('d-none');
+ currentList$.find('.collapser').removeClass('d-none');
+ currentList$.find('.list').removeClass('d-none');
+ });
+
+ this.table$.find('.articles-list .collapser').click(function () {
+ const currentList$ = $(this).parents('.articles-list');
+ currentList$.find('.expander').removeClass('d-none');
+ currentList$.find('.collapser').addClass('d-none');
+ currentList$.find('.list').addClass('d-none');
+ });
+ }
+
+ updateUnitsInSelect(units, unitSelect$, includeBlank) {
+ const valueBeforeUpdate = unitSelect$.val();
+
+ unitSelect$.empty();
+ if (includeBlank) {
+ unitSelect$.append($(``));
+ }
+ for (const unit of units) {
+ unitSelect$.append($(``));
+ }
+
+ const initialValue = unitSelect$.attr('data-initial-value');
+ if (initialValue) {
+ unitSelect$.val(initialValue);
+ unitSelect$.removeAttr('data-initial-value');
+ } else {
+ if (unitSelect$.find(`option[value="${valueBeforeUpdate}"]`).length > 0) {
+ unitSelect$.val(valueBeforeUpdate);
+ } else {
+ unitSelect$.val(unitSelect$.find('option:first').val());
+ }
+ }
+
+ unitSelect$.trigger('change');
+
+ unitSelect$.parents('.control-group').find('.immutable_unit_label').remove();
+ if (units.length === 1) {
+ unitSelect$.parents('.controls').hide();
+ unitSelect$.parents('.control-group').append($(`${units[0].label}
`))
+ } else {
+ unitSelect$.parents('.controls').show();
+ }
+ }
+
+
+}
diff --git a/app/assets/javascripts/receive-order-form.js b/app/assets/javascripts/receive-order-form.js
new file mode 100644
index 00000000..42b43ef0
--- /dev/null
+++ b/app/assets/javascripts/receive-order-form.js
@@ -0,0 +1,153 @@
+(function ( $ ) {
+ class ReceiveOrderForm {
+ constructor(receiveForm$, packageHelperIcon, newOrderArticlePath) {
+ this.receiveForm$ = receiveForm$;
+ this.packageHelperIcon = packageHelperIcon;
+ this.newOrderArticlePath = newOrderArticlePath;
+ $(document).on('change keyup', 'input[data-units-expected]', (e) => {
+ this.updateDelta(e.target);
+ });
+
+ $(document).on('touchclick', '#order_articles .unlocker', (e) => this.unlockReceiveInputField($(e.target)));
+
+ $(document).on('click', '#set_all_to_zero', () => {
+ $('tbody input').each((_, input) => {
+ $(input).val(0);
+ this.updateDelta(input);
+ });
+ });
+
+ $('input[data-units-expected]').each((_, field) => {
+ this.convertToBillingUnit($(field));
+ this.updateDelta(field);
+ });
+
+ this.receiveForm$.submit(() => {
+ $('input[data-units-expected]').each((_, field) => {
+ this.convertFromBillingUnit($(field));
+ });
+ });
+
+ this.initAddArticle('#add_article');
+ }
+
+ convertFieldUnit(field$, fromUnit, toUnit) {
+ const units = parseFloat(field$.val().replace(',', '.'));
+ if (isNaN(units)) {
+ return;
+ }
+
+ const converter = field$.unitConversionField('getConverter');
+ if (converter === undefined) {
+ return units;
+ }
+ return converter.getUnitRatio(units, fromUnit, toUnit);
+ }
+
+ convertToBillingUnit(field$) {
+ const val = this.convertFieldUnit(field$, field$.data('supplier-order-unit'), field$.data('billing-unit'));
+ field$.val(val === undefined ? '' : round(val));
+ }
+
+ convertFromBillingUnit(field$) {
+ const convertedValue = this.convertFieldUnit(field$, field$.data('billing-unit'), field$.data('supplier-order-unit'));
+ const hiddenReceivedField$ = $(``);
+ this.receiveForm$.append(hiddenReceivedField$);
+ }
+
+ updateDelta(input) {
+ const units = $(input).val().replace(',', '.');
+ const expected = $(input).data('units-expected');
+ const delta = round(units-expected);
+ let html;
+
+ if (units.replace(/\s/g,"")=="") {
+ // no value
+ html = '';
+ } else if (isNaN(units)) {
+ html = '';
+ } else if (delta == 0) {
+ // equal value
+ html = '';
+ } else {
+ if (delta < 0) {
+ html = '- '+(-delta)+'';
+ } else /*if (units> expected)*/ {
+ html = '+ '+(delta)+'';
+ }
+ // show package icon only if the receive field has one
+ if ($(input).hasClass('package')) {
+ html += this.packageHelperIcon;
+ }
+ }
+
+ $(input).closest('tr').find('.units_delta').html(html);
+
+ // un-dim row when received is nonzero
+ $(input).closest('tr').toggleClass('unavailable', expected == 0 && html=='');
+ }
+
+
+ replace(id, newRow$) {
+ const oldRow$ = this.receiveForm$.find(`#order_article_${id}`);
+ const oldField$ = oldRow$.find('input.units_received')
+ const currentValue = oldField$.val();
+ oldRow$.replaceWith(newRow$);
+ const newField$ = newRow$.find('input.units_received');
+ newField$.val(currentValue);
+ newField$.unitConversionField(oldField$.unitConversionField('getConversionField'));
+ this.updateDelta(newField$);
+ }
+
+
+ initAddArticle(sel) {
+ $(sel).removeAttr('disabled').select2({
+ placeholder: I18n.t('orders.receive.add_article'),
+ formatNoMatches: () => I18n.t('no_articles_available')
+ // TODO implement adding a new article, like in deliveries
+ }).on('change', (e) => {
+ var $input = $(e.target);
+ var selectedArticleId = $input.val();
+ if(!selectedArticleId) {
+ return false;
+ }
+
+ $.ajax({
+ url: this.newOrderArticlePath,
+ type: 'post',
+ data: JSON.stringify({order_article: {article_version: {article_id: selectedArticleId}}}),
+ contentType: 'application/json; charset=UTF-8'
+ });
+
+ $input.val('').trigger('change');
+ });
+ $(sel).val('').trigger('change');
+ }
+
+ unlockReceiveInputField(unlockButton$) {
+ $('.units_received', unlockButton$.closest('tr')).prop('disabled', false).focus();
+ unlockButton$.closest('.input-prepend').prop('title', I18n.t('orders.edit_amount.field_unlocked_title'));
+ unlockButton$.replaceWith('');
+ }
+ }
+
+ const receiveOrderFormsMap = new Map();
+
+ $.fn.receiveOrderForm = function (options, actionOptions) {
+ switch(options) {
+ case 'replace': {
+ const receiveOrderForm = receiveOrderFormsMap.get($(this)[0]);
+ receiveOrderForm.replace(actionOptions.id, actionOptions.newEntry);
+ break;
+ }
+ default: {
+ const receiveOrderForm = new ReceiveOrderForm($(this), options.packageHelperIcon, options.newOrderArticlePath);
+ receiveOrderFormsMap.set($(this)[0], receiveOrderForm);
+ break;
+ }
+ }
+
+ return this;
+ };
+
+}( jQuery ));
diff --git a/app/assets/javascripts/unit-conversion-field.js b/app/assets/javascripts/unit-conversion-field.js
new file mode 100644
index 00000000..7efde41d
--- /dev/null
+++ b/app/assets/javascripts/unit-conversion-field.js
@@ -0,0 +1,251 @@
+
+
+
+(function ( $ ) {
+
+ class UnitConversionField {
+ constructor(field$, units, popoverTemplate$, useTargetUnitForStep = true, ratios = undefined, supplierOrderUnit = undefined, defaultUnit = undefined, customUnit = undefined) {
+ this.field$ = field$;
+ this.popoverTemplate$ = popoverTemplate$;
+ this.popoverTemplate = this.popoverTemplate$[0].content.querySelector('.popover_contents');
+ this.units = units;
+ this.useTargetUnitForStep = useTargetUnitForStep;
+ this.ratios = ratios;
+ this.supplierOrderUnit = supplierOrderUnit;
+ this.defaultUnit = defaultUnit;
+ this.customUnit = customUnit;
+
+ this.loadArticleUnitRatios();
+ this.unitSelectOptions = this.getUnitSelectOptions();
+
+ this.converter = new UnitsConverter(this.units, this.ratios, this.supplierOrderUnit);
+
+ // if there's less then two options, don't even bother showing the popover:
+ if (this.unitSelectOptions.length < 2) {
+ return;
+ }
+
+ this.initializeFocusListener();
+ }
+
+ loadArticleUnitRatios() {
+ if (!this.ratios) {
+ this.ratios = [];
+ for (let i = 0; this.field$.data(`ratio-quantity-${i}`) !== undefined; i++) {
+ this.ratios.push({
+ quantity: parseFloat(this.field$.data(`ratio-quantity-${i}`)),
+ unit: this.field$.data(`ratio-unit-${i}`),
+ });
+ }
+ }
+
+ if (this.supplierOrderUnit === undefined) {
+ this.supplierOrderUnit = this.field$.data('supplier-order-unit');
+ }
+ if (this.customUnit === undefined) {
+ this.customUnit = this.field$.data('custom-unit');
+ }
+ if (this.defaultUnit === undefined) {
+ this.defaultUnit = this.field$.data('default-unit');
+ }
+ }
+
+ initializeFocusListener() {
+ this.field$.popover({title: this.popoverTemplate.dataset.title, placement: 'bottom', trigger: 'manual'});
+
+ this.field$.on('focus.unit-conversion-field',() => this.openPopover());
+
+ this.field$.on('shown.bs.popover', () => this.initializeConversionPopover(this.field$.next('.popover')));
+ }
+
+ openPopover() {
+ $(document).on('mousedown.unit-conversion-field', (e) => {
+ if ($(e.target).parents('.popover').length !== 0 || e.target === this.field$[0]) {
+ return;
+ }
+
+ this.closePopover();
+ });
+ this.field$.popover('show');
+ }
+
+ closePopover() {
+ $(document).off('mousedown.unit-conversion-field');
+ this.field$.off('change.unit-conversion-field');
+ this.field$.popover('hide');
+ }
+
+ initializeConversionPopover(popover$) {
+ let showAgainHack = false;
+ if (!popover$.hasClass('wide')) {
+ popover$.addClass('wide');
+ showAgainHack = true;
+ }
+
+ const popoverContent$ = popover$.find('.popover-content');
+ popoverContent$.empty();
+
+ const contents$ = $(document.importNode(this.popoverTemplate, true));
+ popoverContent$.append(contents$);
+
+ if (showAgainHack) {
+ this.openPopover();
+ return;
+ }
+
+ this.quantityInput$ = contents$.find('input.quantity');
+ this.quantityInput$.val(String(this.field$.val()).replace(',', '.'));
+ this.applyButton$ = contents$.find('input.apply');
+ this.conversionResult$ = contents$.find('.conversion-result');
+ this.unitSelect$ = contents$.find('select.unit');
+ this.unitSelect$.append(this.unitSelectOptions.map(option => $(``)))
+ let initialUnitSelectValue = this.defaultUnit;
+ if (initialUnitSelectValue === undefined) {
+ initialUnitSelectValue = this.unitSelectOptions[0].value;
+ }
+ this.unitSelect$.val(initialUnitSelectValue);
+
+ this.previousUnitSelectValue = convertEmptyStringToUndefined(this.unitSelect$.val());
+
+ this.unitSelect$.change(() => this.onUnitSelectChanged());
+
+ // eslint-disable-next-line no-undef
+ mergeJQueryObjects([this.quantityInput$, this.unitSelect$]).change(() => this.prepareConversion())
+ this.quantityInput$.keyup(() => this.quantityInput$.trigger('change'));
+
+ this.field$.on('change.unit-conversion-field', () => {
+ this.quantityInput$.val(this.field$.val());
+ this.quantityInput$.trigger('change')
+ this.unitSelect$.val(initialUnitSelectValue);
+ });
+
+ contents$.find('input.cancel').click(() => this.closePopover());
+ this.applyButton$.click(() => {
+ this.applyConversion();
+ this.closePopover();
+ });
+
+ this.onUnitSelectChanged();
+ this.prepareConversion();
+ }
+
+ getUnitSelectOptions() {
+ const options = [];
+ const unit = this.units[this.supplierOrderUnit];
+ options.push({
+ value: this.supplierOrderUnit,
+ label: unit === undefined ? this.customUnit : unit.name
+ });
+ if (unit !== undefined) {
+ options.push(...this.getRelatedUnits(this.supplierOrderUnit));
+ }
+
+
+ for (const ratio of this.ratios) {
+ options.push({
+ value: ratio.unit,
+ label: this.units[ratio.unit].name
+ });
+
+ options.push(...this.getRelatedUnits(ratio.unit));
+ }
+
+ return options;
+ }
+
+ prepareConversion() {
+ const unit = this.defaultUnit === undefined ? this.supplierOrderUnit : this.defaultUnit;
+ const unitLabel = this.unitSelectOptions.find(option => option.value === unit).label;
+ this.conversionResult$.text('= ' + this.getConversionResult() + ' x ' + unitLabel);
+ this.conversionResult$.parent().find('.numeric-step-error').remove();
+ if (this.quantityInput$.is(':invalid')) {
+ this.applyButton$.attr('disabled', 'disabled');
+ const errorSpan$ = $(`${I18n.t('errors.step_error', {min: 0, granularity: this.quantityInput$.attr('step')})}
`);
+ errorSpan$.show();
+ this.conversionResult$.after(errorSpan$);
+ } else {
+ this.applyButton$.removeAttr('disabled');
+ }
+ }
+
+ applyConversion() {
+ this.field$
+ .val(this.getConversionResult())
+ .trigger('change');
+ }
+
+ getQuantityInputValue() {
+ const val = parseFloat(this.quantityInput$.val().trim().replace(',', '.'));
+ if (isNaN(val)) {
+ return 0;
+ }
+
+ return val;
+ }
+
+ getTargetUnit() {
+ return this.defaultUnit === undefined ? this.supplierOrderUnit : this.defaultUnit;
+ }
+
+ getConversionResult() {
+ const result = this.converter.getUnitRatio(this.getQuantityInputValue(), convertEmptyStringToUndefined(this.unitSelect$.val()), this.getTargetUnit());
+ return Big(result).round(4).toNumber();
+ }
+
+ onUnitSelectChanged() {
+ const newValue = this.converter.getUnitRatio(this.getQuantityInputValue(), this.previousUnitSelectValue, convertEmptyStringToUndefined(this.unitSelect$.val()));
+ this.quantityInput$.val(newValue);
+
+ const selectedUnit = convertEmptyStringToUndefined(this.unitSelect$.val());
+ this.previousUnitSelectValue = selectedUnit;
+
+ const step = this.useTargetUnitForStep ? this.converter.getUnitRatio(this.field$.attr('step'), this.getTargetUnit(), selectedUnit) : 0.001;
+ this.quantityInput$.attr('step', step);
+ }
+
+ getRelatedUnits(unitId) {
+ return Object.entries(this.units)
+ .filter(([id, unit]) => unit.visible && unit.baseUnit !== null && unit.baseUnit === this.units[unitId].baseUnit && id !== unitId)
+ .map(([id, unit]) => ({
+ value: id,
+ label: unit.name
+ }));
+ }
+ }
+
+ function convertEmptyStringToUndefined(str) {
+ if (str === '') {
+ return undefined;
+ }
+
+ return str;
+ }
+
+ const convertersMap = new Map();
+
+ $.fn.unitConversionField = function (optionsOrAction) {
+ const conversionField = convertersMap.get($(this)[0]);
+ switch(optionsOrAction) {
+ case 'getConversionField':
+ return conversionField;
+ case 'getConverter':
+ return conversionField === undefined ? undefined : conversionField.converter;
+ case 'destroy':
+ if (conversionField === undefined || conversionField.field$ === undefined) {
+ break;
+ }
+ conversionField.field$.off('focus.unit-conversion-field');
+ convertersMap.delete($(this)[0]);
+ break;
+ default: {
+ const converter = new UnitConversionField($(this), optionsOrAction.units, optionsOrAction.popoverTemplate$, optionsOrAction.useTargetUnitForStep, optionsOrAction.ratios, optionsOrAction.supplierOrderUnit, optionsOrAction.defaultUnit, optionsOrAction.customUnit);
+ convertersMap.set($(this)[0], converter);
+ break;
+ }
+ }
+
+ return this;
+ };
+
+
+}( jQuery ));
diff --git a/app/assets/javascripts/units-converter.js b/app/assets/javascripts/units-converter.js
new file mode 100644
index 00000000..40c65d62
--- /dev/null
+++ b/app/assets/javascripts/units-converter.js
@@ -0,0 +1,40 @@
+class UnitsConverter {
+ constructor(units, ratios, supplierOrderUnit) {
+ this.units = units;
+ this.ratios = ratios;
+ this.supplierOrderUnit = supplierOrderUnit;
+ }
+
+ getUnitQuantity(unitId) {
+ if (unitId === this.supplierOrderUnit) {
+ return 1;
+ }
+
+ const ratio = this.ratios.find(ratio => ratio.unit === unitId);
+ if (ratio !== undefined) {
+ return ratio.quantity;
+ }
+
+ const unit = this.units[unitId];
+ const relatedRatio = this.ratios.find(ratio => this.units[ratio.unit].baseUnit === unit.baseUnit);
+ if (relatedRatio !== undefined) {
+ const relatedUnit = this.units[relatedRatio.unit];
+ return Big(relatedRatio.quantity).div(unit.conversionFactor).mul(relatedUnit.conversionFactor).toNumber();
+ }
+
+ const supplierOrderUnitConversionFactor = this.units[this.supplierOrderUnit].conversionFactor;
+ return Big(supplierOrderUnitConversionFactor).div(unit.conversionFactor).toNumber();
+ }
+
+ getUnitRatio(quantity, inputUnit, outputUnit) {
+ return Big(quantity).div(this.getUnitQuantity(inputUnit)).mul(this.getUnitQuantity(outputUnit)).toNumber();
+ }
+
+ isUnitSiConversible(unitId) {
+ const unit = this.units[unitId];
+ if (unit === undefined) {
+ return false;
+ }
+ return !!unit.conversionFactor;
+ }
+}
diff --git a/app/assets/stylesheets/article.less b/app/assets/stylesheets/article.less
new file mode 100644
index 00000000..a71d7e2f
--- /dev/null
+++ b/app/assets/stylesheets/article.less
@@ -0,0 +1,38 @@
+.extra-unit-fields {
+ display: none;
+ position: absolute;
+ background: #fff;
+ border: 1px solid #888;
+ border-radius: 3px;
+ padding: 10px;
+ z-index: 999;
+ width:650px;
+ box-shadow: 10px 10px 22px 0px rgba(0,0,0,0.25);
+
+
+ &.show {
+ display: block;
+ }
+}
+
+.extra-unit-fields-btn {
+ color: #000;
+}
+
+.toggle-extra-units.default-values {
+ color: #333;
+ opacity: 0.65;
+}
+
+.toggle-extra-units.show {
+ color: #333;
+ opacity: 1;
+}
+
+.sync-table .control-group {
+ margin-bottom: 0;
+}
+
+.sync-table .extra-unit-fields .control-group {
+ margin-bottom: 20px;
+}
diff --git a/app/assets/stylesheets/bootstrap_and_overrides.css.less b/app/assets/stylesheets/bootstrap_and_overrides.css.less
index 971308c9..a46089bf 100644
--- a/app/assets/stylesheets/bootstrap_and_overrides.css.less
+++ b/app/assets/stylesheets/bootstrap_and_overrides.css.less
@@ -1,5 +1,7 @@
@import "twitter/bootstrap/bootstrap";
@import "twitter/bootstrap/responsive";
+@import "group_order";
+@import "article";
@import "delta_input";
body {
@@ -42,6 +44,10 @@ body {
@articleUnusedColor: red;
@articleUnavailColor: #999;
@articleUpdatedColor: #468847;
+@articleCustomUnitWarning: #ff8c00;
+
+// article units:
+@unitUntranslatedColor: #999;
// dim colors by this amount when the information is less important
@nonessentialDim: 35%;
@@ -279,6 +285,21 @@ tr.order-article:hover .article-info {
display: block;
}
+tr.order-article {
+ .group-order-unit {
+ white-space: nowrap;
+ }
+
+ .group-order-input {
+ text-align: center;
+
+ .goa-quantity {
+ width: 45px;
+ text-align: right;
+ }
+ }
+}
+
// ********* Articles
@@ -319,6 +340,12 @@ td.symbol, th.symbol {
.partused .symbol { color: tint(@articlePartusedColor, @nonessentialDim); }
.unavailable .symbol { color: @articleUnavailColor; }
+.article-form .icon-warning-sign {
+ margin-top: 8px;
+ margin-left: 10px;
+ color: @articleCustomUnitWarning;
+}
+
// hide symbols completely on small screens to save space
@media (max-width: 768px) {
.symbol {
@@ -328,6 +355,11 @@ td.symbol, th.symbol {
}
}
+// ********* Article units
+tr.untranslated {
+ color: @unitUntranslatedColor;
+}
+
// ********* Tweaks & fixes
@@ -448,6 +480,16 @@ i.package.icon-only {
margin: 0;
}
+// bootstrap 2 doesn't support differnt modal sizes -> let's provide our own modal-xl:
+.modal.modal-xl {
+ width: 80vw;
+ max-width: 650px;
+ position: absolute;
+ left: 0;
+ right: 0;
+ margin: 0 auto;
+}
+
// multiple-column layout in forms (landscape tablet and wider only)
@media (min-width: 768px) {
.form-horizontal .fold-line {
@@ -540,3 +582,90 @@ span.negative_amout {
.deleted_row {
text-decoration: line-through;
}
+
+.form-horizontal .no-indent-left .control-group {
+ margin-bottom: 0;
+ .controls {
+ margin-left: 0;
+ }
+}
+
+// bootstrap version 2 doesn't support some styles -> let's add makeshift ones manually for now:
+.d-flex {
+ display: flex;
+}
+
+.d-none {
+ display: none;
+}
+
+.align-items-center {
+ align-items: center;
+}
+
+.m-0 {
+ margin:0 !important;
+}
+
+.ml-1 {
+ margin-left: 3px !important;
+}
+
+.ml-2 {
+ margin-left: 6px !important;
+}
+
+td.ml-1 > *:first-child {
+ margin-left: 3px !important;
+}
+
+
+.mr-1 {
+ margin-right: 3px !important;
+}
+
+td.mr-1 > *:last-child {
+ margin-right: 3px;
+}
+
+.mt-1 {
+ margin-top: 3px !important;
+}
+
+.mb-1 {
+ margin-bottom: 3px !important;
+}
+
+.text-decoration-none:hover {
+ text-decoration: none;
+}
+
+// Wider popovers
+.popover.wide {
+ max-width: 40vw;
+}
+
+.gap-1 {
+ gap: .25rem;
+}
+
+*[role="button"] {
+ cursor: pointer;
+}
+
+.user-select-none {
+ user-select: none;
+}
+
+.numeric-step-error {
+ display: none;
+ color: #b94a48;
+}
+
+.btn-group.numeric-step > input:invalid {
+ border: 1px solid #b94a48;
+}
+
+.btn-group.numeric-step:has(> input:invalid) + .numeric-step-error {
+ display: block;
+}
diff --git a/app/assets/stylesheets/delta_input.less b/app/assets/stylesheets/delta_input.less
index c0afaf4c..24a38d66 100644
--- a/app/assets/stylesheets/delta_input.less
+++ b/app/assets/stylesheets/delta_input.less
@@ -6,13 +6,13 @@ form.delta-input, .delta-input form {
}
.delta-input {
-
- .btn {
+ .btn.modify {
padding: 4px 0;
width: 24px;
vertical-align: middle;
font-size: 10px;
}
+
input[data-delta] {
text-align: center;
}
diff --git a/app/assets/stylesheets/group_order.less b/app/assets/stylesheets/group_order.less
new file mode 100644
index 00000000..3e68a346
--- /dev/null
+++ b/app/assets/stylesheets/group_order.less
@@ -0,0 +1,16 @@
+.group-order-input {
+ .btn-group .btn-ordering {
+ margin-top: 0;
+ }
+
+ input.goa-quantity, input.goa-tolerance {
+ height: 14px;
+ border-radius: 0;
+ -moz-appearance: textfield;
+
+ &::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ }
+ }
+}
+
diff --git a/app/assets/stylesheets/main.sass b/app/assets/stylesheets/main.sass
index 6a92e70c..189d2d55 100644
--- a/app/assets/stylesheets/main.sass
+++ b/app/assets/stylesheets/main.sass
@@ -25,7 +25,7 @@ body
:color black
:border
:width 2px
- :style solid
+ :style solid
color: $main_red
a, a:visited
@@ -51,11 +51,11 @@ h1
h2
:font-size 1.4em
:margin-top .5em
-
+
h3
:font-size 1em
:margin-top 1.5em
-
+
input
:color #2e2e2e
@@ -121,14 +121,14 @@ option
:border-top 1px dotted #ED0606
:color #2e2e2e
-
+
// ******************************** - Logo - head
-#header
+#header
:margin 0
:padding 0
-#logo
+#logo
background: $main_red
:height 1.1em
:width 8em
@@ -150,45 +150,45 @@ option
:font-weight bold
:border-top
:width 2px
- :style dotted
+ :style dotted
color: $main_red
-
+
#logininfo
:position absolute
:top 3px
:right 10px
:font-size 1em
- ul
+ ul
:list-style none
- li
+ li
:margin 0 0 0 5px
:float left
- a
+ a
:color #737272
:font-weight bold
- a:hover
+ a:hover
color: $main_red
-
+
// ************************************* box structure
-#main
+#main
:background #FFF
:padding 0
:margin 0 15px 0 15px
// ************************************* infobar
-#infobar
- :width 10%
+#infobar
+ :width 10%
:min-width 5em
:float right
:padding 2.6em 1em
:margin 3em 0 0 0
:border-left 1px dotted #ED0606
- :font-size 1.2em
+ :font-size 1.2em
h3
:color #ED0606
- ul
+ ul
:list-style none
- li
+ li
:margin .3em 0 0 -3em
// ************************************ embedded menu
@@ -198,33 +198,33 @@ option
:padding 0 10px 0px 5px
:float left
- ul
+ ul
:list-style-type none
:margin 0 0 0.2em 0
:padding 0
- li
+ li
:border-bottom 1px solid #dedede
:color #666
:margin 0.8em 0 0 0
- :font-weight bold
- a:link, a:visited
+ :font-weight bold
+ a:link, a:visited
:display block
:padding 0.25em 1em
:text-decoration none
- :width 12em
- a:hover, a:focus
+ :width 12em
+ a:hover, a:focus
:background-color #e3e3e3
- ul
+ ul
:margin 0
:padding 0
- li
+ li
:border-top 1px solid #dedede
:border-bottom none
:margin 0
:font-weight normal
- a:link, a:visited
+ a:link, a:visited
:width 11.5em
:padding 0 1em 0.1em 1.5em
:font-weight normal
@@ -233,9 +233,9 @@ option
:position absolute
:top 100px
:right 1px
-
+
// ************************************** content
-#content
+#content
:padding .5em 0 2.5em 0
:margin 0
:background #FFF
@@ -245,28 +245,28 @@ option
/************************************ tables
-table
+table
:border-collapse collapse
// border2px solid #e3e3e3
:text-align left
:width 100%
:margin 0
-
- thead th, tbody td, th, td
- :padding 0.3em
- thead tr
+ thead th, tbody td, th, td
+ :padding 0.3em
+
+ thead tr
:background-color #efefef
th
:color black
tr.odd, tr.even
- :border-top 1px solid #DDDDDD
+ :border-top 1px solid #DDDDDD
tr.odd, tr.odd input
:background-color #F6F6F6
tr.even
:background-color #FBFBFB
tr.unavailable, tr.unavailable a
- :color grey
+ :color grey
tr.unavailable a:hover
:color #ED0606
tr.just_updated
@@ -279,14 +279,14 @@ table
color: green
tr.failed
color: red
-
+
table.list
//:border 2px solid #78b74e
tr
:border 1px solid #e3e3e3
tbody tr:hover
:background-color #EEEEDD
-
+
table tfoot tr
:background-color #fff
td
@@ -297,12 +297,12 @@ tr.edit_inline
td, span
:padding 0.5em 0.2em
-div.legend, div.legend table th
+div.legend, div.legend table th
:color grey
:font-size .8em
:background none
-form table
+form table
:border none
table.ordered_articles
@@ -316,7 +316,7 @@ table.ordered_articles
table
tfoot
:font-weight bold
-
+
td.currency, td.actions
:text-align right
:padding-right 0.5em
@@ -336,7 +336,7 @@ div.edit_form
:padding 0 0.8em 0.8em 0.8em
:margin 5px 0
:color black
-
+
#edit_article, #edit_box, #ajax_box
:position fixed
:top 5em
@@ -345,22 +345,22 @@ div.edit_form
:background #FBFBFB
:padding 3em
:padding-top 1em
- :border
+ :border
:width 3px
:style solid
color: $main_red
-
+
// ***************************************** other boxes */
// *********boxes in columns ****/
-div.box
+div.box
:border-left 2px solid #78b74e
:padding-left 5px
-div.single_column
+div.single_column
:width 100%
-div.left_column
+div.left_column
:width 40%
:float left
@@ -376,22 +376,22 @@ div.right_column
// *********** content of boxes ******/
-div.box_title
+div.box_title
:background #78b74e
:padding 5px 10px
-
- h2, h2 a
+
+ h2, h2 a
:color #FFF
:margin 0
h2
- :font-size 1.3em
+ :font-size 1.3em
-div.column_content
+div.column_content
background: $boxContent
:color black
:padding 10px
margin-bottom: 2em
- h2
+ h2
:color black
:font-size 1.3em
:margin 1em 0 0 0
@@ -407,16 +407,16 @@ div.column_content
// * article show
tr.current_price
:background #cdee9e
-
+
// **** maybe later for the very little spinner
li.check div.spinner
- :display block
- :height 5px
- :width 21px
+ :display block
+ :height 5px
+ :width 21px
:background-image image-url('dots-white.gif')
- :line-height 16px
- :float left
+ :line-height 16px
+ :float left
:margin-right 5px
:background-position center center
:background-repeat no-repeat
@@ -425,12 +425,12 @@ li.check div.spinner
// ************************************* the order page */
span.used, span.unused
:font-weight bold
-span.used
+span.used
:color #008000
span.unused
:color #ed0606
span.total
- :font-size 80%
+ :font-size 80%
table#order
:text-align center
input
@@ -513,7 +513,7 @@ tr.order-article:hover .article-info
.timestamp
:font-size 0.8em
:color grey
-
+
// *************** Clearing Order Page ..
#editOrderNav
a
@@ -526,7 +526,7 @@ tr.order-article:hover .article-info
:display inline
:list-style none
:margin-right 1em
-
+
// *************** Tasks ...
.accepted
color: green
@@ -534,7 +534,7 @@ tr.order-article:hover .article-info
.done, .done a, .done .accepted
color: grey
font-weight: normal
-
+
// ************** auto_complete
ul.autocomplete
.nick, .informal
@@ -544,7 +544,7 @@ ul.autocomplete
.informal
color: grey
margin-left: 1em
-
+
// ******* to navigate easy to the next element, e.g. next order
#element_navigation
position: relative
@@ -559,7 +559,7 @@ ul.autocomplete
background-color: #fff
text-align: center
margin: 0 10px 10px 0
-
+
// *** wiki
#wiki_content
border-style: none
diff --git a/app/controllers/admin/bank_accounts_controller.rb b/app/controllers/admin/bank_accounts_controller.rb
index a54ea798..ac6bb64f 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_attributes(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 @@ def new
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 @@ def create
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 @@ def list
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 @@ def update
# 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 @@ def get_tabs
# 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 76a93619..7d2ddff6 100644
--- a/app/controllers/admin/finances_controller.rb
+++ b/app/controllers/admin/finances_controller.rb
@@ -9,16 +9,16 @@ def index
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
end
diff --git a/app/controllers/admin/financial_transaction_classes_controller.rb b/app/controllers/admin/financial_transaction_classes_controller.rb
index 10359acd..fe46a422 100644
--- a/app/controllers/admin/financial_transaction_classes_controller.rb
+++ b/app/controllers/admin/financial_transaction_classes_controller.rb
@@ -6,25 +6,25 @@ def new
render layout: false
end
+ def edit
+ @financial_transaction_class = FinancialTransactionClass.find(params[:id])
+ render action: 'new', layout: false
+ end
+
def create
@financial_transaction_class = FinancialTransactionClass.new(params[:financial_transaction_class])
if @financial_transaction_class.save
- redirect_to update_transaction_types_admin_finances_url, status: 303
+ redirect_to update_transaction_types_admin_finances_url, status: :see_other
else
render action: 'new', layout: false
end
end
- def edit
- @financial_transaction_class = FinancialTransactionClass.find(params[:id])
- render action: 'new', layout: false
- end
-
def update
@financial_transaction_class = FinancialTransactionClass.find(params[:id])
if @financial_transaction_class.update_attributes(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 @@ def update
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 401f31e7..a3b7abeb 100644
--- a/app/controllers/admin/financial_transaction_types_controller.rb
+++ b/app/controllers/admin/financial_transaction_types_controller.rb
@@ -7,25 +7,25 @@ def new
render layout: false
end
+ def edit
+ @financial_transaction_type = FinancialTransactionType.find(params[:id])
+ render action: 'new', layout: false
+ end
+
def create
@financial_transaction_type = FinancialTransactionType.new(params[:financial_transaction_type])
if @financial_transaction_type.save
- redirect_to update_transaction_types_admin_finances_url, status: 303
+ redirect_to update_transaction_types_admin_finances_url, status: :see_other
else
render action: 'new', layout: false
end
end
- def edit
- @financial_transaction_type = FinancialTransactionType.find(params[:id])
- render action: 'new', layout: false
- end
-
def update
@financial_transaction_type = FinancialTransactionType.find(params[:id])
if @financial_transaction_type.update_attributes(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 @@ def update
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 279cccdf..4a91f44e 100644
--- a/app/controllers/admin/ordergroups_controller.rb
+++ b/app/controllers/admin/ordergroups_controller.rb
@@ -5,13 +5,12 @@ def index
@ordergroups = Ordergroup.undeleted.order('name ASC')
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 @@ def index
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/users_controller.rb b/app/controllers/admin/users_controller.rb
index 97ff4177..132051ee 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -5,12 +5,10 @@ def index
@users = params[:show_deleted] ? User.deleted : User.undeleted
@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.natural_order.page(params[:page]).per(@per_page)
end
@@ -18,17 +16,17 @@ def index
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 @@ def index
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/articles_controller.rb b/app/controllers/api/v1/articles_controller.rb
new file mode 100644
index 00000000..67850fee
--- /dev/null
+++ b/app/controllers/api/v1/articles_controller.rb
@@ -0,0 +1,58 @@
+class Api::V1::ArticlesController < Api::V1::BaseController
+ skip_before_action :authenticate
+
+ def index
+ supplier = Supplier.find_by_external_uuid(index_params.fetch(:shared_supplier_uuid))
+ raise ActionController::RoutingError, 'Not Found' if supplier.nil?
+
+ @articles = Article.with_latest_versions_and_categories.undeleted.where(supplier_id: supplier, type: nil)
+ if index_params.include?(:updated_after)
+ @articles = @articles.where('article_versions.updated_at > ?',
+ index_params[:updated_after].to_datetime)
+ end
+ if index_params.include?(:name)
+ @articles = @articles.where('article_versions.name LIKE ?',
+ "%#{index_params[:name]}%")
+ end
+ @articles = @articles.where(article_versions: { origin: index_params[:origin] }) if index_params.include?(:origin)
+ @articles = @articles.page(index_params[:page]).per(index_params.fetch(:per_page)) if index_params.include?(:page)
+
+ data = @articles.map do |article|
+ version_attributes = article.latest_article_version.attributes
+ version_attributes.delete_if { |key| key == 'id' || key.end_with?('_id') }
+
+ version_attributes['article_unit_ratios'] = article.latest_article_version.article_unit_ratios.map do |ratio|
+ ratio_attributes = ratio.attributes
+ ratio_attributes.delete_if { |key| key == 'id' || key.end_with?('_id') }
+ end
+
+ version_attributes
+ end
+
+ latest_update = Article.with_latest_versions_and_categories.undeleted.where(supplier_id: supplier,
+ type: nil).maximum('article_versions.updated_at')
+ latest_update = latest_update.utc unless latest_update.nil?
+
+ pagination = nil
+ if index_params.include?(:page)
+ current = @articles.current_page
+ total = @articles.total_pages
+ per_page = @articles.limit_value
+ pagination = {
+ current_page: current,
+ previous_page: (current > 1 ? (current - 1) : nil),
+ next_page: (current == total ? nil : (current + 1)),
+ per_page: per_page,
+ total_pages: total,
+ number: @articles.total_count
+ }
+ end
+ render json: { articles: data, pagination: pagination, latest_update: latest_update }
+ end
+
+ protected
+
+ def index_params
+ params.permit(:shared_supplier_uuid, :updated_after, :name, :origin, :page, :per_page)
+ 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 @@ def current_ordergroup
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 @@ def skip_session
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 @@ def doorkeeper_forbidden_render_options(error:)
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/order_articles_controller.rb b/app/controllers/api/v1/order_articles_controller.rb
index 6e7d220d..00c3de18 100644
--- a/app/controllers/api/v1/order_articles_controller.rb
+++ b/app/controllers/api/v1/order_articles_controller.rb
@@ -14,7 +14,7 @@ def show
private
def scope
- OrderArticle.includes(:article_price, article: :supplier)
+ OrderArticle.includes(:article_version, article: :supplier)
end
def search_scope
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 @@ def show
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 eeeb122c..1a1ec4d3 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 @@ def update
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_attributes! 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 @@ def financial_overview
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 @@ def self.current
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 @@ def find_supplier
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/article_units_controller.rb b/app/controllers/article_units_controller.rb
new file mode 100644
index 00000000..783ca883
--- /dev/null
+++ b/app/controllers/article_units_controller.rb
@@ -0,0 +1,63 @@
+class ArticleUnitsController < ApplicationController
+ before_action :authenticate_article_meta
+ before_action :load_available_units, only: %i[search create destroy]
+
+ def index; end
+
+ def search
+ @query = params[:q].blank? ? nil : params[:q].downcase
+
+ existing_article_units = ArticleUnit.all.to_a
+ @article_units = @available_units
+ .to_h do |key, value|
+ [key, value.merge({ code: key, record: existing_article_units.find do |existing_unit|
+ existing_unit.unit == key
+ end })]
+ end
+
+ unless @query.nil?
+ @article_units = @article_units.select do |_key, value|
+ (value[:name].downcase.include?(@query) || value[:symbol]&.downcase&.include?(@query)) &&
+ (params[:only_recommended] == '0' || !value[:untranslated])
+ end
+ end
+
+ @article_units = @article_units
+ .sort { |a, b| sort_by_unit_name(@query, a, b) }
+ .map { |_key, value| value }
+
+ @article_units = @article_units.take(100) unless @query.nil?
+ @article_units = @article_units.reject { |unit| unit[:record].nil? } if @query.nil?
+ end
+
+ def create
+ @article_unit = ArticleUnit.create(unit: params[:unit])
+ end
+
+ def destroy
+ @article_unit = ArticleUnit.find(params[:id])
+ @article_unit.destroy
+ end
+
+ private
+
+ def load_available_units
+ @available_units = ArticleUnitsLib.units
+ end
+
+ def article_unit_params
+ params.require(:article_unit).permit(:unit)
+ end
+
+ def sort_by_unit_name(query, a_unit, b_unit)
+ a_name = a_unit[1][:name].downcase
+ b_name = b_unit[1][:name].downcase
+
+ unless query.nil?
+ return -1 if a_name.starts_with?(query) && !b_name.starts_with?(query)
+ return 1 if !a_name.starts_with?(query) && b_name.starts_with?(query)
+ end
+
+ a_name <=> b_name
+ end
+end
diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb
index 5e8c402d..686750f4 100644
--- a/app/controllers/articles_controller.rb
+++ b/app/controllers/articles_controller.rb
@@ -1,73 +1,89 @@
class ArticlesController < ApplicationController
before_action :authenticate_article_meta, :find_supplier
+ before_action :load_article, only: %i[edit update]
+ before_action :load_article_units, only: %i[edit update new create sync update_synchronized]
+ before_action :load_article_categories, only: %i[edit_all migrate_units update_all]
+ before_action :new_empty_article_ratio,
+ only: %i[edit edit_all migrate_units update new create parse_upload sync update_synchronized]
+
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 'article_versions.name'
+ when 'unit' then 'article_versions.unit'
+ when 'article_category' then 'article_categories.name'
+ when 'note' then 'article_versions.note'
+ when 'availability' then 'article_versions.availability'
+ when 'name_reverse' then 'article_versions.name DESC'
+ when 'unit_reverse' then 'article_versions.unit DESC'
+ when 'article_category_reverse' then 'article_categories.name DESC'
+ when 'note_reverse' then 'article_versions.note DESC'
+ when 'availability_reverse' then 'article_versions.availability DESC'
end
- else
- sort = "article_categories.name, articles.name"
- end
+ else
+ 'article_categories.name, article_versions.name'
+ end
- @articles = Article.undeleted.where(supplier_id: @supplier, :type => nil).includes(:article_category).order(sort)
+ @articles = Article.with_latest_versions_and_categories.order(sort).undeleted.where(supplier_id: @supplier,
+ type: nil)
if request.format.csv?
send_data ArticlesCsv.new(@articles, encoding: 'utf-8').to_csv, filename: 'articles.csv', type: 'text/csv'
return
end
- @articles = @articles.where('articles.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
+ @articles = @articles.where('article_versions.name LIKE ?', "%#{params[:query]}%") unless params[:query].nil?
@articles = @articles.page(params[:page]).per(@per_page)
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
+ @article.latest_article_version = @article.article_versions.build
+ render layout: false
end
def copy
@article = @supplier.articles.find(params[:article_id]).dup
- render :layout => false
+ render layout: false
+ end
+
+ def edit
+ render action: 'new', layout: false
end
def create
- @article = Article.new(params[:article])
- if @article.valid? && @article.save
- render :layout => false
- else
- render :action => 'new', :layout => false
+ valid = false
+ Article.transaction do
+ @article = Article.create(supplier_id: @supplier.id)
+ @article.attributes = { latest_article_version_attributes: params[:article_version] }
+ raise ActiveRecord::Rollback unless @article.valid?
+
+ valid = @article.save
end
- end
- def edit
- @article = Article.find(params[:id])
- render :action => 'new', :layout => false
+ if valid
+ render layout: false
+ else
+ render action: 'new', layout: false
+ end
end
# Updates one Article and highlights the line if succeded
def update
- @article = Article.find(params[:id])
-
- if @article.update_attributes(params[:article])
- render :layout => false
- else
- render :action => 'new', :layout => false
+ Article.transaction do
+ if @article.update_attributes(latest_article_version_attributes: params[:article_version])
+ render layout: false
+ else
+ Rails.logger.info @article.errors.to_yaml.to_s
+ render action: 'new', layout: false
+ end
end
end
@@ -75,31 +91,158 @@ def update
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
def edit_all
@articles = @supplier.articles.undeleted
+
+ load_article_units
end
+ def prepare_units_migration; end
+
+ def migrate_units
+ build_article_migration_samples
+ end
+
+ def complete_units_migration
+ @invalid_articles = []
+ @samples = []
+
+ Article.transaction do
+ params[:samples].values.each do |sample|
+ next unless sample[:apply_migration] == '1'
+
+ original_unit = nil
+ articles = Article.with_latest_versions_and_categories
+ .includes(latest_article_version: [:article_unit_ratios])
+ .find(sample[:article_ids])
+ articles.each do |article|
+ latest_article_version = article.latest_article_version
+ original_unit = latest_article_version.unit
+ next if latest_article_version.article_unit_ratios.length > 1 ||
+ latest_article_version.billing_unit != latest_article_version.group_order_unit ||
+ latest_article_version.price_unit != latest_article_version.group_order_unit
+
+ article_version_params = sample.slice(:supplier_order_unit, :group_order_granularity, :group_order_unit)
+ article_version_params[:unit] = nil
+ article_version_params[:billing_unit] = article_version_params[:group_order_unit]
+ article_version_params[:price_unit] = article_version_params[:group_order_unit]
+ article_version_params[:article_unit_ratios_attributes] = {}
+ if sample[:first_ratio_unit].present?
+ article_version_params[:article_unit_ratios_attributes]['1'] = {
+ id: latest_article_version.article_unit_ratios.first&.id,
+ sort: 1,
+ quantity: sample[:first_ratio_quantity],
+ unit: sample[:first_ratio_unit]
+ }
+ end
+ article_version_params[:id] = latest_article_version.id
+ @invalid_articles << article unless article.update_attributes(latest_article_version_attributes: article_version_params)
+ end
+
+ errors = articles.find { |a| !a.errors.nil? }&.errors
+ @samples << {
+ unit: original_unit,
+ conversion_result: sample
+ .except(:article_ids, :first_ratio_quantity, :first_ratio_unit)
+ .merge(
+ first_ratio: {
+ quantity: sample[:first_ratio_quantity],
+ unit: sample[:first_ratio_unit]
+ }
+ ),
+ articles: articles,
+ errors: errors,
+ error: errors.present?
+ }
+ end
+ @supplier.update_attribute(:unit_migration_completed, Time.now)
+ raise ActiveRecord::Rollback unless @invalid_articles.empty?
+ end
+
+ if @invalid_articles.empty?
+ redirect_to supplier_articles_path(@supplier),
+ notice: I18n.t('articles.controller.complete_units_migration.notice')
+ else
+ additional_units = @samples.map do |sample|
+ [sample[:conversion_result][:supplier_order_unit], sample[:conversion_result][:group_order_unit],
+ sample[:conversion_result][:first_ratio]&.dig(:unit)]
+ end.flatten.uniq.compact
+ load_article_units(additional_units)
+
+ flash.now.alert = I18n.t('articles.controller.error_invalid')
+ render :migrate_units
+ end
+ end
+
+ # def migrate_units
+ # @articles = @supplier.articles.with_latest_versions_and_categories.undeleted.includes(latest_article_version: [:article_unit_ratios])
+ # @original_units = {}
+ # @articles.each do |article|
+ # article_version = article.latest_article_version
+ # quantity = 1
+ # ratios = article_version.article_unit_ratios
+
+ # @original_units[article.id] = article_version.unit
+
+ # next if ratios.length > 1
+
+ # if ratios.length == 1 && ratios[0].quantity != 1 && ratios[0].unit == 'XPP'
+ # quantity = ratios[0].quantity
+ # end
+
+ # conversion_result = ArticleUnitsLib.convert_old_unit(article_version.unit, quantity)
+
+ # next if conversion_result.nil? ||
+ # (conversion_result[:first_ratio].nil? && !article_version.article_unit_ratios.empty?) ||
+ # (!conversion_result[:first_ratio].nil? && article_version.article_unit_ratios.length > 1)
+
+ # article_version.unit = nil
+ # article_version.supplier_order_unit = conversion_result[:supplier_order_unit]
+ # article_version.group_order_granularity = conversion_result[:group_order_granularity]
+ # article_version.group_order_unit = conversion_result[:group_order_unit]
+
+ # first_ratio = conversion_result[:first_ratio]
+ # next if first_ratio.nil?
+
+ # first_ratio = first_ratio.merge(sort: 1)
+ # first_existing_ratio = article_version.article_unit_ratios[0]
+ # if first_existing_ratio.nil?
+ # article_version.article_unit_ratios.build(first_ratio)
+ # else
+ # first_existing_ratio.assign_attributes(first_ratio)
+ # end
+ # end
+
+ # load_article_units
+
+ # render :edit_all
+ # end
+
# Updates all article of specific supplier
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_attributes(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.with_latest_versions_and_categories
+ .includes(latest_article_version: [:article_unit_ratios])
+ .find(params[:articles].keys)
+ @articles.each do |article|
+ article_version_params = params[:articles][article.id.to_s]
+ article_version_params['id'] = article.latest_article_version.id
+ unless article.update_attributes(latest_article_version_attributes: article_version_params)
+ invalid_articles ||= true # Remember that there are validation errors
end
-
- raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
end
+
+ @supplier.update_attribute(:unit_migration_completed, Time.now) if params[:complete_migration]
+
+ raise ActiveRecord::Rollback if invalid_articles # Rollback all changes
end
end
@@ -117,7 +260,9 @@ def update_all
def update_selected
raise I18n.t('articles.controller.error_nosel') if params[:selected_articles].nil?
- articles = Article.find(params[:selected_articles])
+ articles = Article.with_latest_versions_and_categories
+ .includes(latest_article_version: [:article_unit_ratios])
+ .find(params[:selected_articles])
Article.transaction do
case params[:selected_action]
when 'destroy'
@@ -134,16 +279,15 @@ def update_selected
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,94 +295,159 @@ def parse_upload
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
+
+ @articles = @updated_article_pairs.pluck(0) + @new_articles
+ load_article_units
+
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
# renders a form with articles, which should be updated
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)
- end
- # sync articles against external database
- @updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_all
+ @updated_article_pairs, @outlisted_articles, @new_articles = @supplier.sync_from_remote
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty?
- redirect_to supplier_articles_path(@supplier), :notice => I18n.t('articles.controller.sync.notice')
+ redirect_to supplier_articles_path(@supplier),
+ notice: I18n.t('articles.controller.parse_upload.notice')
end
+ @ignored_article_count = 0
+ rescue StandardError => e
+ redirect_to upload_supplier_articles_path(@supplier), alert: I18n.t('errors.general_msg', msg: e.message)
end
# Updates, deletes articles when upload or sync form is submitted
def update_synchronized
- @outlisted_articles = Article.find(params[:outlisted_articles].try(:keys) || [])
- @updated_articles = Article.find(params[:articles].try(:keys) || [])
- @updated_articles.map { |a| a.assign_attributes(params[:articles][a.id.to_s]) }
- @new_articles = (params[:new_articles] || []).map { |a| @supplier.articles.build(a) }
+ @outlisted_articles = Article.includes(:latest_article_version).where(article_versions: { id: params[:outlisted_articles]&.values || [] })
+ @updated_articles = Article.includes(:latest_article_version).where(article_versions: { id: params[:articles]&.values&.map do |v|
+ v[:id]
+ end || [] })
+ @new_articles = (params[:new_articles]&.values || []).map do |a|
+ article = @supplier.articles.build
+ article_version = article.article_versions.build(a)
+ article.article_versions << article_version
+ article.latest_article_version = article_version
+ article_version.article = article
+ article
+ end
has_error = false
Article.transaction do
# delete articles
begin
@outlisted_articles.each(&:mark_as_deleted)
- rescue
+ rescue StandardError
# raises an exception when used in current order
has_error = true
end
# Update articles
- @updated_articles.each { |a| a.save or has_error = true }
+ @updated_articles.each_with_index do |a, index|
+ current_params = params[:articles][index.to_s]
+ current_params.delete(:id)
+
+ a.latest_article_version.article_unit_ratios.clear
+ a.latest_article_version.assign_attributes(current_params)
+ a.save
+ end or has_error = true
# Add new articles
@new_articles.each { |a| a.save or has_error = true }
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
- # renders a view to import articles in local database
- #
- def shared
- # build array of keywords, required for ransack _all suffix
- q = params.fetch(:q, {})
- 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
- end
+ private
- # 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
- else
- render :action => 'new', :layout => false
+ def build_article_migration_samples
+ articles = @supplier.articles.with_latest_versions_and_categories.undeleted.includes(latest_article_version: [:article_unit_ratios])
+ samples_hash = {}
+ articles.each do |article|
+ article_version = article.latest_article_version
+ quantity = 1
+ ratios = article_version.article_unit_ratios
+
+ next if ratios.length > 1 ||
+ article_version.billing_unit != article_version.group_order_unit ||
+ article_version.price_unit != article_version.group_order_unit
+
+ quantity = ratios[0].quantity if ratios.length == 1 && ratios[0].quantity != 1 && ratios[0].unit == 'XPP'
+
+ samples_hash[article_version.unit] = {} if samples_hash[article_version.unit].nil?
+ samples_hash[article_version.unit][quantity] = [] if samples_hash[article_version.unit][quantity].nil?
+ samples_hash[article_version.unit][quantity] << article
+ end
+ @samples = samples_hash.map do |unit, quantities_hash|
+ quantities_hash.map do |quantity, sample_articles|
+ conversion_result = ArticleUnitsLib.convert_old_unit(unit, quantity)
+ { unit: unit, quantity: quantity, articles: sample_articles, conversion_result: conversion_result }
+ end
end
+ @samples = @samples.flatten
+ .reject { |sample| sample[:conversion_result].nil? }
+
+ additional_units = @samples.map do |sample|
+ [sample[:conversion_result][:supplier_order_unit], sample[:conversion_result][:group_order_unit],
+ sample[:conversion_result][:first_ratio]&.dig(:unit)]
+ end.flatten.uniq.compact
+ load_article_units(additional_units)
end
- private
+ def load_article
+ @article = Article
+ .with_latest_versions_and_categories
+ .includes(latest_article_version: [:article_unit_ratios])
+ .find(params[:id])
+ end
+
+ def load_article_units(additional_units = [])
+ additional_units = if !@article.nil?
+ @article.current_article_units
+ elsif !@articles.nil?
+ @articles.map(&:current_article_units)
+ .flatten
+ .uniq
+ else
+ additional_units
+ end
+
+ @article_units = ArticleUnit.as_options(additional_units: additional_units)
+ @all_units = ArticleUnit.as_hash(additional_units: additional_units)
+ end
+
+ def load_article_categories
+ @article_categories = ArticleCategory.all
+ end
+
+ def new_empty_article_ratio
+ @empty_article_unit_ratio = ArticleUnitRatio.new
+ @empty_article_unit_ratio.article_version = @article.latest_article_version unless @article.nil?
+ @empty_article_unit_ratio.sort = -1
+ end
# @return [Number] Number of articles not taken into account when syncing (having no number)
def ignored_article_count
if action_name == 'sync' || params[:from_action] == 'sync'
- @ignored_article_count ||= @supplier.articles.undeleted.where(order_number: [nil, '']).count
+ @ignored_article_count ||= @supplier.articles.includes(:latest_article_version).undeleted.where(article_versions: { order_number: [
+ nil, ''
+ ] }).count
else
0
end
diff --git a/app/controllers/concerns/auth.rb b/app/controllers/concerns/auth.rb
index 277acd69..f08622ad 100644
--- a/app/controllers/concerns/auth.rb
+++ b/app/controllers/concerns/auth.rb
@@ -9,15 +9,17 @@ 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 +49,7 @@ def logout
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
@@ -62,9 +59,9 @@ def authenticate(role = 'any')
when 'pickups' then current_user.role_pickups?
when 'suppliers' then current_user.role_suppliers?
when 'orders' then current_user.role_orders?
- when 'finance_or_invoices' then (current_user.role_finance? || current_user.role_invoices?)
- when 'finance_or_orders' then (current_user.role_finance? || current_user.role_orders?)
- when 'pickups_or_orders' then (current_user.role_pickups? || current_user.role_orders?)
+ when 'finance_or_invoices' then current_user.role_finance? || current_user.role_invoices?
+ when 'finance_or_orders' then current_user.role_finance? || current_user.role_orders?
+ when 'pickups_or_orders' then current_user.role_pickups? || current_user.role_orders?
when 'any' then true # no role required
else false # any unknown role will always fail
end
@@ -73,6 +70,11 @@ def authenticate(role = 'any')
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 +118,13 @@ def authenticate_pickups_or_orders
# 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 @@ def doorkeeper_authorize!(*scopes)
# 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 @@ def doorkeeper_authorize_roles!(*scopes)
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 @@ def doorkeeper_scope_permitted?(scope)
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 @@ def select_foodcoop
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..c55d48a2 100644
--- a/app/controllers/concerns/locale.rb
+++ b/app/controllers/concerns/locale.rb
@@ -30,7 +30,7 @@ def default_language
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 @@ def available_locales
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 21ae0c71..955cea82 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')
@@ -7,7 +7,7 @@ def index
def show
@delivery = Delivery.find(params[:id])
- @stock_changes = @delivery.stock_changes.includes(:stock_article).order('articles.name ASC')
+ @stock_changes = @delivery.stock_changes.includes(stock_article: :latest_article_version).order('article_versions.name ASC')
end
def new
@@ -15,6 +15,10 @@ def new
@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 @@ def create
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 @@ def update
flash[:notice] = I18n.t('deliveries.update.notice')
redirect_to [@supplier, @delivery]
else
- render :action => "edit"
+ render action: 'edit'
end
end
@@ -52,18 +52,18 @@ def destroy
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 09c109f8..6f85fd13 100644
--- a/app/controllers/finance/balancing_controller.rb
+++ b/app/controllers/finance/balancing_controller.rb
@@ -5,22 +5,22 @@ def index
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,
+ @articles = @order.order_articles.ordered_or_member.includes(article_version: :article,
group_order_articles: { group_order: :ordergroup })
sort_param = params['sort'] || 'name'
@articles = case sort_param
- when 'name' then
- @articles.order('articles.name ASC')
- when 'name_reverse' then
- @articles.order('articles.name DESC')
- when 'order_number' then
- @articles.order('articles.order_number ASC')
- when 'order_number_reverse' then
- @articles.order('articles.order_number DESC')
+ when 'name'
+ @articles.order('article_versions.name ASC')
+ when 'name_reverse'
+ @articles.order('article_versions.name DESC')
+ when 'order_number'
+ @articles.order('article_versions.order_number ASC')
+ when 'order_number_reverse'
+ @articles.order('article_versions.order_number DESC')
else
@articles
end
@@ -31,13 +31,13 @@ def new
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 @@ def update_summary
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_attributes(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_attributes! 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,19 @@ def close
@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')
+ # TODO-article-version
+ # rescue => error
+ # redirect_to new_finance_order_url(order_id: @order.id), alert: t('finance.balancing.close.alert', message: error.message)
end
# Close the order directly, without automaticly updating ordergroups account balances
def close_direct
@order = Order.find(params[:id])
@order.close_direct!(@current_user)
- redirect_to finance_order_index_url, notice: t('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 +104,8 @@ def close_all_direct_with_invoice
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 @@ def assign_unlinked_transactions
@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 @@ def import
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..09783640 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 @@ def show
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 @@ def new_financial_transaction
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
diff --git a/app/controllers/finance/financial_transactions_controller.rb b/app/controllers/finance/financial_transactions_controller.rb
index 8b9d372d..c1a619a5 100644
--- a/app/controllers/finance/financial_transactions_controller.rb
+++ b/app/controllers/finance/financial_transactions_controller.rb
@@ -1,22 +1,22 @@
class Finance::FinancialTransactionsController < ApplicationController
before_action :authenticate_finance
- before_action :find_ordergroup, :except => [:new_collection, :create_collection, :index_collection]
+ before_action :find_ordergroup, except: %i[new_collection create_collection index_collection]
inherit_resources
# belongs_to :ordergroup
def index
- if params['sort']
- sort = case params['sort']
- when "date" then "created_on"
- when "note" then "note"
- when "amount" then "amount"
- when "date_reverse" then "created_on DESC"
- when "note_reverse" then "note DESC"
- when "amount_reverse" then "amount DESC"
+ sort = if params['sort']
+ case params['sort']
+ when 'date' then 'created_on'
+ when 'note' then 'note'
+ when 'amount' then 'amount'
+ when 'date_reverse' then 'created_on DESC'
+ when 'note_reverse' then 'note DESC'
+ when 'amount_reverse' then 'amount DESC'
end
- else
- sort = "created_on DESC"
- end
+ else
+ 'created_on DESC'
+ end
@q = FinancialTransaction.search(params[:q])
@financial_transactions_all = @q.result(distinct: true).includes(:user).order(sort)
@@ -26,9 +26,11 @@ def index
@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 @@ def index_collection
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 @@ def create
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 @@ def create_collection
params[:financial_transactions].each do |trans|
# ignore empty amount fields ...
- unless trans[:amount].blank?
- amount = 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 = 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 @@ def create_collection
user: @current_user,
amount: foodcoop_amount,
note: params[:note],
- financial_link: financial_link,
+ financial_link: financial_link
})
ft.save!
end
@@ -117,8 +121,8 @@ def create_collection
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 33949017..64cab6d8 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 @@ def unpaid
@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 @@ def edit
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 @@ def create
end
else
fill_deliveries_and_orders_collection @invoice.id, @invoice.supplier_id
- render :action => "new"
+ render action: 'new'
end
end
@@ -81,7 +83,7 @@ def attachment
@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 @@ def find_invoice
# 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..a8836f6b 100644
--- a/app/controllers/finance/ordergroups_controller.rb
+++ b/app/controllers/finance/ordergroups_controller.rb
@@ -1,11 +1,11 @@
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)
diff --git a/app/controllers/foodcoop/ordergroups_controller.rb b/app/controllers/foodcoop/ordergroups_controller.rb
index 8aeb977e..a6a6d104 100644
--- a/app/controllers/foodcoop/ordergroups_controller.rb
+++ b/app/controllers/foodcoop/ordergroups_controller.rb
@@ -2,19 +2,15 @@ class Foodcoop::OrdergroupsController < ApplicationController
def index
@ordergroups = Ordergroup.undeleted.order('name')
- 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 a3a8bd45..c4684126 100644
--- a/app/controllers/foodcoop/users_controller.rb
+++ b/app/controllers/foodcoop/users_controller.rb
@@ -3,17 +3,18 @@ def index
@users = User.undeleted.natural_order
# 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 794c21a0..ce63ac84 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 @@ def edit
def update
@workgroup = Workgroup.find(params[:id])
if @workgroup.update_attributes(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 d34db7a1..4351b04b 100644
--- a/app/controllers/group_order_articles_controller.rb
+++ b/app/controllers/group_order_articles_controller.rb
@@ -1,8 +1,8 @@
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
+ layout false # We only use this controller to server js snippets, no need for layout rendering
def new
@order_article = OrderArticle.find(params[:order_article_id])
@@ -65,7 +65,7 @@ def update_summaries(group_order_article)
# Update the price attribute of new GroupOrder
group_order_article.group_order.update_price!
# Update units_to_order of order_article
- group_order_article.order_article.update_results! if group_order_article.order_article.article.is_a?(StockArticle)
+ group_order_article.order_article.update_results! if group_order_article.order_article.article_version.is_a?(StockArticle)
end
def find_group_order_article
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 @@ def index
@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 @@ def create
@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 @@ def archive
# 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 @@ def ensure_open_order
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 6f677b6b..50c4dce7 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -9,8 +9,7 @@ def index
@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 @@ def ordergroup
@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 @@ def user_params
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 @@ def create
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 @@ def forgot_password
# 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 @@ def update_password
@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 @@ def accept_invitation
@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 @@ def accept_invitation
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..bd5bd33d 100644
--- a/app/controllers/order_articles_controller.rb
+++ b/app/controllers/order_articles_controller.rb
@@ -1,38 +1,39 @@
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]
+ before_action :load_order_article, only: %i[edit update]
+ before_action :load_article_units, only: %i[edit update]
+ before_action :new_empty_article_ratio, only: %i[edit update]
- layout false # We only use this controller to serve js snippets, no need for layout rendering
+ layout false # We only use this controller to serve js snippets, no need for layout rendering
def new
@order_article = @order.order_articles.build(params[:order_article])
end
+ def edit; 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.joins(:article_version).where(article_versions: { article_id: params[:order_article][:article_version][: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
- render action: :edit
- end
+ # begin
+ version_params = params.require(:article_version).permit(:id, :unit, :supplier_order_unit, :minimum_order_quantity,
+ :billing_unit, :group_order_granularity, :group_order_unit, :price, :price_unit, :tax, :deposit, article_unit_ratios_attributes: %i[id sort quantity unit _destroy])
+ @order_article.update_handling_versioning!(params[:order_article], version_params)
+ # TODO-article-version
+ # rescue
+ # render action: :edit
+ # end
end
def destroy
@@ -61,4 +62,20 @@ def authenticate_finance_orders_or_pickup
deny_access
end
+
+ def load_order_article
+ @order_article = OrderArticle.includes(article_version: :article_unit_ratios).find(params[:id])
+ end
+
+ def load_article_units
+ additional_units = @order_article&.article_version&.article&.current_article_units || []
+ @article_units = ArticleUnit.as_options(additional_units: additional_units)
+ @all_units = ArticleUnit.as_hash(additional_units: additional_units)
+ end
+
+ def new_empty_article_ratio
+ @empty_article_unit_ratio = ArticleUnitRatio.new
+ @empty_article_unit_ratio.article_version = @order_article.price
+ @empty_article_unit_ratio.sort = -1
+ 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 72b10deb..b44ee192 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 @@ def show
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 @@ def new
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 @@ def create
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_attributes 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,9 @@ def finish
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)
+ # TODO-article-version
+ # rescue => error
+ # redirect_to orders_url, alert: I18n.t('errors.general_msg', :msg => error.message)
end
# Send a order to the supplier.
@@ -122,20 +124,19 @@ def send_result_to_supplier
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)
+ # TODO-article-version
+ # rescue => error
+ # redirect_to order, alert: I18n.t('errors.general_msg', :msg => error.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 +146,25 @@ def receive
else
redirect_to receive_order_path(@order)
end
+ else
+ @order_articles = @order.order_articles.ordered_or_member.includes(:article_version).order('article_versions.order_number, article_versions.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,30 +179,39 @@ def update_order_amounts
# "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?
+ units_received = oa.article_version.convert_quantity(oa.units_received,
+ oa.article_version.supplier_order_unit, oa.article_version.billing_unit)
+ cunits[0] += units_received
+ oacounts = oa.redistribute units_received, 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
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 5b3d0780..6cc988ab 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -12,16 +12,16 @@ def create
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')
+ redirect_to login_url, notice: I18n.t('sessions.logged_out')
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..d61488a0 100644
--- a/app/controllers/stock_takings_controller.rb
+++ b/app/controllers/stock_takings_controller.rb
@@ -7,21 +7,23 @@ def index
def new
@stock_taking = StockTaking.new
- StockArticle.undeleted.each { |a| @stock_taking.stock_changes.build(:stock_article => a) }
+ StockArticle.with_latest_versions_and_categories.undeleted.each do |a|
+ @stock_taking.stock_changes.build(stock_article: a)
+ end
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 c44e3380..6bea7b90 100644
--- a/app/controllers/stockit_controller.rb
+++ b/app/controllers/stockit_controller.rb
@@ -1,88 +1,121 @@
class StockitController < ApplicationController
+ before_action :new_empty_article_ratio, only: %i[edit update new create derive copy]
+ before_action :load_article,
+ only: %i[index_on_stock_article_create index_on_stock_article_update show edit update show_on_stock_article_update
+ destroy]
+ before_action :load_article_units, only: %i[edit update new create derive copy]
+ before_action :prepare_params_for_create_update, only: %i[create update]
+
def index
- @stock_articles = StockArticle.undeleted.includes(:supplier, :article_category)
- .order('suppliers.name, article_categories.name, articles.name')
+ @stock_articles = StockArticle.with_latest_versions_and_categories.joins(:supplier).order('suppliers.name, article_categories.name, article_versions.name').undeleted
end
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
- render :layout => false
+ def show
+ @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])
+ @stock_article.latest_article_version = ArticleVersion.new
- render :layout => false
+ render layout: false
end
# (2) StockArticle as template
def copy
- @stock_article = StockArticle.find(params[:stock_article_id]).dup
+ stock_article = StockArticle.find(params[:stock_article_id])
+ @stock_article = stock_article.dup
+ @stock_article.latest_article_version = stock_article.latest_article_version.duplicate_including_article_unit_ratios
- render :layout => false
+ render layout: false
end
# (3) non-stock Article as template
def derive
- @stock_article = Article.find(params[:old_article_id]).becomes(StockArticle).dup
-
- render :layout => false
- end
+ article = Article.find(params[:old_article_id])
+ @stock_article = article.becomes(StockArticle).dup
+ @stock_article.latest_article_version = article.latest_article_version.duplicate_including_article_unit_ratios
- 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
+ render layout: false
end
def edit
- @stock_article = StockArticle.find(params[:id])
-
- render :layout => false
+ render layout: false
end
- def update
- @stock_article = StockArticle.find(params[:id])
- @stock_article.update_attributes!(params[:stock_article])
- render :layout => false
+ def create
+ StockArticle.transaction do
+ @stock_article = StockArticle.create(quantity: 0, supplier_id: @supplier_id)
+ @stock_article.attributes = { latest_article_version_attributes: params[:article_version] }
+ @stock_article.save
+ end
+ render layout: false
rescue ActiveRecord::RecordInvalid
- render :action => 'edit', :layout => false
+ render action: 'new', layout: false
end
- def show
- @stock_article = StockArticle.find(params[:id])
- @stock_changes = @stock_article.stock_changes.order('stock_changes.created_at DESC')
+ def update
+ article_version_attributes = params[:article_version]
+ @stock_article.update_attributes!(supplier_id: @supplier_id,
+ latest_article_version_attributes: article_version_attributes)
+ 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
+
+ private
+
+ def load_article
+ @stock_article = StockArticle.find(params[:id])
+ end
+
+ def load_article_units
+ additional_units = @stock_article&.current_article_units || []
+ @article_units = ArticleUnit.as_options(additional_units: additional_units)
+ @all_units = ArticleUnit.as_hash(additional_units: additional_units)
+ end
+
+ def new_empty_article_ratio
+ @empty_article_unit_ratio = ArticleUnitRatio.new
+ @empty_article_unit_ratio.article_version = @article.latest_article_version unless @article.nil?
+ @empty_article_unit_ratio.sort = -1
+ end
+
+ def prepare_params_for_create_update
+ @supplier_id = params[:article_version][:article][:supplier_id]
+ params[:article_version].delete :article
+
+ # Stock article form doesn't have some of the unit fields -> set them to supplier_order_unit:
+ params[:article_version][:group_order_unit] = params[:article_version][:supplier_order_unit]
+ params[:article_version][:billing_unit] = params[:article_version][:supplier_order_unit]
+ params[:article_version][:price_unit] = params[:article_version][:supplier_order_unit]
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/supplier_shares_controller.rb b/app/controllers/supplier_shares_controller.rb
new file mode 100644
index 00000000..cac31d41
--- /dev/null
+++ b/app/controllers/supplier_shares_controller.rb
@@ -0,0 +1,15 @@
+class SupplierSharesController < ApplicationController
+ def create
+ @supplier = Supplier.find(params[:supplier_id])
+ @supplier.update_attribute(:external_uuid, SecureRandom.uuid)
+
+ render 'update'
+ end
+
+ def destroy
+ @supplier = Supplier.find(params[:supplier_id])
+ @supplier.update_attribute(:external_uuid, nil)
+
+ render 'update'
+ end
+end
diff --git a/app/controllers/suppliers_controller.rb b/app/controllers/suppliers_controller.rb
index 058c4fbc..56ddf224 100644
--- a/app/controllers/suppliers_controller.rb
+++ b/app/controllers/suppliers_controller.rb
@@ -1,5 +1,7 @@
+require 'ostruct'
+
class SuppliersController < ApplicationController
- before_action :authenticate_suppliers, :except => [:index, :list]
+ before_action :authenticate_suppliers, except: %i[index list]
helper :deliveries
def index
@@ -14,14 +16,12 @@ def show
end
# new supplier
- # if shared_supplier_id is given, the new supplier will filled whith its attributes
def new
- if params[:shared_supplier_id]
- shared_supplier = SharedSupplier.find(params[:shared_supplier_id])
- @supplier = shared_supplier.suppliers.new(shared_supplier.autofill_attributes)
- else
- @supplier = Supplier.new
- end
+ @supplier = Supplier.new
+ end
+
+ def edit
+ @supplier = Supplier.find(params[:id])
end
def create
@@ -31,21 +31,17 @@ def create
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_attributes(supplier_params)
flash[:notice] = I18n.t('suppliers.update.notice')
redirect_to @supplier
else
- render :action => 'edit'
+ render action: 'edit'
end
end
@@ -54,23 +50,34 @@ def destroy
@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
- # gives a list with all available shared_suppliers
- def shared_suppliers
- @shared_suppliers = SharedSupplier.all
+ def remote_articles
+ @supplier = Supplier.find(remote_articles_params.fetch(:supplier_id))
+ search_params = {}
+ search_params[:name] = params.fetch(:name).split if params.include?(:name)
+ search_params[:origin] = params.fetch(:origin) if params.include?(:origin)
+ search_params[:page] = params.fetch(:page, 1)
+ search_params[:per_page] = @per_page
+ data = @supplier.read_from_remote(search_params)
+ @articles = data[:articles]
+ @pagination = OpenStruct.new(data[:pagination])
end
private
+ def remote_articles_params
+ params.permit(:supplier_id, :name, :origin)
+ end
+
def supplier_params
params
.require(:supplier)
.permit(:name, :address, :phone, :phone2, :fax, :email, :url, :contact_person, :customer_number,
:iban, :custom_fields, :delivery_days, :order_howto, :note, :supplier_category_id,
- :shared_supplier_id, :min_order_quantity, :shared_sync_method)
+ :min_order_quantity, :shared_sync_method, :supplier_remote_source)
end
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 @@ def user
@accepted_tasks = Task.accepted_tasks_for(current_user)
end
+ def show
+ @task = Task.find(params[:id])
+ end
+
def new
@task = Task.new(current_user_id: current_user.id)
end
+ def edit
+ @task = Task.find(params[:id])
+ @periodic = !!params[:periodic]
+ @task.current_user_id = current_user.id
+ end
+
def create
@task = Task.new(current_user_id: current_user.id)
@task.created_by = current_user
@task.attributes = (task_params)
- if params[:periodic]
- @task.periodic_task_group = PeriodicTaskGroup.new
- end
+ @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')
+ redirect_to tasks_url, notice: I18n.t('tasks.create.notice')
else
- render :template => "tasks/new"
+ render template: 'tasks/new'
end
end
- def show
- @task = Task.find(params[:id])
- end
-
- def edit
- @task = Task.find(params[:id])
- @periodic = !!params[:periodic]
- @task.current_user_id = current_user.id
- end
-
def update
@task = Task.find(params[:id])
task_group = @task.periodic_task_group
@@ -50,16 +48,14 @@ def update
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 @@ def destroy
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 @@ def accept
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 @@ def archive
# 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..bb8bebcc 100644
--- a/app/documents/order_by_articles.rb
+++ b/app/documents/order_by_articles.rb
@@ -1,15 +1,16 @@
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
each_order_article do |order_article|
+ article_version = order_article.article_version
dimrows = []
rows = [[
GroupOrder.human_attribute_name(:ordergroup),
@@ -21,13 +22,16 @@ def body
each_group_order_article_for_order_article(order_article) do |goa|
dimrows << rows.length if goa.result == 0
rows << [goa.group_order.ordergroup_name,
- group_order_article_quantity_with_tolerance(goa),
- goa.result,
+ billign_quantity_with_tolerance(goa),
+ article_version.convert_quantity(goa.result, article_version.group_order_unit,
+ article_version.billing_unit),
number_to_currency(goa.total_price)]
end
next unless rows.length > 1
- name = "#{order_article.article.name} (#{order_article.article.unit} | #{order_article.price.unit_quantity} | #{number_to_currency(order_article.price.fc_price)})"
+ name = "#{article_version.name}, #{format_billing_unit_with_ratios(article_version)}, #{number_to_currency(article_version.convert_quantity(
+ article_version.fc_price, article_version.billing_unit, article_version.supplier_order_unit
+ ))}"
name += " #{order_article.order.name}" if @options[:show_supplier]
nice_table name, rows, dimrows do |table|
table.column(0).width = bounds.width / 2
diff --git a/app/documents/order_by_groups.rb b/app/documents/order_by_groups.rb
index d6711731..6d91e98c 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
@@ -23,11 +23,11 @@ def body
each_group_order_article_for_ordergroup(oa_id) do |goa|
dimrows << rows.length if goa.result == 0
- rows << [goa.order_article.article.name,
+ rows << [goa.order_article.article_version.name,
goa.group_order.order.name,
- group_order_article_quantity_with_tolerance(goa),
- group_order_article_result(goa),
- order_article_price_per_unit(goa.order_article),
+ billign_quantity_with_tolerance(goa),
+ billing_article_result(goa),
+ price_per_billing_unit(goa),
number_to_currency(goa.total_price)]
end
next unless rows.length > 1
diff --git a/app/documents/order_fax.rb b/app/documents/order_fax.rb
index b4b50577..4a1f741c 100644
--- a/app/documents/order_fax.rb
+++ b/app/documents/order_fax.rb
@@ -1,8 +1,10 @@
class OrderFax < OrderPdf
+ include ArticlesHelper
+
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
@@ -10,9 +12,20 @@ def title
end
def body
- contact = FoodsoftConfig[:contact].symbolize_keys
+ from_paragraph
+
+ recipient_paragraph
+
+ articles_paragraph
+ rescue StandardError => e
+ Rails.logger.info e.backtrace
+ raise # always reraise
+ end
+
+ private
- # From paragraph
+ def from_paragraph
+ contact = FoodsoftConfig[:contact].symbolize_keys
bounding_box [margin_box.right - 200, margin_box.top], width: 200 do
text FoodsoftConfig[:name], size: fontsize(9), align: :right
move_down 5
@@ -20,25 +33,28 @@ def body
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
+ end
- # Recipient
+ def recipient_paragraph
bounding_box [margin_box.left, margin_box.top - 60], width: 200 do
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,23 +66,27 @@ def body
move_down 10
text "#{Delivery.human_attribute_name :date}:"
move_down 10
- unless order.supplier.try(:contact_person).blank?
- text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}"
- move_down 10
- end
+ return if order.supplier.try(:contact_person).blank?
- # Articles
+ text "#{Supplier.human_attribute_name :contact_person}: #{order.supplier[:contact_person]}"
+ move_down 10
+ end
+
+ def articles_paragraph
total = 0
data = [I18n.t('documents.order_fax.rows')]
each_order_article do |oa|
- subtotal = oa.units_to_order * oa.price.unit_quantity * oa.price.price
+ price = oa.article_version.price
+ subtotal = oa.units_to_order * price
total += subtotal
- data << [oa.article.order_number,
+ data << [oa.article_version.order_number,
oa.units_to_order,
- oa.article.name,
- oa.price.unit_quantity,
- oa.article.unit,
- number_to_currency(oa.price.price),
+ oa.article_version.name,
+ # TODO-article-units: Why should we show the supplier the group order unit quantity?:
+ oa.article_version.convert_quantity(1, oa.article_version.supplier_order_unit,
+ oa.article_version.group_order_unit),
+ format_supplier_order_unit_with_ratios(oa.price),
+ number_to_currency(price),
number_to_currency(subtotal)]
end
data << [I18n.t('documents.order_fax.total'), nil, nil, nil, nil, nil, number_to_currency(total)]
@@ -78,8 +98,8 @@ def body
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),
@@ -89,16 +109,14 @@ def body
# align: {0 => :left}
end
- private
-
def order_articles
order.order_articles.ordered
- .joins(:article)
- .order('articles.order_number').order('articles.name')
- .preload(:article, :article_price)
+ .joins(:article_version)
+ .order('article_versions.order_number').order('article_versions.name')
+ .preload(article_version: :article)
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..9676b760 100644
--- a/app/documents/order_matrix.rb
+++ b/app/documents/order_matrix.rb
@@ -3,29 +3,29 @@ 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
order_articles_data = [[
OrderArticle.human_attribute_name(:article),
Article.human_attribute_name(:supplier),
- ArticlePrice.human_attribute_name(:unit_quantity),
+ ArticleVersion.human_attribute_name(:unit_quantity),
OrderArticle.human_attribute_name(:units_received),
Article.human_attribute_name(:fc_price_short)
]]
- each_order_article do |a|
- order_articles_data << [a.article.name,
- a.article.supplier.name,
- a.price.unit_quantity,
- a.units,
- order_article_price_per_unit(a)]
+ each_order_article do |oa|
+ order_articles_data << [oa.article_version.name,
+ oa.article_version.article.supplier.name,
+ oa.article_version.unit_quantity,
+ oa.units,
+ order_article_price_per_unit(oa)]
end
order_articles_data.each { |row| row.delete_at 1 } unless @options[:show_supplier]
@@ -68,7 +68,7 @@ def body
last_supplier_id = -1
each_order_article do |order_article|
- supplier = order_article.article.supplier
+ supplier = order_article.article_version.article.supplier
if @options[:show_supplier] && last_supplier_id != supplier.id
row = [make_cell(supplier.name, colspan: 2, font_style: :bold)]
batch_groups.each { row << nil }
@@ -76,7 +76,7 @@ def body
last_supplier_id = supplier.id
end
- row = [order_article.article.name, order_article_price_per_unit(order_article)]
+ row = [order_article.article_version.name, order_article_price_per_unit(order_article)]
row += batch_results[order_article.id] if batch_results[order_article.id]
rows << row
end
@@ -87,7 +87,7 @@ def body
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 @@ def body
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 @@ def config_input(form, key, options = {}, &block)
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 @@ def config_input_field(form, key, options = {})
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 @@ def config_input_field(form, key, options = {})
# @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 @@ def config_use_heading(form, key, options = {})
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 @@ def config_input_tooltip_options(form, key, options)
# 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..5412e1bc 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: '%d.%m.%Y %H:%M') unless time.nil?
end
def format_date(time = Time.now)
@@ -16,7 +16,7 @@ def format_datetime(time = Time.now)
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 @@ def format_currency(amount)
# 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 @@ def sort_link_helper(text, key, options = {})
# 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 @@ def sort_link_helper(text, key, options = {})
# 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",
+ 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",
+ options.merge({ fallback: true, default: '', count: 2 }))
+ s = raw "#{sshort}" if sshort.present?
end
s
end
@@ -117,7 +117,7 @@ def link_to_top
# 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 @@ def tab_is_active?(tab)
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 @@ def icon(name, options = {})
# 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 @@ def bootstrap_flash_patched
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 @@ def bootstrap_flash_patched
# 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 @@ def show_user(user = @current_user, options = {})
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,13 @@ def foodcoop_css_path(options = {})
# @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
+
+ def format_number(value)
+ format('%g', format('%.2f', value))
end
end
diff --git a/app/helpers/articles_helper.rb b/app/helpers/articles_helper.rb
index add1c6ba..45781967 100644
--- a/app/helpers/articles_helper.rb
+++ b/app/helpers/articles_helper.rb
@@ -1,15 +1,81 @@
module ArticlesHelper
# useful for highlighting attributes, when synchronizing articles
- def highlight_new(unequal_attributes, attribute)
+ def highlight_new(unequal_attributes, attributes)
+ attributes = [attributes] unless attributes.is_a?(Array)
return unless unequal_attributes
- unequal_attributes.has_key?(attribute) ? "background-color: yellow" : ""
+ intersection = (unequal_attributes.keys & attributes)
+ intersection.empty? ? '' : '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
+
+ def format_unit(unit_property, article)
+ unit_code = article.send(unit_property)
+ return article.unit if unit_code.nil?
+
+ unit = ArticleUnitsLib.units.to_h[unit_code]
+ unit[:symbol] || unit[:name]
+ end
+
+ def format_supplier_order_unit(article)
+ format_unit(:supplier_order_unit, article)
+ end
+
+ def format_group_order_unit(article)
+ format_unit(:group_order_unit, article)
+ end
+
+ def format_billing_unit(article)
+ format_unit(:billing_unit, article)
+ end
+
+ def format_unit_with_ratios(unit_property, article)
+ base = format_unit(unit_property, article)
+ unit_code = article.send(unit_property)
+ return base if ArticleUnitsLib.unit_is_si_convertible(unit_code)
+
+ relevant_units = [article.article_unit_ratios.map(&:unit)]
+ relevant_units << article.supplier_order_unit unless unit_property == :supplier_order_unit
+ first_si_convertible_unit = relevant_units
+ .flatten
+ .find { |unit| ArticleUnitsLib.unit_is_si_convertible(unit) }
+
+ return base if first_si_convertible_unit.nil?
+
+ quantity = article.convert_quantity(1, unit_code, first_si_convertible_unit)
+ "#{base} (#{format_number(quantity)}\u00a0#{ArticleUnitsLib.units.to_h[first_si_convertible_unit][:symbol]})"
+ end
+
+ def format_supplier_order_unit_with_ratios(article)
+ format_unit_with_ratios(:supplier_order_unit, article)
+ end
+
+ def format_group_order_unit_with_ratios(article)
+ format_unit_with_ratios(:group_order_unit, article)
+ end
+
+ def format_billing_unit_with_ratios(article)
+ format_unit_with_ratios(:billing_unit, article)
+ end
+
+ def field_with_preset_value_and_errors(options)
+ form, field, value, field_errors, input_html = options.values_at(:form, :field, :value, :errors, :input_html)
+ form.input field, label: false, wrapper_html: { class: field_errors.blank? ? '' : 'error' },
+ input_html: input_html do
+ output = [form.input_field(field, { value: value }.merge(input_html))]
+ if field_errors.present?
+ errors = tag.span(class: 'help-inline') do
+ field_errors.join(', ')
+ end
+ output << errors
+ end
+ safe_join(output)
+ end
end
end
diff --git a/app/helpers/deliveries_helper.rb b/app/helpers/deliveries_helper.rb
index a97a7df7..6d4aa0f1 100644
--- a/app/helpers/deliveries_helper.rb
+++ b/app/helpers/deliveries_helper.rb
@@ -10,23 +10,27 @@ def link_to_invoice(delivery)
end
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.reorder('article_versions.name ASC')
+ articles = articles.reject { |a| !except.index(a.id).nil? } if except
+ block_given? or block = proc { |a| "#{a.name} (#{number_to_currency a.price}/#{a.unit})" }
articles.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)
- articles.undeleted.reorder('articles.name ASC')
+ articles.undeleted.reorder('article_versions.name ASC')
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..db2fd087 100644
--- a/app/helpers/group_order_articles_helper.rb
+++ b/app/helpers/group_order_articles_helper.rb
@@ -1,13 +1,28 @@
module GroupOrderArticlesHelper
# return an edit field for a GroupOrderArticle result
- def group_order_article_edit_result(goa)
+ def group_order_article_edit_result(goa, convert_to_billing_unit = true)
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?
+ order_article = goa.order_article
+ article_version = order_article.article_version
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
+ quantity_data = ratio_quantity_data(order_article, order_article.article_version.billing_unit)
+ converted_value = if convert_to_billing_unit
+ article_version.convert_quantity(goa.result,
+ article_version.group_order_unit, article_version.billing_unit)
+ else
+ result
+ end
+ input_data = { min: 0 }.merge(quantity_data)
+ if convert_to_billing_unit
+ input_data = input_data.merge('multiply-before-submit': article_version.convert_quantity(1,
+ article_version.billing_unit, article_version.group_order_unit))
+ end
+ f.input_field(:result, as: :delta, class: 'input-nano', data: input_data, id: "r_#{goa.id}",
+ value: converted_value.round(3))
end
+ else
+ result
end
end
end
diff --git a/app/helpers/group_orders_helper.rb b/app/helpers/group_orders_helper.rb
index c5e27c66..438b1230 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 @@ def link_to_ordering(order, options = {}, &block)
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 @@ def link_to_ordering(order, options = {}, &block)
# 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'
@@ -44,13 +45,20 @@ def get_order_results(order_article, group_order_id)
{ group_order_article: goa, quantity: quantity, tolerance: tolerance, result: result, sub_total: sub_total }
end
- def get_missing_units_css_class(quantity_missing)
- if (quantity_missing == 1)
- return 'missing-few';
- elsif (quantity_missing == 0)
- return ''
+ def requires_tolerance_input?(order_article, ordering_data)
+ (
+ !order_article.article_version.supplier_order_unit_is_si_convertible &&
+ ordering_data[:order_articles][order_article.id][:ratio_group_order_unit_supplier_unit] != order_article.article_version.group_order_granularity
+ ) || (order_article.article_version.minimum_order_quantity.presence || 0) > order_article.article_version.group_order_granularity
+ end
+
+ def get_missing_units_css_class(quantity_missing, article_version)
+ if quantity_missing == 0
+ ''
+ elsif quantity_missing <= article_version.group_order_granularity
+ 'missing-few'
else
- return 'missing-many'
+ 'missing-many'
end
end
end
diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb
index ff238730..91e08a46 100644
--- a/app/helpers/orders_helper.rb
+++ b/app/helpers/orders_helper.rb
@@ -18,7 +18,7 @@ def order_pdf(order, document, text, options = {})
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,18 +29,44 @@ def units_history_line(order_article, options = {})
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
+ price = order_article.price
+ %i[units_to_order units_billed units_received].map do |unit|
+ next unless n = order_article.send(unit)
+
+ converted_quantity = price.convert_quantity(n, price.supplier_order_unit,
+ price.billing_unit.presence || price.supplier_order_unit)
+ line = converted_quantity.round(3).to_s + ' '
+ line += pkg_helper(price, options) + ' ' unless n == 0
+ line += OrderArticle.human_attribute_name("#{unit}_short", count: n)
+ units_info << line
end
units_info.join(', ').html_safe
end
end
+ def ordered_quantities_different_from_group_orders?(order_article, ordered_mark = '!', billed_mark = '?',
+ received_mark = '?')
+ price = order_article.price
+ group_orders_sum_quantity = order_article.group_orders_sum[:quantity]
+ if !order_article.units_received.nil?
+ if price.convert_quantity(order_article.units_received, price.supplier_order_unit,
+ price.group_order_unit).round(3) == group_orders_sum_quantity
+ false
+ else
+ received_mark
+ end
+ elsif !order_article.units_billed.nil?
+ order_article.units_billed == group_orders_sum_quantity ? false : billed_mark
+ elsif !order_article.units_to_order.nil?
+ if price.convert_quantity(order_article.units_to_order, price.supplier_order_unit,
+ price.group_order_unit).round(3) == group_orders_sum_quantity
+ false
+ else
+ ordered_mark
+ end
+ end
+ end
+
# @param article [Article]
# @option options [String] :icon +false+ to hide the icon
# @option options [String] :plain +true+ to not use HTML (implies +icon+=+false+)
@@ -48,9 +74,15 @@ def units_history_line(order_article, options = {})
# Sensible in tables with multiple columns.
# @return [String] Text showing unit and unit quantity when applicable.
def pkg_helper(article, options = {})
- return '' if !article || article.unit_quantity == 1
+ if options[:unit].nil? || article.supplier_order_unit == options[:unit]
+ first_ratio = article&.article_unit_ratios&.first
+ return '' if first_ratio.nil? || first_ratio.quantity == 1
- uq_text = "× #{article.unit_quantity}"
+ uq_text = "× #{first_ratio.quantity} #{ArticleUnitsLib.units[first_ratio.unit][:symbol]}"
+ else
+ unit = ArticleUnitsLib.units[options[:unit]]
+ uq_text = unit[:symbol] || unit[:name]
+ end
uq_text = content_tag(:span, uq_text, class: 'hidden-phone') if options[:soft_uq]
if options[:plain]
uq_text
@@ -67,60 +99,77 @@ def pkg_helper(article, options = {})
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
- def article_price_change_hint(order_article, gross = false)
- return nil if order_article.article.price == order_article.price.price
+ def article_version_change_hint(order_article, gross = false)
+ return nil if order_article.article_version.price == order_article.article_version.price
- title = "#{t('helpers.orders.old_price')}: #{number_to_currency order_article.article.price}"
- title += " / #{number_to_currency order_article.article.gross_price}" if gross
+ title = "#{t('helpers.orders.old_price')}: #{number_to_currency order_article.article_version.price}"
+ title += " / #{number_to_currency order_article.article_version.gross_price}" if gross
content_tag(:i, nil, class: 'icon-asterisk', title: j(title)).html_safe
end
def receive_input_field(form)
order_article = form.object
- units_expected = (order_article.units_billed || order_article.units_to_order) *
- 1.0 * order_article.article.unit_quantity / order_article.article_price.unit_quantity
+ price = order_article.article_version
+ quantity = order_article.units_billed || order_article.units_to_order
+ # units_expected = (order_article.units_billed || order_article.units_to_order) *
+ # 1.0 * order_article.article_version.unit_quantity / order_article.article_version.unit_quantity
+ units_expected = price.convert_quantity(quantity, price.supplier_order_unit, price.billing_unit)
input_classes = 'input input-nano units_received'
- input_classes += ' package' unless order_article.article_price.unit_quantity == 1
+ input_classes += ' package' unless price.unit_quantity == 1 || price.supplier_order_unit != price.billing_unit
+ data = { units_expected: units_expected, billing_unit: price.billing_unit }
+ data.merge!(ratio_quantity_data(order_article, price.billing_unit))
input_html = form.text_field :units_received, class: input_classes,
- data: { 'units-expected' => units_expected },
+ data: data,
disabled: order_article.result_manually_changed?,
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
end
+ def ratio_quantity_data(order_article, default_unit = nil)
+ data = {}
+ data['supplier-order-unit'] = order_article.article_version.supplier_order_unit
+ data['default-unit'] = default_unit
+ data['custom-unit'] = order_article.article_version.unit
+ order_article.article_version.article_unit_ratios.all.each_with_index do |ratio, index|
+ data["ratio-quantity-#{index}"] = ratio.quantity
+ data["ratio-unit-#{index}"] = ratio.unit
+ end
+
+ data
+ end
+
# @param order [Order]
# @return [String] Number of ordergroups participating in order with groups in title.
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 +201,8 @@ def receive_button(order, options = {})
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 @@ def link_to_stock_change_reason(stock_change)
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/suppliers_helper.rb b/app/helpers/suppliers_helper.rb
index 507d2035..c4ddec9b 100644
--- a/app/helpers/suppliers_helper.rb
+++ b/app/helpers/suppliers_helper.rb
@@ -1,10 +1,8 @@
module SuppliersHelper
- def associated_supplier_names(shared_supplier)
- "(#{shared_supplier.suppliers.map(&:name).join(', ')})"
- end
-
- def shared_sync_method_collection(shared_supplier)
- shared_supplier.shared_sync_methods.map do |m|
+ def shared_sync_method_collection
+ # TODO: See if we really need the import limit `shared_supplier_article_sync_limit`
+ # also see https://github.com/foodcoops/foodsoft/pull/609/files
+ Supplier.shared_sync_methods.keys.map do |m|
[t("suppliers.shared_supplier_methods.#{m}"), m]
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..12d6fe4f 100644
--- a/app/inputs/delta_input.rb
+++ b/app/inputs/delta_input.rb
@@ -6,12 +6,12 @@ def input(wrapper_options)
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) +
+ delta_button(content_tag(:i, nil, class: 'icon icon-plus'), 1, options) +
@builder.text_field(attribute_name, options) +
- delta_button(content_tag(:i, nil, class: 'icon icon-plus'), 1, options)
+ delta_button(content_tag(:i, nil, class: 'icon icon-minus'), -1, options)
end
end
# template.button_tag('−', type: :submit, data: {decrement: options[:id]}, tabindex: -1, class: 'btn') +
@@ -21,6 +21,7 @@ def input(wrapper_options)
def delta_button(title, direction, options)
data = { (direction > 0 ? 'increment' : 'decrement') => options[:id] }
delta = direction * options[:data][:delta]
- template.button_tag(title, type: :button, name: 'delta', value: delta, data: data, tabindex: -1, class: 'btn')
+ template.button_tag(title, type: :button, name: 'delta', value: delta, data: data, tabindex: -1,
+ class: 'btn modify')
end
end
diff --git a/app/mailers/mailer.rb b/app/mailers/mailer.rb
index 4ff340a0..90c8a062 100644
--- a/app/mailers/mailer.rb
+++ b/app/mailers/mailer.rb
@@ -81,7 +81,7 @@ def order_result_supplier(user, order, options = {})
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 @@ def mail(args)
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 @@ def mail(args)
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 @@ def add_order_result_attachments(order, options = {})
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 f8c129b6..e6e73190 100644
--- a/app/models/article.rb
+++ b/app/models/article.rb
@@ -19,89 +19,97 @@ class Article < ApplicationRecord
# @see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
# @!attribute price
# @return [Number] Net price
- # @see ArticlePrice#price
+ # @see ArticleVersion#price
# @!attribute tax
# @return [Number] VAT percentage (10 is 10%).
- # @see ArticlePrice#tax
+ # @see ArticleVersion#tax
# @!attribute deposit
# @return [Number] Deposit
- # @see ArticlePrice#deposit
+ # @see ArticleVersion#deposit
# @!attribute unit_quantity
# @return [Number] Number of units in wholesale package (box).
- # @see ArticlePrice#unit_quantity
+ # @see ArticleVersion#unit_quantity
# @!attribute order_number
# Order number, this can be used by the supplier to identify articles.
# This is required when using the shared database functionality.
# @return [String] Order number.
# @!attribute article_category
# @return [ArticleCategory] Category this article is in.
- belongs_to :article_category
# @!attribute supplier
# @return [Supplier] Supplier this article belongs to.
belongs_to :supplier
- # @!attribute article_prices
- # @return [Array] Price history (current price first).
- has_many :article_prices, -> { order("created_at DESC") }
- # @!attribute order_articles
- # @return [Array] Order articles for this article.
- has_many :order_articles
+ # @!attribute article_versions
+ # @return [Array] Price history (current price first).
+ has_many :article_versions, -> { order('created_at DESC') }
+
# @!attribute order
# @return [Array] Orders this article appears in.
has_many :orders, through: :order_articles
- # Replace numeric seperator with database format
- localize_input_of :price, :tax, :deposit
- # Get rid of unwanted whitespace. {Unit#new} may even bork on whitespace.
- normalize_attributes :name, :unit, :note, :manufacturer, :origin, :order_number
+ has_one :latest_article_version, lambda {
+ merge(ArticleVersion.latest)
+ }, foreign_key: :article_id, class_name: :ArticleVersion
scope :undeleted, -> { where(deleted_at: nil) }
- scope :available, -> { undeleted.where(availability: true) }
+ scope :available, -> { undeleted.with_latest_versions_and_categories.where(article_versions: { availability: true }) }
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_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
+ scope :with_latest_versions_and_categories, lambda {
+ includes(:latest_article_version)
+ .joins(article_versions: [:article_category])
+ .joins(ArticleVersion.latest_outer_join_sql("#{table_name}.#{primary_key}"))
+ .where(later_article_versions: { id: nil })
+ }
+
+ accepts_nested_attributes_for :latest_article_version
+
+ # TODO-article-version: Discuss if these delegates aren't actually a code smell:
+ begin
+ ArticleVersion.column_names.each do |column_name|
+ next if column_name == ArticleVersion.primary_key
+ next if column_name == 'article_id'
+
+ delegate column_name, "#{column_name}=", to: :latest_article_version, allow_nil: true
+ end
+ rescue StandardError
+ # Ignore if these delegates cannot be created (can happen if table article_versions doesn't yet exist in migrations)
+ end
+
+ delegate :article_category, to: :latest_article_version, allow_nil: true
+ delegate :article_unit_ratios, to: :latest_article_version, allow_nil: true
# Callbacks
- before_save :update_price_history
+ before_save :update_or_create_article_version
before_destroy :check_article_in_use
+ after_save :reload_article_on_version_change
- 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)
+ # TODO-article-version
+ %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)
+ # TODO-article-version
+ %w[article_category supplier order_articles orders]
end
# Returns true if article has been updated at least 2 days ago
def recently_updated
- updated_at > 2.days.ago
+ latest_article_version.updated_at > 2.days.ago
end
# If the article is used in an open Order, the Order will be returned.
def in_open_order
@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_articles.detect { |oa| oa.article_version.article_id == id }
order_article ? order_article.order : nil
end
end
# Returns true if the article has been ordered in the given order at least once
def ordered_in_order?(order)
- order.order_articles.where(article_id: id).where('quantity > 0').one?
+ order.order_articles.includes(:article_version).where(article_version: { article_id: id }).where('quantity > 0').one?
end
# this method checks, if the shared_article has been changed
@@ -111,15 +119,15 @@ def ordered_in_order?(order)
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
@@ -130,81 +138,101 @@ def shared_article_changed?(supplier = self.supplier)
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(
+ ret = ArticleVersion.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: [latest_article_version.name, new_article.name],
+ manufacturer: [latest_article_version.manufacturer, new_article.manufacturer.to_s],
+ origin: [latest_article_version.origin, new_article.origin],
+ unit: [latest_article_version.unit, new_unit],
+ supplier_order_unit: [latest_article_version.supplier_order_unit, new_article.supplier_order_unit],
+ minimum_order_quantity: [latest_article_version.minimum_order_quantity, new_article.minimum_order_quantity],
+ billing_unit: [latest_article_version.billing_unit || latest_article_version.supplier_order_unit,
+ new_article.billing_unit || new_article.supplier_order_unit],
+ group_order_granularity: [latest_article_version.group_order_granularity, new_article.group_order_granularity],
+ group_order_unit: [latest_article_version.group_order_unit, new_article.group_order_unit],
+ price: [latest_article_version.price.to_f.round(2), new_price.to_f.round(2)],
+ tax: [latest_article_version.tax, new_article.tax],
+ deposit: [latest_article_version.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]
+ # :article_unit_ratios_attributes => [self.latest_article_version.article_unit_ratios, new_unit_quantity.article_unit_ratios],
+ note: [latest_article_version.note.to_s, new_article.note.to_s]
}
)
- end
- # Compare attributes from two different articles.
- #
- # This is used for auto-synchronization
- # @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] }]
+ ratios_differ = latest_article_version.article_unit_ratios.length != new_article.article_unit_ratios.length ||
+ latest_article_version.article_unit_ratios.each_with_index.any? do |article_unit_ratio, index|
+ new_article.article_unit_ratios[index].unit != article_unit_ratio.unit ||
+ new_article.article_unit_ratios[index].quantity != article_unit_ratio.quantity
+ end
+
+ if ratios_differ
+ ratio_attribs = new_article.article_unit_ratios.map(&:attributes)
+ ret[:article_unit_ratios_attributes] = ratio_attribs
+ end
+
+ ret
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
# uses unit factors in app_config.yml to calc the price/unit_quantity
- # returns new price and unit_quantity in array, when calc is possible => [price, unit_quanity]
+ # returns new price and unit_quantity in array, when calc is possible => [price, unit_quantity]
# 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 && 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
@@ -217,40 +245,49 @@ def mark_as_deleted
update_column :deleted_at, Time.now
end
+ def current_article_units
+ [supplier_order_unit, group_order_unit, billing_unit, price_unit, article_unit_ratios.map(&:unit)]
+ .flatten
+ .uniq
+ .compact
+ end
+
protected
# 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
+ # Create an ArticleVersion, when the price-attr are changed.
+ def update_or_create_article_version
+ @version_changed_before_save = false
+ return unless version_dup_required?
+
+ old_version = latest_article_version
+ new_version = old_version.duplicate_including_article_unit_ratios
+ article_versions << new_version
+
+ OrderArticle.belonging_to_open_order
+ .joins(:article_version)
+ .where(article_versions: { article_id: id })
+ .update_all(article_version_id: new_version.id)
+
+ # reload old version to avoid updating it too (would automatically happen after before_save):
+ old_version.reload
+
+ @version_changed_before_save = true
end
- def price_changed?
- changed.detect { |attr| attr == 'price' || 'tax' || 'deposit' || 'unit_quantity' } ? true : false
+ def reload_article_on_version_change
+ reload if @version_changed_before_save
+ @version_changed_before_save = false
end
- # We used have the name unique per supplier+deleted_at+type. With the addition of shared_sync_method all,
- # this came in the way, and we now allow duplicate names for the 'all' methods - expecting foodcoops to
- # make their own choice among products with different units by making articles available/unavailable.
- def uniqueness_of_name
- matches = Article.where(name: name, supplier_id: supplier_id, deleted_at: deleted_at, type: type)
- matches = matches.where.not(id: id) unless new_record?
- # 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?
- end
+ def version_dup_required?
+ return false if latest_article_version.nil?
+ return false unless latest_article_version.self_or_ratios_changed?
+
+ OrderArticle.belonging_to_finished_order.exists?(article_version_id: latest_article_version.id)
end
end
diff --git a/app/models/article_BACKUP_56136.rb b/app/models/article_BACKUP_56136.rb
new file mode 100644
index 00000000..41925e99
--- /dev/null
+++ b/app/models/article_BACKUP_56136.rb
@@ -0,0 +1,388 @@
+class Article < ApplicationRecord
+ include LocalizeInput
+ include PriceCalculation
+
+ # @!attribute name
+ # @return [String] Article name
+ # @!attribute unit
+ # @return [String] Unit, e.g. +kg+, +2 L+ or +5 pieces+.
+ # @!attribute note
+ # @return [String] Short line with optional extra article information.
+ # @!attribute availability
+ # @return [Boolean] Whether this article is available within the Foodcoop.
+ # @!attribute manufacturer
+ # @return [String] Original manufacturer.
+ # @!attribute origin
+ # Where the article was produced.
+ # ISO 3166-1 2-letter country code, optionally prefixed with region.
+ # E.g. +NL+ or +Sicily, IT+ or +Berlin, DE+.
+ # @return [String] Production origin.
+ # @see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
+ # @!attribute price
+ # @return [Number] Net price
+ # @see ArticleVersion#price
+ # @!attribute tax
+ # @return [Number] VAT percentage (10 is 10%).
+ # @see ArticleVersion#tax
+ # @!attribute deposit
+ # @return [Number] Deposit
+ # @see ArticleVersion#deposit
+ # @!attribute unit_quantity
+ # @return [Number] Number of units in wholesale package (box).
+ # @see ArticleVersion#unit_quantity
+ # @!attribute order_number
+ # Order number, this can be used by the supplier to identify articles.
+ # This is required when using the shared database functionality.
+ # @return [String] Order number.
+ # @!attribute article_category
+ # @return [ArticleCategory] Category this article is in.
+ # @!attribute supplier
+ # @return [Supplier] Supplier this article belongs to.
+ belongs_to :supplier
+<<<<<<< HEAD
+ # @!attribute article_prices
+ # @return [Array] Price history (current price first).
+ has_many :article_prices, -> { order('created_at DESC') }
+ # @!attribute order_articles
+ # @return [Array] Order articles for this article.
+ has_many :order_articles
+=======
+ # @!attribute article_versions
+ # @return [Array] Price history (current price first).
+ has_many :article_versions, -> { order("created_at DESC") }
+
+>>>>>>> 206a3e51 (Article units squash)
+ # @!attribute order
+ # @return [Array] Orders this article appears in.
+ has_many :orders, through: :order_articles
+
+ has_one :latest_article_version, -> { merge(ArticleVersion.latest) }, foreign_key: :article_id, class_name: :ArticleVersion
+
+ scope :undeleted, -> { where(deleted_at: nil) }
+ scope :available, -> { undeleted.with_latest_versions_and_categories.where(article_versions: { availability: true }) }
+ scope :not_in_stock, -> { where(type: nil) }
+
+<<<<<<< HEAD
+ # Validations
+ 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
+=======
+ scope :with_latest_versions_and_categories, lambda {
+ includes(:latest_article_version)
+ .joins(article_versions: [:article_category])
+ .joins(ArticleVersion.latest_outer_join_sql("#{table_name}.#{primary_key}"))
+ .where(later_article_versions: { id: nil })
+ }
+
+ accepts_nested_attributes_for :latest_article_version
+
+ # TODO-article-version: Discuss if these delegates aren't actually a code smell:
+ begin
+ ArticleVersion.column_names.each do |column_name|
+ next if column_name == ArticleVersion.primary_key
+ next if column_name == 'article_id'
+
+ delegate column_name, "#{column_name}=", to: :latest_article_version, allow_nil: true
+ end
+ rescue
+ # Ignore if these delegates cannot be created (can happen if table article_versions doesn't yet exist in migrations)
+ end
+
+ delegate :article_category, to: :latest_article_version, allow_nil: true
+ delegate :article_unit_ratios, to: :latest_article_version, allow_nil: true
+>>>>>>> 206a3e51 (Article units squash)
+
+ # Callbacks
+ before_save :update_or_create_article_version
+ before_destroy :check_article_in_use
+ after_save :reload_article_on_version_change
+
+<<<<<<< HEAD
+ 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_attributes(auth_object = nil)
+ # TODO-article-version
+ %w(id name supplier_id article_category_id unit note manufacturer origin unit_quantity order_number)
+ end
+
+ def self.ransackable_associations(auth_object = nil)
+ # TODO-article-version
+ %w(article_category supplier order_articles orders)
+>>>>>>> 206a3e51 (Article units squash)
+ end
+
+ # Returns true if article has been updated at least 2 days ago
+ def recently_updated
+ latest_article_version.updated_at > 2.days.ago
+ end
+
+ # If the article is used in an open Order, the Order will be returned.
+ def in_open_order
+ @in_open_order ||= begin
+ order_articles = OrderArticle.where(order_id: Order.open.collect(&:id))
+<<<<<<< HEAD
+ order_article = order_articles.detect { |oa| oa.article_id == id }
+ order_article&.order
+=======
+ order_article = order_articles.detect { |oa| oa.article_version.article_id == id }
+ order_article ? order_article.order : nil
+>>>>>>> 206a3e51 (Article units squash)
+ end
+ end
+
+ # Returns true if the article has been ordered in the given order at least once
+ def ordered_in_order?(order)
+ order.order_articles.includes(:article_version).where(article_version: { article_id: id }).where('quantity > 0').one?
+ end
+
+ # this method checks, if the shared_article has been changed
+ # unequal attributes will returned in array
+ # if only the timestamps differ and the attributes are equal,
+ # false will returned and self.shared_updated_on will be updated
+ def shared_article_changed?(supplier = self.supplier)
+ # skip early if the timestamp hasn't changed
+ shared_article = self.shared_article(supplier)
+ 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
+
+ # Return article attributes that were changed (incl. unit conversion)
+ # @param new_article [Article] New article to update self
+ # @option options [Boolean] :convert_units Omit or set to +true+ to keep current unit and recompute unit quantity and price.
+ # @return [Hash] Attributes with new values
+ 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 = unit
+ else
+ new_price = new_article.price
+ new_unit_quantity = new_article.unit_quantity
+ new_unit = new_article.unit
+ end
+
+<<<<<<< HEAD
+ Article.compare_attributes(
+ {
+ 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: [unit_quantity.to_s.to_f, new_unit_quantity.to_s.to_f],
+ note: [note.to_s, new_article.note.to_s]
+=======
+ ret = ArticleVersion.compare_attributes(
+ {
+ :name => [self.latest_article_version.name, new_article.name],
+ :manufacturer => [self.latest_article_version.manufacturer, new_article.manufacturer.to_s],
+ :origin => [self.latest_article_version.origin, new_article.origin],
+ :unit => [self.latest_article_version.unit, new_unit],
+ :supplier_order_unit => [self.latest_article_version.supplier_order_unit, new_article.supplier_order_unit],
+ :minimum_order_quantity => [self.latest_article_version.minimum_order_quantity, new_article.minimum_order_quantity],
+ :billing_unit => [self.latest_article_version.billing_unit || self.latest_article_version.supplier_order_unit, new_article.billing_unit || new_article.supplier_order_unit],
+ :group_order_granularity => [self.latest_article_version.group_order_granularity, new_article.group_order_granularity],
+ :group_order_unit => [self.latest_article_version.group_order_unit, new_article.group_order_unit],
+ :price => [self.latest_article_version.price.to_f.round(2), new_price.to_f.round(2)],
+ :tax => [self.latest_article_version.tax, new_article.tax],
+ :deposit => [self.latest_article_version.deposit.to_f.round(2), new_article.deposit.to_f.round(2)],
+ # take care of different num-objects.
+ # :article_unit_ratios_attributes => [self.latest_article_version.article_unit_ratios, new_unit_quantity.article_unit_ratios],
+ :note => [self.latest_article_version.note.to_s, new_article.note.to_s]
+>>>>>>> 206a3e51 (Article units squash)
+ }
+ )
+
+<<<<<<< HEAD
+ # Compare attributes from two different articles.
+ #
+ # This is used for auto-synchronization
+ # @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 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
+=======
+ ratios_differ = self.latest_article_version.article_unit_ratios.length != new_article.article_unit_ratios.length ||
+ self.latest_article_version.article_unit_ratios.each_with_index.any? do |article_unit_ratio, index|
+ new_article.article_unit_ratios[index].unit != article_unit_ratio.unit ||
+ new_article.article_unit_ratios[index].quantity != article_unit_ratio.quantity
+ end
+
+ if ratios_differ
+ ratio_attribs = new_article.article_unit_ratios.map(&:attributes)
+ ret[:article_unit_ratios_attributes] = ratio_attribs
+ end
+
+ ret
+>>>>>>> 206a3e51 (Article units squash)
+ end
+
+ # to get the correspondent shared article
+ def shared_article(supplier = self.supplier)
+ 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
+ # uses unit factors in app_config.yml to calc the price/unit_quantity
+ # returns new price and unit_quantity in array, when calc is possible => [price, unit_quantity]
+ # returns false if units aren't foodsoft-compatible
+ # returns nil if units are eqal
+ def convert_units(new_article = shared_article)
+ return unless unit != new_article.unit
+ return false if new_article.unit.include?(',')
+
+ # 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
+ end
+ end
+
+ def deleted?
+ deleted_at.present?
+ end
+
+ def mark_as_deleted
+ check_article_in_use
+ update_column :deleted_at, Time.now
+ end
+
+ def current_article_units
+ [supplier_order_unit, group_order_unit, billing_unit, price_unit, article_unit_ratios.map(&:unit)]
+ .flatten
+ .uniq
+ .compact
+ end
+
+ protected
+
+ # 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: name.to_s) if in_open_order
+ end
+
+<<<<<<< HEAD
+ # Create an ArticlePrice, when the price-attr are changed.
+ def update_price_history
+ return unless price_changed?
+
+ article_prices.build(
+ price: price,
+ tax: tax,
+ deposit: deposit,
+ unit_quantity: unit_quantity
+ )
+=======
+ # Create an ArticleVersion, when the price-attr are changed.
+ def update_or_create_article_version
+ @version_changed_before_save = false
+ return unless self.version_dup_required?
+
+ old_version = self.latest_article_version
+ new_version = old_version.duplicate_including_article_unit_ratios
+ self.article_versions << new_version
+
+ OrderArticle.belonging_to_open_order
+ .joins(:article_version)
+ .where(article_versions: { article_id: self.id })
+ .update_all(article_version_id: new_version.id)
+
+ # reload old version to avoid updating it too (would automatically happen after before_save):
+ old_version.reload
+
+ @version_changed_before_save = true
+>>>>>>> 206a3e51 (Article units squash)
+ end
+
+ def reload_article_on_version_change
+ self.reload if @version_changed_before_save
+ @version_changed_before_save = false
+ end
+
+<<<<<<< HEAD
+ # We used have the name unique per supplier+deleted_at+type. With the addition of shared_sync_method all,
+ # this came in the way, and we now allow duplicate names for the 'all' methods - expecting foodcoops to
+ # make their own choice among products with different units by making articles available/unavailable.
+ def uniqueness_of_name
+ matches = Article.where(name: name, supplier_id: supplier_id, deleted_at: deleted_at, type: type)
+ matches = matches.where.not(id: id) unless new_record?
+ # 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?
+ elsif matches.where(unit: unit, unit_quantity: unit_quantity).any?
+ errors.add :name, :taken_with_unit
+ end
+=======
+ def version_dup_required?
+ return false if latest_article_version.nil?
+ return false unless latest_article_version.self_or_ratios_changed?
+
+ OrderArticle.belonging_to_finished_order.exists?(article_version_id: latest_article_version.id)
+>>>>>>> 206a3e51 (Article units squash)
+ end
+end
diff --git a/app/models/article_BASE_56136.rb b/app/models/article_BASE_56136.rb
new file mode 100644
index 00000000..f8c129b6
--- /dev/null
+++ b/app/models/article_BASE_56136.rb
@@ -0,0 +1,256 @@
+class Article < ApplicationRecord
+ include PriceCalculation
+
+ # @!attribute name
+ # @return [String] Article name
+ # @!attribute unit
+ # @return [String] Unit, e.g. +kg+, +2 L+ or +5 pieces+.
+ # @!attribute note
+ # @return [String] Short line with optional extra article information.
+ # @!attribute availability
+ # @return [Boolean] Whether this article is available within the Foodcoop.
+ # @!attribute manufacturer
+ # @return [String] Original manufacturer.
+ # @!attribute origin
+ # Where the article was produced.
+ # ISO 3166-1 2-letter country code, optionally prefixed with region.
+ # E.g. +NL+ or +Sicily, IT+ or +Berlin, DE+.
+ # @return [String] Production origin.
+ # @see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
+ # @!attribute price
+ # @return [Number] Net price
+ # @see ArticlePrice#price
+ # @!attribute tax
+ # @return [Number] VAT percentage (10 is 10%).
+ # @see ArticlePrice#tax
+ # @!attribute deposit
+ # @return [Number] Deposit
+ # @see ArticlePrice#deposit
+ # @!attribute unit_quantity
+ # @return [Number] Number of units in wholesale package (box).
+ # @see ArticlePrice#unit_quantity
+ # @!attribute order_number
+ # Order number, this can be used by the supplier to identify articles.
+ # This is required when using the shared database functionality.
+ # @return [String] Order number.
+ # @!attribute article_category
+ # @return [ArticleCategory] Category this article is in.
+ belongs_to :article_category
+ # @!attribute supplier
+ # @return [Supplier] Supplier this article belongs to.
+ belongs_to :supplier
+ # @!attribute article_prices
+ # @return [Array] Price history (current price first).
+ has_many :article_prices, -> { order("created_at DESC") }
+ # @!attribute order_articles
+ # @return [Array] Order articles for this article.
+ has_many :order_articles
+ # @!attribute order
+ # @return [Array] Orders this article appears in.
+ has_many :orders, through: :order_articles
+
+ # Replace numeric seperator with database format
+ localize_input_of :price, :tax, :deposit
+ # Get rid of unwanted whitespace. {Unit#new} may even bork on whitespace.
+ normalize_attributes :name, :unit, :note, :manufacturer, :origin, :order_number
+
+ scope :undeleted, -> { where(deleted_at: nil) }
+ scope :available, -> { undeleted.where(availability: true) }
+ 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_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
+
+ # Callbacks
+ 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)
+ end
+
+ 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
+ def recently_updated
+ updated_at > 2.days.ago
+ end
+
+ # If the article is used in an open Order, the Order will be returned.
+ def in_open_order
+ @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
+ end
+ end
+
+ # Returns true if the article has been ordered in the given order at least once
+ def ordered_in_order?(order)
+ order.order_articles.where(article_id: id).where('quantity > 0').one?
+ end
+
+ # this method checks, if the shared_article has been changed
+ # unequal attributes will returned in array
+ # if only the timestamps differ and the attributes are equal,
+ # false will returned and self.shared_updated_on will be updated
+ 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
+ end
+ end
+
+ # Return article attributes that were changed (incl. unit conversion)
+ # @param new_article [Article] New article to update self
+ # @option options [Boolean] :convert_units Omit or set to +true+ to keep current unit and recompute unit quantity and price.
+ # @return [Hash] Attributes with new values
+ 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
+ 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
+
+ return 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)],
+ # 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]
+ }
+ )
+ end
+
+ # Compare attributes from two different articles.
+ #
+ # This is used for auto-synchronization
+ # @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] }]
+ 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
+ end
+
+ # convert units in foodcoop-size
+ # uses unit factors in app_config.yml to calc the price/unit_quantity
+ # returns new price and unit_quantity in array, when calc is possible => [price, unit_quanity]
+ # 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
+ end
+ else
+ nil
+ end
+ end
+
+ def deleted?
+ deleted_at.present?
+ end
+
+ def mark_as_deleted
+ check_article_in_use
+ update_column :deleted_at, Time.now
+ end
+
+ protected
+
+ # 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
+ 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
+ end
+
+ def price_changed?
+ changed.detect { |attr| attr == 'price' || 'tax' || 'deposit' || 'unit_quantity' } ? true : false
+ end
+
+ # We used have the name unique per supplier+deleted_at+type. With the addition of shared_sync_method all,
+ # this came in the way, and we now allow duplicate names for the 'all' methods - expecting foodcoops to
+ # make their own choice among products with different units by making articles available/unavailable.
+ def uniqueness_of_name
+ matches = Article.where(name: name, supplier_id: supplier_id, deleted_at: deleted_at, type: type)
+ matches = matches.where.not(id: id) unless new_record?
+ # 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?
+ end
+ end
+end
diff --git a/app/models/article_LOCAL_56136.rb b/app/models/article_LOCAL_56136.rb
new file mode 100644
index 00000000..289be166
--- /dev/null
+++ b/app/models/article_LOCAL_56136.rb
@@ -0,0 +1,271 @@
+class Article < ApplicationRecord
+ include LocalizeInput
+ include PriceCalculation
+
+ # @!attribute name
+ # @return [String] Article name
+ # @!attribute unit
+ # @return [String] Unit, e.g. +kg+, +2 L+ or +5 pieces+.
+ # @!attribute note
+ # @return [String] Short line with optional extra article information.
+ # @!attribute availability
+ # @return [Boolean] Whether this article is available within the Foodcoop.
+ # @!attribute manufacturer
+ # @return [String] Original manufacturer.
+ # @!attribute origin
+ # Where the article was produced.
+ # ISO 3166-1 2-letter country code, optionally prefixed with region.
+ # E.g. +NL+ or +Sicily, IT+ or +Berlin, DE+.
+ # @return [String] Production origin.
+ # @see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
+ # @!attribute price
+ # @return [Number] Net price
+ # @see ArticlePrice#price
+ # @!attribute tax
+ # @return [Number] VAT percentage (10 is 10%).
+ # @see ArticlePrice#tax
+ # @!attribute deposit
+ # @return [Number] Deposit
+ # @see ArticlePrice#deposit
+ # @!attribute unit_quantity
+ # @return [Number] Number of units in wholesale package (box).
+ # @see ArticlePrice#unit_quantity
+ # @!attribute order_number
+ # Order number, this can be used by the supplier to identify articles.
+ # This is required when using the shared database functionality.
+ # @return [String] Order number.
+ # @!attribute article_category
+ # @return [ArticleCategory] Category this article is in.
+ belongs_to :article_category
+ # @!attribute supplier
+ # @return [Supplier] Supplier this article belongs to.
+ belongs_to :supplier
+ # @!attribute article_prices
+ # @return [Array] Price history (current price first).
+ has_many :article_prices, -> { order('created_at DESC') }
+ # @!attribute order_articles
+ # @return [Array] Order articles for this article.
+ has_many :order_articles
+ # @!attribute order
+ # @return [Array] Orders this article appears in.
+ has_many :orders, through: :order_articles
+
+ # Replace numeric seperator with database format
+ localize_input_of :price, :tax, :deposit
+ # Get rid of unwanted whitespace. {Unit#new} may even bork on whitespace.
+ normalize_attributes :name, :unit, :note, :manufacturer, :origin, :order_number
+
+ scope :undeleted, -> { where(deleted_at: nil) }
+ scope :available, -> { undeleted.where(availability: true) }
+ scope :not_in_stock, -> { where(type: nil) }
+
+ # Validations
+ 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
+
+ # Callbacks
+ 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]
+ end
+
+ 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
+ def recently_updated
+ updated_at > 2.days.ago
+ end
+
+ # If the article is used in an open Order, the Order will be returned.
+ def in_open_order
+ @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
+ end
+ end
+
+ # Returns true if the article has been ordered in the given order at least once
+ def ordered_in_order?(order)
+ order.order_articles.where(article_id: id).where('quantity > 0').one?
+ end
+
+ # this method checks, if the shared_article has been changed
+ # unequal attributes will returned in array
+ # if only the timestamps differ and the attributes are equal,
+ # false will returned and self.shared_updated_on will be updated
+ def shared_article_changed?(supplier = self.supplier)
+ # skip early if the timestamp hasn't changed
+ shared_article = self.shared_article(supplier)
+ 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
+
+ # Return article attributes that were changed (incl. unit conversion)
+ # @param new_article [Article] New article to update self
+ # @option options [Boolean] :convert_units Omit or set to +true+ to keep current unit and recompute unit quantity and price.
+ # @return [Hash] Attributes with new values
+ 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 = unit
+ else
+ new_price = new_article.price
+ new_unit_quantity = new_article.unit_quantity
+ new_unit = new_article.unit
+ end
+
+ Article.compare_attributes(
+ {
+ 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: [unit_quantity.to_s.to_f, new_unit_quantity.to_s.to_f],
+ note: [note.to_s, new_article.note.to_s]
+ }
+ )
+ end
+
+ # Compare attributes from two different articles.
+ #
+ # This is used for auto-synchronization
+ # @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 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)
+ 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
+ # uses unit factors in app_config.yml to calc the price/unit_quantity
+ # returns new price and unit_quantity in array, when calc is possible => [price, unit_quanity]
+ # returns false if units aren't foodsoft-compatible
+ # returns nil if units are eqal
+ def convert_units(new_article = shared_article)
+ return unless unit != new_article.unit
+ return false if new_article.unit.include?(',')
+
+ # 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
+ end
+ end
+
+ def deleted?
+ deleted_at.present?
+ end
+
+ def mark_as_deleted
+ check_article_in_use
+ update_column :deleted_at, Time.now
+ end
+
+ protected
+
+ # 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: name.to_s) if in_open_order
+ end
+
+ # Create an ArticlePrice, when the price-attr are changed.
+ def update_price_history
+ return unless price_changed?
+
+ article_prices.build(
+ price: price,
+ tax: tax,
+ deposit: deposit,
+ unit_quantity: unit_quantity
+ )
+ end
+
+ def price_changed?
+ changed.detect { |attr| attr == 'price' || 'tax' || 'deposit' || 'unit_quantity' } ? true : false
+ end
+
+ # We used have the name unique per supplier+deleted_at+type. With the addition of shared_sync_method all,
+ # this came in the way, and we now allow duplicate names for the 'all' methods - expecting foodcoops to
+ # make their own choice among products with different units by making articles available/unavailable.
+ def uniqueness_of_name
+ matches = Article.where(name: name, supplier_id: supplier_id, deleted_at: deleted_at, type: type)
+ matches = matches.where.not(id: id) unless new_record?
+ # 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?
+ 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_REMOTE_56136.rb b/app/models/article_REMOTE_56136.rb
new file mode 100644
index 00000000..46aeb014
--- /dev/null
+++ b/app/models/article_REMOTE_56136.rb
@@ -0,0 +1,279 @@
+class Article < ApplicationRecord
+ include PriceCalculation
+
+ # @!attribute name
+ # @return [String] Article name
+ # @!attribute unit
+ # @return [String] Unit, e.g. +kg+, +2 L+ or +5 pieces+.
+ # @!attribute note
+ # @return [String] Short line with optional extra article information.
+ # @!attribute availability
+ # @return [Boolean] Whether this article is available within the Foodcoop.
+ # @!attribute manufacturer
+ # @return [String] Original manufacturer.
+ # @!attribute origin
+ # Where the article was produced.
+ # ISO 3166-1 2-letter country code, optionally prefixed with region.
+ # E.g. +NL+ or +Sicily, IT+ or +Berlin, DE+.
+ # @return [String] Production origin.
+ # @see http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
+ # @!attribute price
+ # @return [Number] Net price
+ # @see ArticleVersion#price
+ # @!attribute tax
+ # @return [Number] VAT percentage (10 is 10%).
+ # @see ArticleVersion#tax
+ # @!attribute deposit
+ # @return [Number] Deposit
+ # @see ArticleVersion#deposit
+ # @!attribute unit_quantity
+ # @return [Number] Number of units in wholesale package (box).
+ # @see ArticleVersion#unit_quantity
+ # @!attribute order_number
+ # Order number, this can be used by the supplier to identify articles.
+ # This is required when using the shared database functionality.
+ # @return [String] Order number.
+ # @!attribute article_category
+ # @return [ArticleCategory] Category this article is in.
+ # @!attribute supplier
+ # @return [Supplier] Supplier this article belongs to.
+ belongs_to :supplier
+ # @!attribute article_versions
+ # @return [Array] Price history (current price first).
+ has_many :article_versions, -> { order("created_at DESC") }
+
+ # @!attribute order
+ # @return [Array] Orders this article appears in.
+ has_many :orders, through: :order_articles
+
+ has_one :latest_article_version, -> { merge(ArticleVersion.latest) }, foreign_key: :article_id, class_name: :ArticleVersion
+
+ scope :undeleted, -> { where(deleted_at: nil) }
+ scope :available, -> { undeleted.with_latest_versions_and_categories.where(article_versions: { availability: true }) }
+ scope :not_in_stock, -> { where(type: nil) }
+
+ scope :with_latest_versions_and_categories, lambda {
+ includes(:latest_article_version)
+ .joins(article_versions: [:article_category])
+ .joins(ArticleVersion.latest_outer_join_sql("#{table_name}.#{primary_key}"))
+ .where(later_article_versions: { id: nil })
+ }
+
+ accepts_nested_attributes_for :latest_article_version
+
+ # TODO-article-version: Discuss if these delegates aren't actually a code smell:
+ begin
+ ArticleVersion.column_names.each do |column_name|
+ next if column_name == ArticleVersion.primary_key
+ next if column_name == 'article_id'
+
+ delegate column_name, "#{column_name}=", to: :latest_article_version, allow_nil: true
+ end
+ rescue
+ # Ignore if these delegates cannot be created (can happen if table article_versions doesn't yet exist in migrations)
+ end
+
+ delegate :article_category, to: :latest_article_version, allow_nil: true
+ delegate :article_unit_ratios, to: :latest_article_version, allow_nil: true
+
+ # Callbacks
+ before_save :update_or_create_article_version
+ before_destroy :check_article_in_use
+ after_save :reload_article_on_version_change
+
+ def self.ransackable_attributes(auth_object = nil)
+ # TODO-article-version
+ %w(id name supplier_id article_category_id unit note manufacturer origin unit_quantity order_number)
+ end
+
+ def self.ransackable_associations(auth_object = nil)
+ # TODO-article-version
+ %w(article_category supplier order_articles orders)
+ end
+
+ # Returns true if article has been updated at least 2 days ago
+ def recently_updated
+ latest_article_version.updated_at > 2.days.ago
+ end
+
+ # If the article is used in an open Order, the Order will be returned.
+ def in_open_order
+ @in_open_order ||= begin
+ order_articles = OrderArticle.where(order_id: Order.open.collect(&:id))
+ order_article = order_articles.detect { |oa| oa.article_version.article_id == id }
+ order_article ? order_article.order : nil
+ end
+ end
+
+ # Returns true if the article has been ordered in the given order at least once
+ def ordered_in_order?(order)
+ order.order_articles.includes(:article_version).where(article_version: { article_id: id }).where('quantity > 0').one?
+ end
+
+ # this method checks, if the shared_article has been changed
+ # unequal attributes will returned in array
+ # if only the timestamps differ and the attributes are equal,
+ # false will returned and self.shared_updated_on will be updated
+ 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
+ end
+ end
+
+ # Return article attributes that were changed (incl. unit conversion)
+ # @param new_article [Article] New article to update self
+ # @option options [Boolean] :convert_units Omit or set to +true+ to keep current unit and recompute unit quantity and price.
+ # @return [Hash] Attributes with new values
+ 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
+ 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
+
+ ret = ArticleVersion.compare_attributes(
+ {
+ :name => [self.latest_article_version.name, new_article.name],
+ :manufacturer => [self.latest_article_version.manufacturer, new_article.manufacturer.to_s],
+ :origin => [self.latest_article_version.origin, new_article.origin],
+ :unit => [self.latest_article_version.unit, new_unit],
+ :supplier_order_unit => [self.latest_article_version.supplier_order_unit, new_article.supplier_order_unit],
+ :minimum_order_quantity => [self.latest_article_version.minimum_order_quantity, new_article.minimum_order_quantity],
+ :billing_unit => [self.latest_article_version.billing_unit || self.latest_article_version.supplier_order_unit, new_article.billing_unit || new_article.supplier_order_unit],
+ :group_order_granularity => [self.latest_article_version.group_order_granularity, new_article.group_order_granularity],
+ :group_order_unit => [self.latest_article_version.group_order_unit, new_article.group_order_unit],
+ :price => [self.latest_article_version.price.to_f.round(2), new_price.to_f.round(2)],
+ :tax => [self.latest_article_version.tax, new_article.tax],
+ :deposit => [self.latest_article_version.deposit.to_f.round(2), new_article.deposit.to_f.round(2)],
+ # take care of different num-objects.
+ # :article_unit_ratios_attributes => [self.latest_article_version.article_unit_ratios, new_unit_quantity.article_unit_ratios],
+ :note => [self.latest_article_version.note.to_s, new_article.note.to_s]
+ }
+ )
+
+ ratios_differ = self.latest_article_version.article_unit_ratios.length != new_article.article_unit_ratios.length ||
+ self.latest_article_version.article_unit_ratios.each_with_index.any? do |article_unit_ratio, index|
+ new_article.article_unit_ratios[index].unit != article_unit_ratio.unit ||
+ new_article.article_unit_ratios[index].quantity != article_unit_ratio.quantity
+ end
+
+ if ratios_differ
+ ratio_attribs = new_article.article_unit_ratios.map(&:attributes)
+ ret[:article_unit_ratios_attributes] = ratio_attribs
+ end
+
+ ret
+ 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
+ end
+
+ # convert units in foodcoop-size
+ # uses unit factors in app_config.yml to calc the price/unit_quantity
+ # returns new price and unit_quantity in array, when calc is possible => [price, unit_quantity]
+ # 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
+ end
+ else
+ nil
+ end
+ end
+
+ def deleted?
+ deleted_at.present?
+ end
+
+ def mark_as_deleted
+ check_article_in_use
+ update_column :deleted_at, Time.now
+ end
+
+ def current_article_units
+ [supplier_order_unit, group_order_unit, billing_unit, price_unit, article_unit_ratios.map(&:unit)]
+ .flatten
+ .uniq
+ .compact
+ end
+
+ protected
+
+ # 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
+ end
+
+ # Create an ArticleVersion, when the price-attr are changed.
+ def update_or_create_article_version
+ @version_changed_before_save = false
+ return unless self.version_dup_required?
+
+ old_version = self.latest_article_version
+ new_version = old_version.duplicate_including_article_unit_ratios
+ self.article_versions << new_version
+
+ OrderArticle.belonging_to_open_order
+ .joins(:article_version)
+ .where(article_versions: { article_id: self.id })
+ .update_all(article_version_id: new_version.id)
+
+ # reload old version to avoid updating it too (would automatically happen after before_save):
+ old_version.reload
+
+ @version_changed_before_save = true
+ end
+
+ def reload_article_on_version_change
+ self.reload if @version_changed_before_save
+ @version_changed_before_save = false
+ end
+
+ def version_dup_required?
+ return false if latest_article_version.nil?
+ return false unless latest_article_version.self_or_ratios_changed?
+
+ OrderArticle.belonging_to_finished_order.exists?(article_version_id: latest_article_version.id)
+ 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 @@ def self.find_match(category)
# 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 @@ def self.find_match(category)
# 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
deleted file mode 100644
index cca4c200..00000000
--- a/app/models/article_price.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-class ArticlePrice < ApplicationRecord
- include PriceCalculation
-
- # @!attribute price
- # @return [Number] Net price
- # @see Article#price
- # @!attribute tax
- # @return [Number] VAT percentage
- # @see Article#tax
- # @!attribute deposit
- # @return [Number] Deposit
- # @see Article#deposit
- # @!attribute unit_quantity
- # @return [Number] Number of units in wholesale package (box).
- # @see Article#unit
- # @see Article#unit_quantity
- # @!attribute article
- # @return [Article] Article this price is about.
- belongs_to :article
- # @!attribute order_articles
- # @return [Array] Order articles this price is associated with.
- has_many :order_articles
-
- 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
-end
diff --git a/app/models/article_unit.rb b/app/models/article_unit.rb
new file mode 100644
index 00000000..296fce54
--- /dev/null
+++ b/app/models/article_unit.rb
@@ -0,0 +1,43 @@
+class ArticleUnit < ApplicationRecord
+ self.primary_key = :unit
+
+ before_save { ArticleUnit.clear_cache }
+ before_destroy { ArticleUnit.clear_cache }
+
+ def self.all_cached
+ @all_cached = {} if @all_cached.nil?
+ cached_units_in_locale = @all_cached[I18n.locale]
+ return cached_units_in_locale unless cached_units_in_locale.nil?
+
+ @all_cached[I18n.locale] = all.load
+ end
+
+ def self.clear_cache
+ @all_cached = {}
+ end
+
+ def self.as_hash(config = nil)
+ additional_units = config&.dig(:additional_units) || []
+ available_units = all_cached.map(&:unit)
+ ArticleUnitsLib.units.to_h do |code, unit|
+ [code, unit.merge({ visible: available_units.include?(code) || additional_units.include?(code) })]
+ end
+ end
+
+ def self.as_options(config = nil)
+ additional_units = config&.dig(:additional_units) || []
+ options = {}
+
+ available_units = all_cached.map(&:unit)
+ ArticleUnitsLib.units.each do |code, unit|
+ next unless available_units.include?(code) || additional_units.include?(code)
+
+ label = unit[:name]
+ label += " (#{unit[:symbol]})" if unit[:symbol].present?
+
+ options[label] = code
+ end
+
+ options
+ end
+end
diff --git a/app/models/article_unit_ratio.rb b/app/models/article_unit_ratio.rb
new file mode 100644
index 00000000..be007f98
--- /dev/null
+++ b/app/models/article_unit_ratio.rb
@@ -0,0 +1,6 @@
+class ArticleUnitRatio < ApplicationRecord
+ belongs_to :article_version
+
+ validates :quantity, :sort, :unit, presence: true
+ validates :quantity, numericality: { greater_than: 0 }
+end
diff --git a/app/models/article_version.rb b/app/models/article_version.rb
new file mode 100644
index 00000000..e7401917
--- /dev/null
+++ b/app/models/article_version.rb
@@ -0,0 +1,177 @@
+class ArticleVersion < ApplicationRecord
+ include PriceCalculation
+
+ # @!attribute price
+ # @return [Number] Net price
+ # @see Article#price
+ # @!attribute tax
+ # @return [Number] VAT percentage
+ # @see Article#tax
+ # @!attribute deposit
+ # @return [Number] Deposit
+ # @see Article#deposit
+ # @!attribute unit_quantity
+ # @return [Number] Number of units in wholesale package (box).
+ # @see Article#unit
+ # @see Article#unit_quantity
+ # @!attribute article
+ # @return [Article] Article this price is about.
+ belongs_to :article
+ belongs_to :article_category
+ # @!attribute order_articles
+ # @return [Array] Order articles this price is associated with.
+ has_many :order_articles
+
+ has_many :article_unit_ratios, after_add: :on_article_unit_ratios_change,
+ after_remove: :on_article_unit_ratios_change, dependent: :destroy
+
+ localize_input_of :price, :tax, :deposit
+
+ # Validations
+ validates :name, :price, :tax, :deposit, :article_category, presence: true
+ validates :name, length: { in: 4..60 }
+ validates :unit, length: { in: 1..15, unless: :supplier_order_unit }
+ validates :supplier_order_unit, presence: { unless: :unit }
+ 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 :group_order_granularity, numericality: { greater_than_or_equal_to: 0 }
+ validates :deposit, :tax, numericality: true
+ validates :minimum_order_quantity,
+ numericality: { allow_nil: true, only_integer: false, if: :supplier_order_unit_is_si_convertible }
+ validates :minimum_order_quantity,
+ numericality: { allow_nil: true, only_integer: true, unless: :supplier_order_unit_is_si_convertible }
+ # 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
+ validate :only_one_unit_type
+
+ # Replace numeric seperator with database format
+ localize_input_of :price, :tax, :deposit
+ # Get rid of unwanted whitespace. {Unit#new} may even bork on whitespace.
+ normalize_attributes :name, :unit, :note, :manufacturer, :origin, :order_number
+
+ accepts_nested_attributes_for :article_unit_ratios, allow_destroy: true
+
+ scope :latest, lambda {
+ joins(latest_outer_join_sql("#{table_name}.article_id")).where(later_article_versions: { id: nil })
+ }
+
+ def self.latest_outer_join_sql(article_field_name)
+ %(
+ LEFT OUTER JOIN article_versions later_article_versions
+ ON later_article_versions.article_id = #{article_field_name}
+ AND later_article_versions.created_at > article_versions.created_at
+ )
+ end
+
+ def supplier_order_unit_is_si_convertible
+ ArticleUnitsLib.unit_is_si_convertible(supplier_order_unit)
+ end
+
+ # TODO: Maybe use the nilify blanks gem instead of the following six methods?:
+ def unit=(value)
+ if value.blank?
+ self[:unit] = nil
+ else
+ super
+ end
+ end
+
+ def supplier_order_unit=(value)
+ if value.blank?
+ self[:supplier_order_unit] = nil
+ else
+ super
+ end
+ end
+
+ def group_order_unit=(value)
+ if value.blank?
+ self[:group_order_unit] = nil
+ else
+ super
+ end
+ end
+
+ def price_unit=(value)
+ if value.blank?
+ self[:price_unit] = nil
+ else
+ super
+ end
+ end
+
+ def billing_unit=(value)
+ if value.blank?
+ self[:billing_unit] = nil
+ else
+ super
+ end
+ end
+
+ def minimum_order_quantity=(value)
+ if value.blank?
+ self[:minimum_order_quantity] = nil
+ else
+ super
+ end
+ end
+
+ def self_or_ratios_changed?
+ changed? || @article_unit_ratios_changed || article_unit_ratios.any?(&:changed?)
+ end
+
+ def duplicate_including_article_unit_ratios
+ new_version = dup
+ article_unit_ratios.each do |ratio|
+ ratio = ratio.dup
+ ratio.article_version_id = nil
+ new_version.article_unit_ratios << ratio
+ end
+
+ new_version
+ end
+
+ # Compare attributes from two different articles.
+ #
+ # This is used for auto-synchronization
+ # @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 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
+
+ protected
+
+ # We used have the name unique per supplier+deleted_at+type. With the addition of shared_sync_method all,
+ # this came in the way, and we now allow duplicate names for the 'all' methods - expecting foodcoops to
+ # make their own choice among products with different units by making articles available/unavailable.
+ def uniqueness_of_name
+ matches = Article.includes(latest_article_version: :article_unit_ratios).where(article_versions: { name: name },
+ supplier_id: article.supplier_id, deleted_at: article.deleted_at, type: article.type)
+ matches = matches.where.not(id: article.id) unless article.new_record?
+ # supplier should always be there - except, perhaps, on initialization (on seeding)
+ if article.supplier && (article.supplier.shared_sync_method.blank? || article.supplier.shared_sync_method == 'import')
+ errors.add :name, :taken if matches.any?
+ elsif matches.where(article_versions: { unit: unit, supplier_order_unit: nil,
+ article_unit_ratios: { quantity: unit_quantity, unit: 'XPP' } }).any?
+ errors.add :name, :taken_with_unit
+ end
+ end
+
+ def only_one_unit_type
+ return if unit.blank? || supplier_order_unit.blank?
+
+ errors.add :unit # not specifying a specific error message as this should be prevented by js
+ end
+
+ def on_article_unit_ratios_change(_some_change)
+ @article_unit_ratios_changed = true
+ end
+end
diff --git a/app/models/bank_account.rb b/app/models/bank_account.rb
index de15ee4b..92cefad3 100644
--- a/app/models/bank_account.rb
+++ b/app/models/bank_account.rb
@@ -5,23 +5,21 @@ 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
klass = BankAccountConnector.find iban
- return klass.new self if klass
+ klass.new self if klass
end
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
+ 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 fa1a3da5..66454b32 100644
--- a/app/models/bank_transaction.rb
+++ b/app/models/bank_transaction.rb
@@ -21,8 +21,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) }
@@ -30,13 +30,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
@@ -48,7 +48,7 @@ def assign_to_invoice
update_attribute :financial_link, link
end
- return true
+ true
end
def assign_to_ordergroup
@@ -77,6 +77,6 @@ def assign_to_ordergroup
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/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..b88fb2d8 100644
--- a/app/models/concerns/price_calculation.rb
+++ b/app/models/concerns/price_calculation.rb
@@ -1,6 +1,15 @@
module PriceCalculation
extend ActiveSupport::Concern
+ def unit_quantity
+ first_ratio = article_unit_ratios.first
+ if first_ratio.nil?
+ 1
+ else
+ first_ratio.quantity
+ end
+ end
+
# Gross price = net price + deposit + tax.
# @return [Number] Gross price.
def gross_price
@@ -12,9 +21,42 @@ def fc_price
add_percent(gross_price, FoodsoftConfig[:price_markup])
end
+ # get the unit ratio quantity in reference to the supplier_order_unit
+ def get_unit_ratio_quantity(unit)
+ return 1 if unit == supplier_order_unit
+
+ ratio = article_unit_ratios.find_by_unit(unit)
+ return ratio.quantity unless ratio.nil?
+
+ related_ratio = article_unit_ratios.detect do |current_ratio|
+ ArticleUnit.as_hash[current_ratio.unit][:baseUnit] == ArticleUnit.as_hash[unit][:baseUnit]
+ end
+ return related_ratio.quantity / ArticleUnit.as_hash[unit][:conversionFactor] * ArticleUnit.as_hash[related_ratio.unit][:conversionFactor] unless related_ratio.nil?
+
+ ArticleUnit.as_hash[supplier_order_unit][:conversionFactor] / ArticleUnit.as_hash[unit][:conversionFactor]
+ end
+
+ def convert_quantity(quantity, input_unit, output_unit)
+ quantity / get_unit_ratio_quantity(input_unit) * get_unit_ratio_quantity(output_unit)
+ end
+
+ def group_order_price(value = nil)
+ value ||= price
+ # price is always stored in supplier_order_unit:
+ value / convert_quantity(1, supplier_order_unit, group_order_unit)
+ end
+
+ def gross_group_order_price
+ group_order_price(gross_price)
+ end
+
+ def fc_group_order_price
+ group_order_price(fc_price)
+ 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 @@ def new_stock_changes=(stock_change_attributes)
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 @@ def sum(type = :gross)
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 5c26058b..68882d0d 100644
--- a/app/models/financial_transaction.rb
+++ b/app/models/financial_transaction.rb
@@ -6,14 +6,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) }
@@ -26,12 +28,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 @@ def self.default
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 @@ def non_members
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..b48c9f71 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,47 @@ 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_version.fc_group_order_price,
+ unit: order_article.article_version.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,
+ ratio_group_order_unit_supplier_unit: order_article.article_version.convert_quantity(1,
+ order_article.article_version.supplier_order_unit, order_article.article_version.group_order_unit),
+ quantity_available: (order.stockit? ? order_article.article_version.article.quantity_available : 0),
+ minimum_order_quantity: if order_article.article_version.minimum_order_quantity
+ order_article.article_version.convert_quantity(
+ order_article.article_version.minimum_order_quantity, order_article.article_version.supplier_order_unit, order_article.article_version.group_order_unit
+ )
+ end
}
end
end
@@ -69,12 +76,12 @@ def save_group_order_articles
# 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 })
- group_order_article.update_quantities(quantities[:quantity].to_i, quantities[:tolerance].to_i)
+ quantities = group_order_articles_attributes.fetch(order_article.id.to_s, { quantity: 0, tolerance: 0 })
+ group_order_article.update_quantities(quantities[:quantity].to_f, quantities[:tolerance].to_f)
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 +90,7 @@ def save_group_order_articles
# 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: :article_version).to_a.sum(&:total_price)
update_attribute(:price, total)
end
@@ -97,7 +104,7 @@ def save_ordering!
end
def ordergroup_name
- ordergroup ? ordergroup.name : I18n.t('model.group_order.stock_ordergroup_name', :user => updated_by.try(:name) || '?')
+ ordergroup ? ordergroup.name : I18n.t('model.group_order.stock_ordergroup_name', user: updated_by.try(:name) || '?')
end
def total
diff --git a/app/models/group_order_article.rb b/app/models/group_order_article.rb
index 8df4fa59..fdec8365 100644
--- a/app/models/group_order_article.rb
+++ b/app/models/group_order_article.rb
@@ -6,21 +6,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 }
+ validates :quantity, :tolerance, numericality: { 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
@@ -30,7 +30,7 @@ def ordergroup_id=(id)
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
@@ -43,7 +43,7 @@ def update_quantities(quantity, tolerance)
# 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
@@ -52,24 +52,26 @@ def update_quantities(quantity, tolerance)
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)
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)
logger.debug("Decreasing tolerance by #{delta}")
@@ -79,12 +81,12 @@ def update_quantities(quantity, tolerance)
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
@@ -93,8 +95,10 @@ def update_quantities(quantity, tolerance)
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.quantity} != #{quantity} || #{self.tolerance} != {tolerance})", self
+ )
end
# Remove zero-only items.
@@ -119,14 +123,15 @@ def calculate_result(total = nil)
quantity = tolerance = total_quantity = 0
# Get total
- if not total.nil?
- logger.debug "<#{order_article.article.name}> => #{total} (given)"
- elsif order_article.article.is_a?(StockArticle)
- total = order_article.article.quantity
- logger.debug "<#{order_article.article.name}> (stock) => #{total}"
+ if !total.nil?
+ logger.debug "<#{order_article.article_version.name}> => #{total} (given)"
+ elsif order_article.article_version.is_a?(StockArticle)
+ total = order_article.article_version.quantity
+ logger.debug "<#{order_article.article_version.name}> (stock) => #{total}"
else
- total = order_article.units_to_order * order_article.price.unit_quantity
- logger.debug "<#{order_article.article.name}> units_to_order #{order_article.units_to_order} => #{total}"
+ total = order_article.article_version.convert_quantity(order_article.units_to_order,
+ order_article.article_version.supplier_order_unit, order_article.article_version.group_order_unit)
+ logger.debug "<#{order_article.article_version.name}> units_to_order #{order_article.units_to_order} => #{total}"
end
if total > 0
@@ -143,7 +148,7 @@ def calculate_result(total = nil)
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
@@ -152,11 +157,11 @@ def calculate_result(total = nil)
# 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
@@ -168,7 +173,7 @@ def calculate_result(total = nil)
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
@@ -183,8 +188,8 @@ def result(type = :total)
# 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
@@ -192,14 +197,15 @@ def save_results!(article_total = nil)
# the minimum price depending on configuration. When the order is finished it
# will be the value depending of the article results.
def total_price(order_article = self.order_article)
+ group_order_price = order_article.article_version.fc_group_order_price
if order_article.order.open?
if FoodsoftConfig[:tolerance_is_costly]
- order_article.article.fc_price * (quantity + tolerance)
+ group_order_price * (quantity + tolerance)
else
- order_article.article.fc_price * quantity
+ group_order_price * quantity
end
else
- order_article.price.fc_price * result
+ group_order_price * result
end
end
@@ -211,8 +217,8 @@ def result_manually_changed?
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 8a9dde9f..c379e15e 100644
--- a/app/models/invoice.rb
+++ b/app/models/invoice.rb
@@ -2,13 +2,13 @@ class Invoice < ApplicationRecord
include CustomFields
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) }
@@ -22,18 +22,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
@@ -43,10 +43,10 @@ def net_amount
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)")
+ .joins(order_articles: [:article_version])
+ .sum('COALESCE(order_articles.units_received, order_articles.units_billed, order_articles.units_to_order)' \
+ + '* article_versions.unit_quantity' \
+ + '* ROUND((article_versions.price + article_versions.deposit) * (100 + article_versions.tax) / 100, 2)')
end
def orders_transport_sum
@@ -62,11 +62,11 @@ def expected_amount
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 15e209d1..db17e345 100644
--- a/app/models/order.rb
+++ b/app/models/order.rb
@@ -2,29 +2,30 @@ 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 :article_versions, through: :order_articles
+ has_many :articles, through: :article_versions
+ 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 +50,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?
@@ -69,10 +70,10 @@ def articles_for_ordering
if stockit?
# 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|
+ StockArticle.available.includes(latest_article_version: :article_category)
+ .order('article_categories.name', 'article_versions.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,12 +88,10 @@ def supplier_articles
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 }
+ @article_ids ||= order_articles.map { |oa| oa.article_version.article_id.to_s }
end
# Returns an array of article ids that lead to a validation error.
@@ -101,19 +100,19 @@ def erroneous_article_ids
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 +133,18 @@ def init_dates
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 +155,7 @@ def init_dates
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 +166,11 @@ def self.ordergroup_group_orders_map(ordergroup)
# 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,9 +178,10 @@ def stock_group_order
# 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])
- .order('articles.name')
- .group_by { |a| a.article.article_category.name }
+ .includes([:article_version, :group_order_articles,
+ { article_version: :article_category }])
+ .order('article_versions.name')
+ .group_by { |oa| oa.article_version.article_category.name }
.sort { |a, b| a[0] <=> b[0] }
end
@@ -189,10 +196,10 @@ def articles_sort_by_category
# 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,26 +209,27 @@ def profit(options = {})
# :fc, guess what...
def sum(type = :gross)
total = 0
- if type == :net || type == :gross || type == :fc
- for oa in order_articles.ordered.includes(:article, :article_price)
- quantity = oa.units * oa.price.unit_quantity
+ if %i[net gross fc].include?(type)
+ for oa in order_articles.ordered.includes(:article_version)
+ quantity = oa.units * oa.article_version.convert_quantity(1, oa.article_version.supplier_order_unit,
+ oa.article_version.group_order_unit)
case type
when :net
- total += quantity * oa.price.price
+ total += quantity * oa.article_version.group_order_price
when :gross
- total += quantity * oa.price.gross_price
+ total += quantity * oa.article_version.gross_group_order_price
when :fc
- total += quantity * oa.price.fc_price
+ total += quantity * oa.article_version.fc_group_order_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: :article_version })
for goa in go.group_order_articles
case type
when :groups
- total += goa.result * goa.order_article.price.fc_price
+ total += goa.result * goa.order_article.article_version.fc_group_order_price
when :groups_without_markup
- total += goa.result * goa.order_article.price.gross_price
+ total += goa.result * goa.order_article.article_version.gross_group_order_price
end
end
end
@@ -232,36 +240,29 @@ def sum(type = :gross)
# 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_attributes!(: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).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
+ return if finished?
+
+ Order.transaction do
+ # set new order state (needed by notify_order_finished)
+ update_attributes!(state: 'finished', ends: Time.now, updated_by: user)
+
+ # Update order_articles. Save the current article_version to keep price consistency
+ # Also save results for each group_order_result
+ # Clean up
+ order_articles.each do |oa|
+ oa.group_order_articles.each do |goa|
+ goa.save_results!
end
+ end
- # Update GroupOrder prices
- group_orders.each(&:update_price!)
+ # Update GroupOrder prices
+ group_orders.each(&:update_price!)
- # Stats
- ordergroups.each(&:update_stats!)
+ # Stats
+ ordergroups.each(&:update_stats!)
- # Notifications
- NotifyFinishedOrderJob.perform_later(self)
- end
+ # Notifications
+ NotifyFinishedOrderJob.perform_later(self)
end
end
@@ -275,13 +276,13 @@ def close!(user, transaction_type = nil)
charge_group_orders!(user, transaction_type)
if stockit? # Decreases the quantity of stock_articles
- for oa in order_articles.includes(:article)
+ for oa in order_articles.includes(article_version: :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_version.article, quantity: oa.units_to_order * -1
end
end
- self.update_attributes! :state => 'closed', :updated_by => user, :foodcoop_result => profit
+ update_attributes! state: 'closed', updated_by: user, foodcoop_result: profit
end
end
@@ -289,7 +290,10 @@ def close!(user, transaction_type = nil)
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_attributes! state: 'closed', updated_by: user
end
@@ -313,13 +317,12 @@ def do_end_action!
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 +332,10 @@ def starts_before_ends
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
@@ -337,23 +343,24 @@ def include_articles
end
def keep_ordered_articles
- chosen_order_articles = order_articles.where(article_id: article_ids)
+ chosen_order_articles = order_articles.joins(:article_version).where(article_versions: { 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 { |oa| oa.article_version.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 = article_versions.map(&:article)
+ (articles_list - articles).each { |article| order_articles.create(article_version: article.latest_article_version) }
# 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
+ order_articles.detect { |order_article| order_article.article_version.article_id == article.id }.destroy
end
end
@@ -363,17 +370,17 @@ def distribute_transport
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)
diff --git a/app/models/order_article.rb b/app/models/order_article.rb
index 38f353ab..2c593c8f 100644
--- a/app/models/order_article.rb
+++ b/app/models/order_article.rb
@@ -5,33 +5,36 @@ class OrderArticle < ApplicationRecord
attr_reader :update_global_price
belongs_to :order
- belongs_to :article
- belongs_to :article_price, optional: true
- has_many :group_order_articles, :dependent => :destroy
+ belongs_to :article_version
+ has_many :group_order_articles, dependent: :destroy
- validates_presence_of :order_id, :article_id
- validate :article_and_price_exist
- validates_uniqueness_of :article_id, scope: :order_id
+ validates :order_id, :article_version_id, presence: true
+ validate :article_version_and_price_exist
+ validates :article_version_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")
+ }
+ scope :belonging_to_open_order, -> { joins(:order).merge(Order.open) }
+ scope :belonging_to_finished_order, -> { joins(:order).merge(Order.finished) }
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
+ # This method returns either the ArticleVersion or the Article
# The first will be set, when the the order is finished
def price
- article_price || article
+ article_version || article
end
# latest information on available units
@@ -46,7 +49,7 @@ def units
# 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_group_order_price }
end
# Update quantity/tolerance/units_to_order from group_order_articles
@@ -81,32 +84,26 @@ def update_results!
# 4 | 5 | 4 | 2
#
def calculate_units_to_order(quantity, tolerance = 0)
- unit_size = price.unit_quantity
- units = quantity / unit_size
- remainder = quantity % unit_size
- units += ((remainder > 0) && (remainder + tolerance >= unit_size) ? 1 : 0)
+ return price.minimum_order_quantity if quantity > 0 && !price.minimum_order_quantity.nil? && quantity < price.minimum_order_quantity && quantity + tolerance >= price.minimum_order_quantity
+
+ unit_size = price.convert_quantity(1, price.supplier_order_unit, price.group_order_unit)
+ if price.supplier_order_unit_is_si_convertible
+ quantity / unit_size
+ else
+ units = (quantity / unit_size).floor
+ remainder = quantity % unit_size
+ units += ((remainder > 0) && (remainder + tolerance >= unit_size) ? 1 : 0)
+ end
end
# Calculate price for ordered quantity.
def total_price
- units * price.unit_quantity * price.price
+ units * price.price
end
# Calculate gross price for ordered qunatity.
def total_gross_price
- 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
- end
+ units * price.gross_price
end
# redistribute articles over ordergroups
@@ -124,12 +121,16 @@ def redistribute(quantity, surplus = [:tolerance], update_totals = true)
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
# Recompute
- group_order_articles.each { |goa| goa.save_results! qty_for_members }
+ group_order_articles.each do |goa|
+ group_order_total = article_version.convert_quantity(qty_for_members, article_version.supplier_order_unit,
+ article_version.group_order_unit)
+ goa.save_results!(group_order_total)
+ end
qty_left -= qty_for_members
# if there's anything left, move to stock if wanted
@@ -139,9 +140,7 @@ def redistribute(quantity, surplus = [:tolerance], update_totals = true)
# 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,52 +149,42 @@ def redistribute(quantity, surplus = [:tolerance], update_totals = true)
order.ordergroups.each(&:update_stats!)
end
- # TODO notifications
+ # TODO: notifications
counts
end
# Updates order_article and belongings during balancing process
- def update_article_and_price!(order_article_attributes, article_attributes, price_attributes = nil)
+ def update_handling_versioning!(order_article_attributes, version_attributes)
OrderArticle.transaction do
# Updates self
- self.update_attributes!(order_article_attributes)
-
- # Updates article
- article.update_attributes!(article_attributes)
-
- # Updates article_price belonging to current order article
- if price_attributes.present?
- article_price.attributes = price_attributes
- if article_price.changed?
- # Updates also price attributes of article if update_global_price is selected
- if update_global_price
- article.update_attributes!(price_attributes)
- self.article_price = article.article_prices.first and save # Assign new created article price to order article
- else
- # Creates a new article_price if neccessary
- # Set created_at timestamp to order ends, to make sure the current article price isn't changed
- create_article_price!(price_attributes.merge(article_id: article_id, created_at: order.ends)) and save
- end
-
- # Updates ordergroup values
- update_ordergroup_prices
- end
+ update_attributes!(order_article_attributes)
+
+ # Updates article_version belonging to current order article
+ original_article_version = article_version.duplicate_including_article_unit_ratios
+ article_version.assign_attributes(version_attributes)
+ if article_version.changed?
+ update_or_create_article_version(version_attributes, original_article_version)
+
+ # Updates ordergroup values
+ update_ordergroup_prices
end
end
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.
def missing_units
- _missing_units(price.unit_quantity, quantity, tolerance)
+ unit_ratio = price.convert_quantity(1, price.supplier_order_unit, price.group_order_unit)
+ _missing_units(unit_ratio, quantity, tolerance, price.minimum_order_quantity)
end
def missing_units_was
- _missing_units(price.unit_quantity, quantity_was, tolerance_was)
+ unit_ratio = price.convert_quantity(1, price.supplier_order_unit, price.group_order_unit)
+ _missing_units(unit_ratio, quantity_was, tolerance_was, price.minimum_order_quantity)
end
# Check if the result of any associated GroupOrderArticle was overridden manually
@@ -209,16 +198,48 @@ def difference_received_ordered
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
- errors.add(:article, I18n.t('model.order_article.error_price'))
+ def article_version_and_price_exist
+ if !(article_version = ArticleVersion.find(article_version_id)) || article_version.fc_price.nil?
+ errors.add(:article_version,
+ I18n.t('model.order_article.error_price'))
+ end
+ rescue StandardError
+ errors.add(:article_version, 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
+ return unless order.present? && order.finished?
+
+ self.article_version = article_version.article.article_versions.first
+ end
+
+ def update_or_create_article_version(version_attributes, original_article_version)
+ version_attributes = version_attributes.merge(article_id: article_version.article_id)
+
+ modifying_earlier_version = article_version.article.latest_article_version.id != article_version_id
+ finished_order_article_using_same_version = OrderArticle.belonging_to_finished_order.where(article_version_id: article_version_id).where.not(id: id)
+
+ if (!update_global_price && modifying_earlier_version && !finished_order_article_using_same_version.exists?) ||
+ (update_global_price && !modifying_earlier_version)
+ # update in place:
+ article_version.save
+ else
+ # create new version:
+ original_version_id = article_version.id
+ self.article_version = article_version.duplicate_including_article_unit_ratios
+ article_version.save
+ update_attribute(:article_version_id, article_version.id)
+
+ if update_global_price
+ # update open order articles:
+ OrderArticle.belonging_to_open_order.where(article_version_id: original_version_id).update_all(article_version_id: article_version.id)
+ else
+ # create yet *another* version, wich contains the old data, so new orders will continue using that data:
+ # (The checkbox "Also update the price of future orders" not being checked implies that)
+ original_article_version.created_at = article_version.created_at + 1.second
+ original_article_version.save
+ end
end
end
@@ -241,14 +262,20 @@ def enforce_boxfill
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
- def _missing_units(unit_quantity, quantity, tolerance)
- units = unit_quantity - ((quantity % unit_quantity) + tolerance)
+ def _missing_units(unit_ratio, quantity, tolerance, minimum_order_quantity)
+ return minimum_order_quantity - quantity - tolerance if !minimum_order_quantity.nil? && quantity > 0 && quantity + tolerance < minimum_order_quantity
+
+ return 0 if article_version.supplier_order_unit_is_si_convertible
+
+ units = unit_ratio - ((quantity % unit_ratio) + tolerance)
+
units = 0 if units < 0
- units = 0 if units == unit_quantity
+ units = 0 if units == unit_ratio
units
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 f699755b..88b01bd4 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!
@@ -27,7 +27,7 @@ def contact
end
def non_members
- User.natural_order.all.reject { |u| (users.include?(u) || u.ordergroup) }
+ User.natural_order.all.reject { |u| users.include?(u) || u.ordergroup }
end
def self.include_transaction_class_sum
@@ -51,9 +51,9 @@ def self.custom_fields
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 @@ def financial_transaction_class_balance(klass)
# 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 @@ def update_stats!
# 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 @@ def update_balance!
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 @@ def not_enough_apples?
# 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
@@ -153,17 +164,20 @@ def account_updated
# 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
+ if user.groups.where(type: 'Ordergroup').size > 1
+ errors.add :user_tokens,
+ I18n.t('ordergroups.model.error_single_group', user: user.display)
+ end
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..3415bce9 100644
--- a/app/models/periodic_task_group.rb
+++ b/app/models/periodic_task_group.rb
@@ -5,7 +5,7 @@ def has_next_task?
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 @@ def create_next_task
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
@@ -53,7 +51,7 @@ def update_tasks_including(template_task, prev_due_date)
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
deleted file mode 100644
index 238b48f0..00000000
--- a/app/models/shared_article.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-class SharedArticle < ApplicationRecord
- # connect to database from sharedLists-Application
- SharedArticle.establish_connection(FoodsoftConfig[:shared_lists])
- # set correct table_name in external DB
- self.table_name = 'articles'
-
- 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),
- # convert to db-compatible-string
- :shared_updated_on => updated_on.to_formatted_s(:db)
- )
- end
-end
diff --git a/app/models/shared_supplier.rb b/app/models/shared_supplier.rb
deleted file mode 100644
index 29c9c1ab..00000000
--- a/app/models/shared_supplier.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-class SharedSupplier < ApplicationRecord
- # connect to database from sharedLists-Application
- SharedSupplier.establish_connection(FoodsoftConfig[:shared_lists])
- # set correct table_name in external DB
- self.table_name = 'suppliers'
-
- has_many :suppliers, -> { undeleted }
- has_many :shared_articles, :foreign_key => :supplier_id
-
- def find_article_by_number(order_number)
- # note that `shared_articles` uses number instead order_number
- cached_articles.detect { |a| a.number == order_number }
- end
-
- def cached_articles
- @cached_articles ||= shared_articles.all
- end
-
- # 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)
- 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)
- methods
- end
-end
diff --git a/app/models/stock_article.rb b/app/models/stock_article.rb
index 42a06d49..30c5b079 100644
--- a/app/models/stock_article.rb
+++ b/app/models/stock_article.rb
@@ -1,7 +1,7 @@
class StockArticle < Article
has_many :stock_changes
- scope :available, -> { undeleted.where('quantity > 0') }
+ scope :available, -> { undeleted.with_latest_versions_and_categories.where('quantity > 0') }
validates :quantity, presence: true, numericality: { greater_than_or_equal_to: 0 }
@@ -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
@@ -28,8 +28,8 @@ def quantity_available
end
def quantity_ordered
- OrderArticle.where(article_id: id)
- .joins(:order).where(orders: { state: %w[open finished received] }).sum(:units_to_order)
+ OrderArticle.joins(:order, :article_version).where(article_versions: { article_id: id })
+ .where(orders: { state: %w[open finished received] }).sum(:units_to_order)
end
def quantity_history
@@ -48,7 +48,7 @@ def mark_as_deleted
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..f0c750fa 100644
--- a/app/models/supplier.rb
+++ b/app/models/supplier.rb
@@ -1,33 +1,41 @@
+require 'net/http'
+
class Supplier < ApplicationRecord
include MarkAsDeletedWithName
include CustomFields
- has_many :articles, -> { 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 :articles, lambda {
+ merge(Article.not_in_stock.with_latest_versions_and_categories.order('article_categories.name, article_versions.name'))
+ }
+ has_many :stock_articles, lambda {
+ merge(StockArticle.with_latest_versions_and_categories.order('article_categories.name, article_versions.name'))
+ }
has_many :orders
has_many :deliveries
has_many :invoices
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
- validate :valid_shared_sync_method
+
+ 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 :uniqueness_of_name
+ validates :shared_sync_method, presence: true, unless: -> { supplier_remote_source.blank? }
+ validates :shared_sync_method, absence: true, if: -> { supplier_remote_source.blank? }
+
+ enum shared_sync_method: { all_available: 'all_available', all_unavailable: 'all_unavailable', import: 'import' }
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 +43,9 @@ def self.ransackable_associations(auth_object = nil)
# 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,19 +54,21 @@ def sync_all
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
- article.attributes = unequal_attributes
+ if unequal_attributes.present? # skip if shared_article has not been changed
+ article.latest_article_version.article_unit_ratios.each(&:delete) if unequal_attributes.key?(:article_unit_ratios_attributes)
+ article.latest_article_version.reload
+ article.latest_article_version.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
@@ -64,10 +76,12 @@ def sync_all
.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 }
+ new_articles.each do |new_article|
+ new_article.latest_article_version.availability = false
+ end
end
end
- return [updated_article_pairs, outlisted_articles, new_articles]
+ [updated_article_pairs, outlisted_articles, new_articles]
end
# Synchronise articles with spreadsheet.
@@ -77,46 +91,24 @@ def sync_all
# @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, options = {})
- all_order_numbers = []
- 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]
- 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))
- unless unequal_attributes.empty?
- article.attributes = unequal_attributes
- updated_article_pairs << [article, unequal_attributes]
- end
- end
- elsif status == :outlisted && article.present?
- outlisted_articles << article
+ data = FoodsoftFile.parse(file, options)
+ parse_import_data({ articles: data }, options)
+ end
- # 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
+ def read_from_remote(search_params = {})
+ url = URI(supplier_remote_source)
+ url.query = URI.encode_www_form(search_params) unless search_params.nil?
+ http = Net::HTTP.new(url.host, url.port)
+ http.use_ssl = url.scheme == 'https'
+ request = Net::HTTP::Get.new(url)
- 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]
+ response = http.request(request)
+ JSON.parse(response.body, symbolize_names: true)
end
- # default value
- def shared_sync_method
- return unless shared_supplier
-
- self[:shared_sync_method] || 'import'
+ def sync_from_remote(options = {})
+ data = read_from_remote(options[:search_params])
+ parse_import_data(data, options)
end
def deleted?
@@ -136,22 +128,73 @@ def has_tolerance?
articles.where('articles.unit_quantity > 1').any?
end
- protected
+ # TODO: Maybe use the nilify blanks gem instead of the following two methods?:
+ def supplier_remote_source=(value)
+ if value.blank?
+ self[:supplier_remote_source] = nil
+ else
+ super
+ end
+ end
- # 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
+ def shared_sync_method=(value)
+ if value.blank?
+ self[:shared_sync_method] = nil
+ else
+ super
end
end
+ protected
+
# 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
+ 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
+
+ def parse_import_data(data, options = {})
+ all_order_numbers = []
+ updated_article_pairs = []
+ outlisted_articles = []
+ new_articles = []
+
+ data[:articles].each do |new_attrs|
+ article = articles.includes(:latest_article_version).undeleted.where(article_versions: { order_number: new_attrs[:order_number] }).first
+ new_attrs[:article_category] = ArticleCategory.find_match(new_attrs[:article_category])
+ new_attrs[:tax] ||= FoodsoftConfig[:tax_default]
+ new_attrs[:article_unit_ratios] = new_attrs[:article_unit_ratios].map do |ratio_hash|
+ ArticleUnitRatio.new(ratio_hash)
+ end
+ new_article = articles.build
+ new_article_version = new_article.article_versions.build(new_attrs)
+ new_article.article_versions << new_article_version
+ new_article.latest_article_version = new_article_version
+
+ if new_attrs[:availability]
+ if article.nil?
+ new_articles << new_article
+ else
+ unequal_attributes = article.unequal_attributes(new_article, options.slice(:convert_units))
+ unless unequal_attributes.empty?
+ article.latest_article_version.article_unit_ratios.target.clear unless unequal_attributes[:article_unit_ratios_attributes].nil?
+ article.latest_article_version.attributes = unequal_attributes
+ duped_ratios = article.latest_article_version.article_unit_ratios.map(&:dup)
+ article.latest_article_version.article_unit_ratios.target.clear
+ article.latest_article_version.article_unit_ratios.target.push(*duped_ratios)
+ updated_article_pairs << [article, unequal_attributes]
+ end
+ end
+ elsif article.present?
+ outlisted_articles << article
+ end
+ all_order_numbers << article.order_number if article
end
+ outlisted_articles += articles.includes(:latest_article_version).undeleted.where.not(article_versions: { order_number: all_order_numbers + [nil] }) if options[:outlist_absent]
+ [updated_article_pairs, outlisted_articles, new_articles]
end
end
diff --git a/app/models/task.rb b/app/models/task.rb
index cd748eb3..ead9eb24 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 @@ def self.accepted_tasks_for(user)
# 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 @@ def self.unassigned_tasks_for(user)
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 @@ def self.next_unassigned_tasks_for(user, max = 2)
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,35 +82,31 @@ def still_required_users
# 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)).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)
diff --git a/app/models/user.rb b/app/models/user.rb
index 82d80ed5..7dcba174 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 @@ def ordergroup
# 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 @@ def ordergroup
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 @@ def self.natural_search(q)
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 @@ def locale
end
def name
- [first_name, last_name].join(" ")
+ [first_name, last_name].join(' ')
end
def receive_email?
@@ -120,22 +122,24 @@ def receive_email?
# 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 @@ def ordergroup_name
# 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 @@ def restore
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,6 +250,6 @@ def display
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
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/serializers/article_unit_serializer.rb b/app/serializers/article_unit_serializer.rb
new file mode 100644
index 00000000..9f1dffc7
--- /dev/null
+++ b/app/serializers/article_unit_serializer.rb
@@ -0,0 +1,3 @@
+class ArticleUnitSerializer < ActiveModel::Serializer
+ attributes :id, :unit
+end
diff --git a/app/serializers/order_article_serializer.rb b/app/serializers/order_article_serializer.rb
index 10de058f..033fc577 100644
--- a/app/serializers/order_article_serializer.rb
+++ b/app/serializers/order_article_serializer.rb
@@ -5,6 +5,6 @@ class OrderArticleSerializer < ActiveModel::Serializer
has_one :article
def price
- object.price.fc_price.to_f
+ object.article_version.fc_price.to_f
end
end
diff --git a/app/views/article_units/_create_link.html.haml b/app/views/article_units/_create_link.html.haml
new file mode 100644
index 00000000..078c5d38
--- /dev/null
+++ b/app/views/article_units/_create_link.html.haml
@@ -0,0 +1 @@
+= link_to t('.add'), article_units_path(unit: unit), class: 'btn btn-mini', remote: true, method: :post, data: {'for-unit': unit}
diff --git a/app/views/article_units/_destroy_link.html.haml b/app/views/article_units/_destroy_link.html.haml
new file mode 100644
index 00000000..12e1e4e6
--- /dev/null
+++ b/app/views/article_units/_destroy_link.html.haml
@@ -0,0 +1 @@
+=link_to t('.delete'), article_unit, method: :delete, class: 'btn btn-mini btn-danger', remote: true, data: { confirm: t('.confirm'), 'for-unit': article_unit.unit }
diff --git a/app/views/article_units/_rows.html.haml b/app/views/article_units/_rows.html.haml
new file mode 100644
index 00000000..8b979205
--- /dev/null
+++ b/app/views/article_units/_rows.html.haml
@@ -0,0 +1,16 @@
+- @article_units.each do |article_unit|
+ %tr{class: ("untranslated" if article_unit[:untranslated])}
+ %td= article_unit[:code]
+ %td= highlight(article_unit[:symbol], @query)
+ %td
+ = highlight(article_unit[:name], @query)
+ - if article_unit[:untranslated]
+ %a{title: t('.request_translation'), href: "https://github.com/foodcoops/foodsoft/issues/new?title=Add%20translations%20for%20article%20unit%20%27#{article_unit[:code]}%27", target: '_blank'}
+ %i.icon-mail-forward
+ %td= article_unit[:description]
+ %td
+ - if article_unit[:record].nil?
+ = render partial: 'create_link', locals: {unit: article_unit[:code]}
+ - else
+ = render partial: 'destroy_link', locals: {article_unit: article_unit[:record]}
+
diff --git a/app/views/article_units/create.js.haml b/app/views/article_units/create.js.haml
new file mode 100644
index 00000000..18eccd4e
--- /dev/null
+++ b/app/views/article_units/create.js.haml
@@ -0,0 +1,2 @@
+$('div.container-fluid').prepend('#{j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.success', name: @available_units[@article_unit.unit][:name])}))}')
+$('a[data-for-unit="#{@article_unit.unit}"]').replaceWith('#{j(render(partial: "destroy_link", locals: {article_unit: @article_unit}))}')
diff --git a/app/views/article_units/destroy.js.haml b/app/views/article_units/destroy.js.haml
new file mode 100644
index 00000000..98d446d0
--- /dev/null
+++ b/app/views/article_units/destroy.js.haml
@@ -0,0 +1,2 @@
+$('div.container-fluid').prepend('#{j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.success', name: @available_units[@article_unit.unit][:name])}))}')
+$('a[data-for-unit="#{@article_unit.unit}"]').replaceWith('#{j(render(partial: "create_link", locals: {unit: @article_unit.unit}))}')
diff --git a/app/views/article_units/index.html.haml b/app/views/article_units/index.html.haml
new file mode 100644
index 00000000..474880ed
--- /dev/null
+++ b/app/views/article_units/index.html.haml
@@ -0,0 +1,60 @@
+=form_with url: search_article_units_path, method: :get, id: :article_unit_search_form, html: {'data-remote': true} do |form|
+ .d-flex.align-items-center
+ = form.text_field :q, id: :article_unit_search, placeholder: t('.search'), class: 'm-0'
+ = form.check_box :only_recommended, id: :only_recommended, class: 'm-0 ml-2', checked: true
+ = form.label :only_recommended, t('.only_recommended'), class: 'm-0 ml-1', for: :only_recommended
+
+%table.table.table-striped
+ %thead
+ %tr
+ %th= heading_helper ArticleUnit, :code, short: true
+ %th= heading_helper ArticleUnit, :symbol
+ %th= heading_helper ArticleUnit, :name
+ %th= heading_helper ArticleUnit, :description
+ %th
+ %th
+
+ %tbody#article_units_search_results
+%br
+
+
+- content_for :javascript do
+ :javascript
+ let timer;
+ function debounce(timeout, func){
+ return (...args) => {
+ if (timer !== undefined) {
+ clearTimeout(timer);
+ timer = undefined;
+ }
+ timer = setTimeout(() => {
+ func.apply(this, args);
+ }, timeout);
+ };
+ }
+
+ $(document).ready(function () {
+ $('#article_unit_search, #only_recommended').on('input', debounce(250, () => {
+ $('#article_unit_search_form').submit();
+ }));
+ });
+
+ let currentXhr;
+
+ $(document).on('ajax:send', function(_event, xhr) {
+ if (timer !== undefined) {
+ clearTimeout(timer);
+ timer = undefined;
+ }
+ if (currentXhr) {
+ currentXhr.abort();
+ }
+ currentXhr = xhr;
+ return true;
+ });
+
+ $(document).on('ajax:complete', function(_event, _xhr, _status) {
+ currentXhr = null;
+ });
+
+ $('#article_unit_search_form').submit();
diff --git a/app/views/article_units/search.js.haml b/app/views/article_units/search.js.haml
new file mode 100644
index 00000000..6ef276a7
--- /dev/null
+++ b/app/views/article_units/search.js.haml
@@ -0,0 +1 @@
+$('#article_units_search_results').html('#{j(render("rows"))}');
diff --git a/app/views/articles/_article.html.haml b/app/views/articles/_article.html.haml
index 33d753d7..35bbbb21 100644
--- a/app/views/articles/_article.html.haml
+++ b/app/views/articles/_article.html.haml
@@ -3,9 +3,9 @@
%td{'data-check-this' => "#checkbox_#{article.id}", :class => 'click-me'}= article.name
%td= article.origin
%td= truncate(article.article_category.name, :length => 11) if article.article_category
- %td= article.unit
+ %td= format_group_order_unit_with_ratios(article)
%td= truncate(article.note, :length => 11)
- %td= article.unit_quantity
+ %td= format_supplier_order_unit_with_ratios(article)
%td{:class => "currency"}
%acronym{:title => t('.last_update', last_update: format_date(article.updated_at), gross_price: number_to_currency(article.gross_price))}
= number_to_currency(article.price)
diff --git a/app/views/articles/_edit_all_table.html.haml b/app/views/articles/_edit_all_table.html.haml
index e7752792..58951db7 100644
--- a/app/views/articles/_edit_all_table.html.haml
+++ b/app/views/articles/_edit_all_table.html.haml
@@ -1,11 +1,21 @@
+- content_for :javascript do
+ :javascript
+ const units = #{raw(@all_units.to_json)};
+- content_for :javascript do
+ = simple_fields_for "articles[TEMPLATEREPLACEMARKER]" do |form|
+ :javascript
+ const ratioTemplateHtml = "#{escape_javascript(render(partial: 'shared/article_unit_ratio', locals: {article_unit_ratio: @empty_article_unit_ratio, f: form, article_unit_ratio_counter: -1}))}";
+- price_markup = FoodsoftConfig[:price_markup].to_f
+= render partial: 'shared/js_templates/unit_conversion_popover_template'
%table.table
%thead
%tr
%th= heading_helper Article, :availability, short: true
%th= heading_helper Article, :name
+ - unless @original_units.nil?
+ %th= heading_helper Article, :original_unit
%th= heading_helper Article, :unit
%th= heading_helper Article, :price, short: true
- %th= heading_helper Article, :unit_quantity, short: true
%th= heading_helper Article, :order_number, short: true
%th= heading_helper Article, :note
%th= heading_helper Article, :article_category
@@ -13,18 +23,55 @@
%th= heading_helper Article, :deposit
%tbody
- @articles.each_with_index do |article, index|
- = fields_for "articles[#{article.id || index}]", article do |form|
- %tr
+ %tr{:id => "article_row_#{article.id}"}
+ = simple_fields_for "articles[#{article.id}]", article.latest_article_version do |form|
%td
= yield form # allow to add hidden fields to form
= form.check_box 'availability'
%td= form.text_field 'name', class: 'input-medium'
- %td= form.text_field 'unit', class: 'input-mini'
- %td= form.text_field 'price', class: 'input-mini'
- %td= form.text_field 'unit_quantity', class: 'input-mini'
+ - unless @original_units.nil?
+ %td
+ = "\"#{@original_units[article.id]}\"" unless @original_units[article.id].blank?
+ %td
+ .d-flex.gap-1.align-items-center
+ = form.hidden_field :unit, id: 'article_unit_hidden', value: ''
+ = form.input :supplier_order_unit, as: :select, collection: @article_units, label: false, value: article.supplier_order_unit, include_blank: t('.custom_unit'), input_html: {class: 'input-medium'}
+ = form.input :unit, input_html: {class: 'input-mini ml-1'}, label: false
+ %div.btn-link.toggle-extra-units.text-decoration-none.default-values
+ %i.icon-cog
+ %div.extra-unit-fields.form-horizontal
+ .fold-line
+ .control-group
+ %label.control-label{for: 'unit_ratios'}
+ = "Unit ratios"
+ %table#fc_base_price{:class => "controls"}
+ %tbody
+ - ratios = article.article_unit_ratios
+ = render :partial => 'shared/article_unit_ratio', :as => 'article_unit_ratio', :collection => ratios, locals: {f: form, original_ratios: article&.article_unit_ratios}
+ %tfoot
+ %tr
+ %td{:colspan => 6}
+ = link_to t('.add_ratio'), '#', 'data-add-ratio' => true, class: 'btn', title: t(".add_ratio")
+ .fold-line
+ = form.input :minimum_order_quantity, label: "Mininum order quantity" do
+ .input-append
+ = form.input_field :minimum_order_quantity, class: 'input-mini', title: "total minimum order quantity for this article"
+ .fold-line
+ = form.input :billing_unit, as: :select, collection: [], input_html: {'data-initial-value': article.billing_unit, class: 'input-medium'}, include_blank: false
+ .fold-line
+ = form.input :group_order_granularity, label: "Allow orders per", input_html: {class: 'input-mini', title: "steps in which ordergroups can order this article"}
+ = form.input :group_order_unit, as: :select, collection: [], input_html: {'data-initial-value': article.group_order_unit, class: 'input-medium'}, label: '×'.html_safe, include_blank: false
+ %td
+ .d-flex.gap-1
+ .input-prepend
+ %span.add-on= t 'number.currency.format.unit'
+ = form.text_field 'price', class: 'input-mini', style: 'width: 45px'
+ .input.d-flex.gap-1.control-group
+ %label=t('articles.form.per')
+ = form.select :price_unit, [], {include_blank: false}, {class: 'input-medium'}
%td= form.text_field 'order_number', class: 'input-mini'
%td= form.text_field 'note', class: 'input-medium'
- %td= form.collection_select 'article_category_id', ArticleCategory.all,
+ %td= form.collection_select 'article_category_id', @article_categories,
:id, :name, { :include_blank => true }, class: 'input-small'
%td.input-append
= form.text_field 'tax', class: 'input-mini'
@@ -33,3 +80,11 @@
- unless article.errors.empty?
%tr.alert
%td(colspan="10")= article.errors.full_messages.join(", ")
+- content_for :javascript do
+ - @articles.each_with_index do |article, index|
+ :javascript
+ articleUnitRatioTemplate$ = $($.parseHTML(ratioTemplateHtml.replace(/TEMPLATEREPLACEMARKER/g, '#{article.id}')));
+ new ArticleForm(articleUnitRatioTemplate$, $('#article_row_#{article.id}'), units, #{price_markup}, $('#article_row_#{article.id}').closest('form'), 'articles_#{article.id}', 'articles[#{article.id}]');
+ - if index + 1 === @articles.length
+ :javascript
+ $('input[name="commit"]').removeAttr('disabled');
diff --git a/app/views/articles/_form.html.haml b/app/views/articles/_form.html.haml
index 7cbd64cd..3238683a 100644
--- a/app/views/articles/_form.html.haml
+++ b/app/views/articles/_form.html.haml
@@ -1,21 +1,26 @@
-= simple_form_for [@supplier, @article], :validate => true, :remote => true do |f|
- = f.hidden_field :shared_updated_on
- = f.hidden_field :supplier_id
+- url = @article.new_record? ? supplier_articles_path(@supplier, @article, foodcoop: FoodsoftConfig.scope) : supplier_article_path(@supplier, @article)
+= simple_form_for [@supplier, @article.latest_article_version], :url => url, :validate => true, :remote => true do |f|
+ :javascript
+ const articleUnitRatioTemplate$ = $($.parseHTML("#{escape_javascript(render(partial: 'shared/article_unit_ratio', locals: {article_unit_ratio: @empty_article_unit_ratio, f: f, article_unit_ratio_counter: -1}))}"));
+ const units = #{raw(@all_units.to_json)};
+ new ArticleForm(articleUnitRatioTemplate$, $('.article-form').parents('form'), units, #{FoodsoftConfig[:price_markup].to_f});
+
+ = render partial: 'shared/js_templates/unit_conversion_popover_template'
.modal-header
= close_button :modal
%h3= @article.new_record? ? t('.title_new') : t('.title_edit')
- .modal-body
+ .modal-body.article-form
+ = f.hidden_field :id
= f.input :availability
= f.input :name
- = render partial: 'shared/article_fields_units', locals: {f: f}
+ = render partial: 'shared/article_fields_units', locals: {f: f, article: @article}
+
+ = render partial: 'shared/article_fields_price', locals: {f: f, article: @article}
= f.input :note
= f.association :article_category
/ TODO labels
-
- = render partial: 'shared/article_fields_price', locals: {f: f}
-
= f.input :origin
= f.input :manufacturer
= f.input :order_number
diff --git a/app/views/articles/_import_article_added.js.erb b/app/views/articles/_import_article_added.js.erb
deleted file mode 100644
index 6007213f..00000000
--- a/app/views/articles/_import_article_added.js.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-// update shared article button on create, so that the article will be shown as imported
-// reloading the list doesn't work because it breaks paging :(
-$('#shared_article_<%= article.shared_article.id %> .actions')
- .html('<%= j content_tag(:i, I18n.t('articles.import_search_results.already_imported'), class: 'icon-ok')%>');
diff --git a/app/views/articles/_import_search_results.haml b/app/views/articles/_import_search_results.haml
deleted file mode 100644
index c5dd56f2..00000000
--- a/app/views/articles/_import_search_results.haml
+++ /dev/null
@@ -1,33 +0,0 @@
-- if @articles.empty?
- %p= t '.not_found'
-- else
- = pagination_links_remote @articles
- %table.table.table-striped
- %thead
- %tr
- %th= heading_helper Article, :name
- %th= heading_helper Article, :origin
- %th= heading_helper Article, :manufacturer
- %th= heading_helper Article, :note
- %th{:style => "width:4em"}= heading_helper Article, :price
- %th= heading_helper Article, :unit
- %th
- %tbody
- - for article in @articles
- %tr{id: "shared_article_#{article.id}"}
- %td= highlight article.name, params.fetch(:name_cont_all_joined, '').split(' ')
- %td= article.origin
- %td= article.manufacturer
- %td{title: article.note}= truncate(article.note, length: 11)
- %td= number_to_currency(article.price)
- %td
- = article.unit
- - if article.unit_quantity > 1
- %span{style: 'color: grey'} × #{article.unit_quantity} #{pkg_helper_icon}
- %td.actions
- - logger.debug "[debug] #{article.attributes.inspect}"
- - if @supplier.articles.undeleted.where(order_number: article.number).exists?
- %i.icon-ok= t '.already_imported'
- - else
- = link_to t('.action_import'), import_supplier_articles_path(@supplier, shared_article_id: article.id, direct: true),
- remote: true, class: 'btn btn-small btn-success'
diff --git a/app/views/articles/_sync.html.haml b/app/views/articles/_sync.html.haml
index 05945112..e120e20a 100644
--- a/app/views/articles/_sync.html.haml
+++ b/app/views/articles/_sync.html.haml
@@ -1,11 +1,15 @@
+- content_for :javascript do
+ :javascript
+ const units = #{raw(@all_units.to_json)};
+ let articleUnitRatioTemplate$ = undefined;
- if @outlisted_articles.any?
%h2= t '.outlist.title'
%p
= t('.outlist.body').html_safe
%ul
- - for article in @outlisted_articles
+ - @outlisted_articles.each_with_index do |article, index|
%li
- = hidden_field_tag "outlisted_articles[#{article.id}]", '1'
+ = hidden_field_tag "outlisted_articles[#{index}]", article.id
= article.name
- if article.in_open_order
.alert= t '.outlist.alert_used', article: article.name
diff --git a/app/views/articles/_sync_table.html.haml b/app/views/articles/_sync_table.html.haml
index ac17adfa..0142d636 100644
--- a/app/views/articles/_sync_table.html.haml
+++ b/app/views/articles/_sync_table.html.haml
@@ -1,18 +1,19 @@
-%table.table
+- price_markup = FoodsoftConfig[:price_markup].to_f
+%table.table.sync-table
%thead
%tr
%th= heading_helper Article, :name
%th= heading_helper Article, :note
%th= heading_helper Article, :manufacturer
%th= heading_helper Article, :origin
- %th= heading_helper Article, :unit
- %th= heading_helper Article, :unit_quantity, short: true
+ %th= heading_helper Article, :supplier_order_unit
%th= heading_helper Article, :price
%th= heading_helper Article, :tax
%th= heading_helper Article, :deposit
%th= heading_helper Article, :article_category
%tbody
- - articles.each do |changed_article, attrs|
+ - articles.each_with_index do |data, index|
+ - changed_article, attrs = data
- unless changed_article.new_record?
- article = Article.find(changed_article.id)
%tr{:style => 'color:grey'}
@@ -20,27 +21,75 @@
%td= article.note
%td= article.manufacturer
%td= article.origin
- %td= article.unit
- %td= article.unit_quantity
+ %td= ArticleUnitsLib.get_translated_name_for_code(article.supplier_order_unit)
%td= number_to_currency article.price
%td= number_to_percentage article.tax
%td= number_to_currency article.deposit
%td= article.article_category.name if article.article_category
- %tr
- = fields_for "#{field}[]", changed_article do |form|
+ %tr{:id => "article_row_#{field}_#{index}"}
+ = render partial: 'shared/js_templates/unit_conversion_popover_template'
+ = simple_fields_for "#{field}[#{index}]", changed_article.latest_article_version do |form|
+ - content_for :javascript do
+ :javascript
+ articleUnitRatioTemplate$ = $($.parseHTML("#{escape_javascript(render(partial: 'shared/article_unit_ratio', locals: {article_unit_ratio: @empty_article_unit_ratio, f: form, article_unit_ratio_counter: -1}))}"));
+ new ArticleForm(articleUnitRatioTemplate$, $('#article_row_#{field}_#{index}'), units, #{price_markup}, $('#article_row_#{field}_#{index}').closest('form'), '#{field}_#{index}', '#{field}[#{index}]');
%td{:style => highlight_new(attrs, :name)}
= form.text_field 'name', :size => 0
- - hidden_fields.each do |field|
- = form.hidden_field field
+ = form.input :id, as: :hidden unless changed_article.new_record?
+ -# TODO:
+ -# - hidden_fields.each do |field|
+ -# = form.input field, as: :hidden
%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, :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'
+ %td{:style => highlight_new(attrs, :supplier_order_unit)}
+ .d-flex.gap-1.align-items-center
+ = form.input :supplier_order_unit, as: :select, collection: @article_units, label: false, value: changed_article.supplier_order_unit, include_blank: t('.custom_unit'), input_html: {class: 'input-medium'}
+ = form.input :unit, input_html: {class: 'input-mini ml-1'}, label: false
+ %div.btn-link.toggle-extra-units.text-decoration-none.default-values
+ %i.icon-cog{:style => highlight_new(attrs, [:article_unit_ratio_attributes, :minimum_order_quantity, :billing_unit, :group_order_unit, :group_order_granularity])}
+ %div.extra-unit-fields.form-horizontal
+ .fold-line
+ .control-group
+ %label.control-label{for: 'unit_ratios'}
+ = "Unit ratios"
+ %table#fc_base_price{:class => "controls"}
+ %tbody
+ - ratios = changed_article.article_unit_ratios
+ = render :partial => 'shared/article_unit_ratio', :as => 'article_unit_ratio', :collection => ratios, locals: {f: form, original_ratios: article&.article_unit_ratios}
+ %tfoot
+ %tr
+ %td{:colspan => 6}
+ = link_to t('.add_ratio'), '#', 'data-add-ratio' => true, class: 'btn', title: "add ratio"
+ - unless changed_article.new_record?
+ %tr{style: 'color: grey;'}
+ %td{:colspan => 6}
+ %ul
+ - article.article_unit_ratios.each do |ratio|
+ %li
+ = "#{ratio.quantity} x #{ArticleUnitsLib.get_translated_name_for_code(ratio.unit)}"
+ = t 'per'
+ = ArticleUnitsLib.get_translated_name_for_code(article.supplier_order_unit)
+ .fold-line
+ = form.input :minimum_order_quantity, label: "Mininum order quantity" do
+ .input-append
+ = form.input_field :minimum_order_quantity, class: 'input-mini', style: highlight_new(attrs, :minimum_order_quantity), title: "total minimum order quantity for this article"
+ %span.add-on
+ - unless changed_article.new_record?
+ %p.help-block{style: 'color: grey;'}=article.minimum_order_quantity.to_s
+ .fold-line
+ = form.input :billing_unit, hint: changed_article.new_record? ? nil : ArticleUnitsLib.get_translated_name_for_code(article.billing_unit || article.supplier_order_unit), hint_html: {style: 'color: grey;'}, as: :select, collection: [], input_html: {'data-initial-value': changed_article.billing_unit, class: 'input-medium', style: highlight_new(attrs, :billing_unit)}, include_blank: false
+ .fold-line
+ = form.input :group_order_granularity, hint: changed_article.new_record? ? nil : "#{article.group_order_granularity} x #{ArticleUnitsLib.get_translated_name_for_code(article.group_order_unit)}", hint_html: {style: 'color: grey;'}, label: "Allow orders per", input_html: {class: 'input-mini', style: highlight_new(attrs, :group_order_granularity), title: "steps in which ordergroups can order this article"}
+ = form.input :group_order_unit, as: :select, collection: [], input_html: {'data-initial-value': changed_article.group_order_unit, class: 'input-medium', style: highlight_new(attrs, :group_order_unit)}, label: '×'.html_safe, include_blank: false
%td{:style => highlight_new(attrs, :price)}
- .input-prepend
- %span.add-on= t 'number.currency.format.unit'
- = form.text_field 'price', class: 'input-mini', style: 'width: 45px'
+ .d-flex.gap-1
+ .input-prepend
+ %span.add-on= t 'number.currency.format.unit'
+ = form.text_field 'price', class: 'input-mini', style: 'width: 45px'
+ .input.d-flex.gap-1
+ %label=t('articles.form.per')
+ = form.select :price_unit, [], {include_blank: false}, {'data-initial-value': changed_article.price_unit, class: 'input-medium'}
%td{:style => highlight_new(attrs, :tax)}
.input-append
= form.text_field 'tax', class: 'input-mini', style: 'width: 45px'
diff --git a/app/views/articles/create.js.haml b/app/views/articles/create.js.haml
index 3d8be65f..7fa89fe9 100644
--- a/app/views/articles/create.js.haml
+++ b/app/views/articles/create.js.haml
@@ -1,3 +1,6 @@
$('#modalContainer').modal('hide');
$('#listbody').prepend('#{escape_javascript(render(@article))}');
+const importActionsCell$ = $('#import #search_results td[data-order-number="#{escape_javascript(@article.order_number)}"]');
+importActionsCell$.find('.icon-ok').show();
+importActionsCell$.find('.article_import_btn').hide();
= render 'import_article_added', article: @article if @article.shared_article
diff --git a/app/views/articles/destroy.js.haml b/app/views/articles/destroy.js.haml
index da7c5a62..1bb7588f 100644
--- a/app/views/articles/destroy.js.haml
+++ b/app/views/articles/destroy.js.haml
@@ -2,3 +2,6 @@
$('#article_#{@article.id}').after('#{escape_javascript(render("destroy_active_article"))}');
- else
$('#article_#{@article.id}').remove();
+ const importActionsCell$ = $('#import #search_results td[data-order-number="#{escape_javascript(@article.order_number)}"]');
+ importActionsCell$.find('.icon-ok').hide();
+ importActionsCell$.find('.article_import_btn').show();
diff --git a/app/views/articles/edit.html.haml b/app/views/articles/edit.html.haml
new file mode 100644
index 00000000..fec1326e
--- /dev/null
+++ b/app/views/articles/edit.html.haml
@@ -0,0 +1,2 @@
+
+=render("form")
\ No newline at end of file
diff --git a/app/views/articles/edit_all.html.haml b/app/views/articles/edit_all.html.haml
index ec865295..35db7515 100644
--- a/app/views/articles/edit_all.html.haml
+++ b/app/views/articles/edit_all.html.haml
@@ -3,8 +3,9 @@
%i= t '.note'
= form_tag(update_all_supplier_articles_path(@supplier)) do
= render 'edit_all_table'
+ = hidden_field_tag :complete_migration, true unless @original_units.nil?
%br/
%i= t '.warning'
.form-actions
- = submit_tag t('.submit'), class: 'btn btn-primary'
+ = submit_tag t('.submit'), class: 'btn btn-primary', disabled: true
= link_to t('ui.or_cancel'), supplier_articles_path(@supplier)
diff --git a/app/views/articles/index.html.haml b/app/views/articles/index.html.haml
index fbb549fb..ff1dc903 100644
--- a/app/views/articles/index.html.haml
+++ b/app/views/articles/index.html.haml
@@ -16,29 +16,31 @@
= text_field_tag :query, params[:query], class: 'input-medium search-query',
placeholder: t('.search_placeholder')
- - if @supplier.shared_supplier
+ - unless @supplier.supplier_remote_source.blank?
.btn-group
- if @supplier.shared_sync_method == 'import'
= link_to t('.ext_db.import'), "#import", 'data-toggle-this' => '#import', class: 'btn btn-primary'
= link_to t('.ext_db.sync'), sync_supplier_articles_path(@supplier), method: :post, class: 'btn btn-success'
.btn-group
- = link_to t('.new'), new_supplier_article_path(@supplier), remote: true, class: "btn #{'btn-primary' unless @supplier.shared_supplier}"
+ = link_to t('.migrate_units') + '...', prepare_units_migration_supplier_articles_path(@supplier), class: 'btn btn-success' if @supplier.unit_migration_completed.nil?
+ .btn-group
+ = link_to t('.new'), new_supplier_article_path(@supplier), remote: true, class: "btn #{'btn-primary' if @supplier.supplier_remote_source.nil?}"
= link_to t('.edit_all'), edit_all_supplier_articles_path(@supplier), class: 'btn'
= link_to t('.upload'), upload_supplier_articles_path(@supplier), class: 'btn'
= link_to t('.download'), supplier_articles_path(@supplier, format: :csv), class: 'btn'
- if current_user.role_orders?
= link_to t('.new_order'), new_order_path(supplier_id: @supplier), class: 'btn'
-- unless @supplier.shared_supplier.nil?
+- unless @supplier.supplier_remote_source.blank?
#import.well.well-small(style="display:none;")
- = form_tag shared_supplier_articles_path(@supplier), method: :get, remote: true, class: 'form-search',
+ = form_tag supplier_remote_articles_path(@supplier), method: :get, remote: true, class: 'form-search',
'data-submit-onchange' => true do
%h3{style: 'display: inline; vertical-align: middle; margin-right: 1em;'}= t('.import.title') + ' '
- = text_field_tag "name_cont_all_joined", "", class: 'input-medium search-query',
+ = text_field_tag "name", "", class: 'input-medium search-query',
placeholder: t('.import.placeholder')
%label.checkbox
- = check_box_tag "q[origin_eq]", "REG", false
+ = check_box_tag "origin", "REG", false
= t '.import.restrict_region'
.pull-right{style: 'line-height: 40px'}
= label_tag :article_category_id, t('.import.category')
@@ -64,11 +66,3 @@
$('#import #article_category_id').val('');
}
});
- // propagate category select to article import
- // XXX quite a url hack
- $(document).on('touchclick', '#import .actions a[href]', function() {
- var category_id = $('#import #article_category_id').children(':selected').val();
- var url = $(this).attr('href');
- url = url.replace(/(&article_category_id=[0-9]*)?$/, '&article_category_id='+category_id);
- $(this).attr('href', url);
- });
diff --git a/app/views/articles/migrate_units.html.haml b/app/views/articles/migrate_units.html.haml
new file mode 100644
index 00000000..f146e765
--- /dev/null
+++ b/app/views/articles/migrate_units.html.haml
@@ -0,0 +1,74 @@
+- title t('.title', supplier: @supplier.name)
+%span= simple_format(t('.explanation'))
+- content_for :javascript do
+ :javascript
+ const units = #{raw(@all_units.to_json)};
+- content_for :javascript do
+ = simple_fields_for "samples[TEMPLATEREPLACEMARKER]" do |form|
+ :javascript
+ const ratioTemplateHtml = "#{escape_javascript(render(partial: 'shared/article_unit_ratio', locals: {article_unit_ratio: @empty_article_unit_ratio, f: form, article_unit_ratio_counter: -1}))}";
+ :javascript
+ $(document).ready(() => new MigrateUnitsForm(ratioTemplateHtml, $('#migrate-units'), units));
+- price_markup = FoodsoftConfig[:price_markup].to_f
+= render partial: 'shared/js_templates/unit_conversion_popover_template'
+
+= form_with url: complete_units_migration_supplier_articles_path do |f|
+ %table.table#migrate-units
+ %thead
+ %tr
+ %th= t('.column_headers.apply')
+ %th= t('.column_headers.original_plaintext_unit')
+ %th= t('.column_headers.affected_articles')
+ %th= heading_helper ArticleVersion, :supplier_order_unit
+ %th= t('.column_headers.contains')
+ %th= heading_helper ArticleVersion, :group_order_granularity
+ %tbody
+ - @samples.each_with_index do |sample, index|
+ %tr{:id => "sample_row_#{index}"}
+ = simple_fields_for "samples[#{index}]", sample do |form|
+ %td
+ = yield form # allow to add hidden fields to form
+ = form.input :apply_migration, as: :boolean, label: false, input_html: { value: true, checked: 'checked' }
+ %td
+ - sample[:articles].each do |article|
+ = hidden_field_tag "samples[#{index}][article_ids][]", article.id
+ = "\"#{sample[:unit]}\""
+ %td
+ %span.articles-list
+ %span.expander{role: :button}
+ %i.icon-chevron-sign-right
+ %span.collapser.d-none{role: :button}
+ %i.icon-chevron-sign-down
+ %span
+ = sample[:articles].length
+ %ul.list.d-none
+ - sample[:articles].each do |article|
+ %li
+ - if article.availability
+ = article.name
+ - else
+ %s=article.name
+
+ %td
+ = form.input :supplier_order_unit, as: :select, collection: [], label: false, include_blank: t('.custom_unit'), input_html: {class: 'input-medium', 'data-initial-value': sample[:conversion_result][:supplier_order_unit]}
+ %td
+ .d-flex.gap-1
+ = field_with_preset_value_and_errors(form: form,
+ field: :first_ratio_quantity,
+ value: sample[:conversion_result][:first_ratio]&.dig(:quantity),
+ errors: sample[:errors]&.messages&.dig(:"latest_article_version.article_unit_ratios.quantity"),
+ input_html: { type: :number, class: 'input-mini', required: true })
+ %span
+ = '×'.html_safe
+ = form.input :first_ratio_unit, as: :select, include_blank: true, collection: [], required: false, label: false, input_html: {class: 'input-medium', 'data-initial-value': sample[:conversion_result][:first_ratio]&.dig(:unit)}
+ %td
+ .d-flex.gap-1
+ = field_with_preset_value_and_errors(form: form,
+ field: :group_order_granularity,
+ value: sample[:conversion_result][:group_order_granularity],
+ errors: sample[:errors]&.messages&.dig(:"latest_article_version.group_order_granularity"),
+ input_html: { type: :number, class: 'input-mini', required: true, title: "steps in which ordergroups can order this article" })
+ %span
+ = '×'.html_safe
+ = form.input :group_order_unit, as: :select, collection: [], input_html: {'data-initial-value': sample[:conversion_result][:group_order_unit], class: 'input-medium'}, label: false, include_blank: false
+ = f.submit t('.run_migration'), class: 'btn btn-primary'
diff --git a/app/views/articles/new.js.haml b/app/views/articles/new.js.haml
index 504f5527..1424b96f 100644
--- a/app/views/articles/new.js.haml
+++ b/app/views/articles/new.js.haml
@@ -1,2 +1,2 @@
-$('#modalContainer').html('#{j(render("form"))}');
+$('#modalContainer').addClass('modal-xl').html('#{j(render("form"))}');
$('#modalContainer').modal();
diff --git a/app/views/articles/prepare_units_migration.haml b/app/views/articles/prepare_units_migration.haml
new file mode 100644
index 00000000..5e22ee7b
--- /dev/null
+++ b/app/views/articles/prepare_units_migration.haml
@@ -0,0 +1,8 @@
+- title t('.title', supplier: @supplier.name)
+%p.mb-1
+ =simple_format(t('.explanation'))
+= link_to t('.download'), supplier_articles_path(@supplier, format: :csv), class: 'btn'
+%br/
+%br/
+%p.mt-1
+ = link_to t('.migrate_units') + '...', migrate_units_supplier_articles_path(@supplier), class: 'btn btn-success' if @supplier.unit_migration_completed.nil?
diff --git a/app/views/articles/shared.js.haml b/app/views/articles/shared.js.haml
deleted file mode 100644
index 23816d7e..00000000
--- a/app/views/articles/shared.js.haml
+++ /dev/null
@@ -1 +0,0 @@
-$('#search_results').html('#{escape_javascript(render("import_search_results"))}');
diff --git a/app/views/articles/update.js.haml b/app/views/articles/update.js.haml
index e007307e..71a2ed0b 100644
--- a/app/views/articles/update.js.haml
+++ b/app/views/articles/update.js.haml
@@ -1,2 +1,2 @@
$('#article_#{@article.id}').replaceWith('#{escape_javascript(render(@article))}');
-$('#modalContainer').modal('hide');
+$('#modalContainer').removeClass('modal-xl').modal('hide');
diff --git a/app/views/deliveries/_stock_article_for_adding.html.haml b/app/views/deliveries/_stock_article_for_adding.html.haml
index 5463bc8f..83356365 100644
--- a/app/views/deliveries/_stock_article_for_adding.html.haml
+++ b/app/views/deliveries/_stock_article_for_adding.html.haml
@@ -4,8 +4,8 @@
%tr{:id => "stock_article_#{article.id}", :class => css_class}
%td= article.name
- %td{:data => {:toggle => :tooltip, :title => "#{render(:partial => 'shared/article_price_info', :locals => {:article => article})}"}}= number_to_currency article.price
- %td= article.unit
+ %td{:data => {:toggle => :tooltip, :title => "#{render(:partial => 'shared/article_version_info', :locals => {:article => article})}"}}= number_to_currency article.price
+ %td= format_supplier_order_unit(article)
%td= article.article_category.name
%td
= link_to t('.action_edit'), edit_stock_article_path(article), remote: true, class: 'btn btn-mini'
diff --git a/app/views/deliveries/_stock_change_fields.html.haml b/app/views/deliveries/_stock_change_fields.html.haml
index 40ec5922..65418acc 100644
--- a/app/views/deliveries/_stock_change_fields.html.haml
+++ b/app/views/deliveries/_stock_change_fields.html.haml
@@ -4,7 +4,7 @@
%td
%span.stock_article_name= stock_change.stock_article.name
= f.association :stock_article, :as => :hidden
- %td.price{:data => {:toggle => :tooltip, :title => "#{render(:partial => 'shared/article_price_info', :locals => {:article => stock_article})}"}}= number_to_currency stock_article.price
- %td.unit= stock_change.stock_article.unit
+ %td.price{:data => {:toggle => :tooltip, :title => "#{render(:partial => 'shared/article_version_info', :locals => {:article => stock_article})}"}}= number_to_currency stock_article.price
+ %td.unit= format_supplier_order_unit(stock_change.stock_article)
%td= f.input :quantity, :wrapper => :intable, :input_html => {:class => 'stock-change-quantity', :autocomplete => :off}
%td= stock_change_remove_link f
diff --git a/app/views/deliveries/add_stock_change.js.erb b/app/views/deliveries/add_stock_change.js.erb
index 2d01bc68..611f7c4d 100644
--- a/app/views/deliveries/add_stock_change.js.erb
+++ b/app/views/deliveries/add_stock_change.js.erb
@@ -2,21 +2,21 @@
if(!is_article_available_for_delivery(<%= @stock_change.stock_article.id %>)) {
return false;
}
-
+
$('#stock_changes tr').removeClass('success');
-
- var quantity = w.prompt('<%= j(t('.how_many_units', :unit => @stock_change.stock_article.unit, :name => @stock_change.stock_article.name)) %>');
+
+ var quantity = w.prompt('<%= j(t('.how_many_units', :unit => format_supplier_order_unit(@stock_change.stock_article), :name => @stock_change.stock_article.name)) %>');
if(null === quantity) {
return false;
}
-
+
var stock_change = $(
'<%= j(render(:partial => 'stock_change', :locals => {:stock_change => @stock_change})) %>'
).addClass('success');
$('input.stock-change-quantity', stock_change).val(quantity);
-
+
$('#stock_changes').append(stock_change);
mark_article_for_delivery(<%= @stock_change.stock_article.id %>);
updateSort('#stock_changes');
-
+
})(window);
diff --git a/app/views/finance/balancing/_group_order_articles.html.haml b/app/views/finance/balancing/_group_order_articles.html.haml
index 896cea3c..41f21640 100644
--- a/app/views/finance/balancing/_group_order_articles.html.haml
+++ b/app/views/finance/balancing/_group_order_articles.html.haml
@@ -20,13 +20,14 @@
%td{:style=>"width:50%"}
= group_order_article.group_order.ordergroup_name
%td.center= group_order_article_edit_result(group_order_article)
- %td.numeric= number_to_currency(group_order_article.order_article.price.fc_price * group_order_article.result)
+ %td.numeric= number_to_currency(group_order_article.order_article.article_version.fc_group_order_price * group_order_article.result)
%td.actions{:style=>"width:1em"}
- unless order_article.order.closed?
= link_to t('ui.delete'), group_order_article_path(group_order_article),
method: :delete, remote: true, class: 'btn btn-mini btn-danger'
%td
- - totals[:result] += group_order_article.result
+ - article_version = order_article.article_version
+ - totals[:result] += article_version.convert_quantity(group_order_article.result, article_version.group_order_unit, article_version.billing_unit)
%tfoot
%tr
%td
diff --git a/app/views/finance/balancing/_order_article.html.haml b/app/views/finance/balancing/_order_article.html.haml
index 47db3e31..f7942a7a 100644
--- a/app/views/finance/balancing/_order_article.html.haml
+++ b/app/views/finance/balancing/_order_article.html.haml
@@ -1,24 +1,24 @@
%td.closed.name
- = link_to order_article.article.name, '#', 'data-toggle-this' => "#group_order_articles_#{order_article.id}"
-%td= order_article.article.order_number
-%td{title: units_history_line(order_article, :plain => true)}
- = order_article.units
- = pkg_helper order_article.article_price
- - if s=order_article.ordered_quantities_different_from_group_orders?
+ = link_to order_article.article_version.name, '#', 'data-toggle-this' => "#group_order_articles_#{order_article.id}"
+%td= order_article.article_version.order_number
+%td{title: units_history_line(order_article, plain: true, unit: order_article.article_version.billing_unit)}
+ = order_article.article_version.convert_quantity(order_article.units, order_article.article_version.supplier_order_unit, order_article.article_version.billing_unit).round(3).to_s
+ = pkg_helper(order_article.article_version, unit: order_article.article_version.billing_unit)
+ - if s=ordered_quantities_different_from_group_orders?(order_article)
%span{:style => "color:red;font-weight: bold"}= s
-%td #{order_article.article.unit}
+%td #{format_billing_unit_with_ratios(order_article.article_version)}
%td
- = number_to_currency(order_article.price.price, :unit => "")
+ = number_to_currency(order_article.article_version.price, :unit => "")
:plain
/
= number_to_currency(order_article.total_price, :unit => "")
%td
- = number_to_currency(order_article.price.gross_price, :unit => "")
+ = number_to_currency(order_article.article_version.gross_price, :unit => "")
:plain
/
= number_to_currency(order_article.total_gross_price, :unit => "")
-%td= number_to_percentage(order_article.price.tax) unless order_article.price.tax.zero?
-%td= number_to_currency(order_article.price.deposit, :unit => "") unless order_article.price.deposit.zero?
+%td= number_to_percentage(order_article.article_version.tax) unless order_article.article_version.tax.zero?
+%td= number_to_currency(order_article.article_version.deposit, :unit => "") unless order_article.article_version.deposit.zero?
%td
= link_to t('ui.edit'), edit_order_order_article_path(order_article.order, order_article), remote: true,
class: 'btn btn-mini' unless order_article.order.closed?
diff --git a/app/views/finance/balancing/new.html.haml b/app/views/finance/balancing/new.html.haml
index 7e143e08..5349ddb1 100644
--- a/app/views/finance/balancing/new.html.haml
+++ b/app/views/finance/balancing/new.html.haml
@@ -1,3 +1,4 @@
+= render partial: 'shared/js_templates/unit_conversion_popover_template', locals: {set_units_data: true}
- content_for :javascript do
:javascript
$(function() {
@@ -11,7 +12,7 @@
contentType: 'application/json; charset=UTF-8'
});
});
-
+
$(document).on('OrderArticle#create', function(e) {
$.ajax({
url: '#{new_on_order_article_create_finance_order_path(@order)}',
@@ -20,6 +21,12 @@
contentType: 'application/json; charset=UTF-8'
});
});
+
+ $('*[name="group_order_article[result]"]').each((_, field) => $(field).unitConversionField({
+ units: unitsData,
+ popoverTemplate$: $('#unit_conversion_popover_content_template'),
+ useTargetUnitForStep: false
+ }));
});
= render 'shared/articles_by/common', order: @order
@@ -40,7 +47,7 @@
- unless @order.note.blank?
= simple_format @order.note
- else
- %p= t('.comment_on_transaction')
+ %p= t('.comment_on_transaction')
= link_to t('.edit_note'), edit_note_finance_order_path(@order), remote: true
.well.well-small
diff --git a/app/views/finance/balancing/new.js.haml b/app/views/finance/balancing/new.js.haml
index 0bd073fd..224e225f 100644
--- a/app/views/finance/balancing/new.js.haml
+++ b/app/views/finance/balancing/new.js.haml
@@ -1 +1,6 @@
$('#results').html('#{j(render(balancing_view_partial, order: @order))}');
+$('*[name="group_order_article[result]"]').each((_, field) => $(field).unitConversionField({
+ units: unitsData,
+ popoverTemplate$: $('#unit_conversion_popover_content_template'),
+ useTargetUnitForStep: false
+}));
diff --git a/app/views/finance/balancing/new_on_order_article_create.js.erb b/app/views/finance/balancing/new_on_order_article_create.js.erb
index 5e55a80a..9a88243c 100644
--- a/app/views/finance/balancing/new_on_order_article_create.js.erb
+++ b/app/views/finance/balancing/new_on_order_article_create.js.erb
@@ -2,15 +2,21 @@
// See publish/subscribe design pattern in /doc.
(function(w) {
$('#order_article_<%= @order_article.id %>').remove(); // just to be sure: remove table row which is added below
-
+
$('#ordered-articles tr').removeClass('success');
-
+
var order_article_entry = $(
'<%= j render('finance/balancing/order_article_result', order_article: @order_article) %>'
).addClass('success');
-
+
$('#result_table').prepend(order_article_entry);
-
+
$('#summaryChangedWarning').show();
+
+ $('*[name="group_order_article[result]"]').each((_, field) => $(field).unitConversionField({
+ units: unitsData,
+ popoverTemplate$: $('#unit_conversion_popover_content_template'),
+ useTargetUnitForStep: false
+ }));
})(window);
diff --git a/app/views/finance/balancing/new_on_order_article_update.js.erb b/app/views/finance/balancing/new_on_order_article_update.js.erb
index a3fe6c27..71a4c314 100644
--- a/app/views/finance/balancing/new_on_order_article_update.js.erb
+++ b/app/views/finance/balancing/new_on_order_article_update.js.erb
@@ -4,11 +4,17 @@
$('#order_article_<%= @order_article.id %>').html(
'<%= j render('finance/balancing/order_article', order_article: @order_article) %>'
);
-
+
$('#group_order_articles_<%= @order_article.id %>').html(
'<%= j render('finance/balancing/group_order_articles', order_article: @order_article) %>'
);
-
+
$('#summaryChangedWarning').show();
+
+ $('*[name="group_order_article[result]"]').each((_, field) => $(field).unitConversionField({
+ units: unitsData,
+ popoverTemplate$: $('#unit_conversion_popover_content_template'),
+ useTargetUnitForStep: false
+ }));
})(window);
diff --git a/app/views/group_order_articles/_form.html.haml b/app/views/group_order_articles/_form.html.haml
index e0921d4e..7a7ade62 100644
--- a/app/views/group_order_articles/_form.html.haml
+++ b/app/views/group_order_articles/_form.html.haml
@@ -2,10 +2,10 @@
= form.hidden_field :order_article_id
.modal-header
= close_button :modal
- %h3= t('.amount_change_for', article: @order_article.article.name)
+ %h3= t('.amount_change_for', article: @order_article.article_version.name)
.modal-body
= form.input :ordergroup_id, as: :select, collection: Ordergroup.undeleted.map { |g| [g.name, g.id] }
- = form.input :result, hint: I18n.t('group_order_articles.form.result_hint', unit: @order_article.article.unit) # Why do we need the full prefix?
+ = form.input :result, hint: I18n.t('group_order_articles.form.result_hint', unit: format_group_order_unit_with_ratios(@order_article.article_version)) # Why do we need the full prefix?
.modal-footer
= link_to t('ui.close'), '#', class: 'btn', data: {dismiss: 'modal'}
= form.submit t('ui.save'), class: 'btn btn-primary'
diff --git a/app/views/group_orders/_form.html.haml b/app/views/group_orders/_form.html.haml
index 3ffd583e..d937a8f6 100644
--- a/app/views/group_orders/_form.html.haml
+++ b/app/views/group_orders/_form.html.haml
@@ -1,12 +1,15 @@
+= render partial: 'shared/js_templates/unit_conversion_popover_template', locals: {set_units_data: true}
- content_for :javascript do
- group_balance = FoodsoftConfig[:charge_members_manually] ? @ordering_data[:account_balance] : @ordering_data[:available_funds]
:javascript
+ new GroupOrderForm($('.group_order_form'), {
+ units: unitsData,
+ toleranceIsCostly: #{FoodsoftConfig[:tolerance_is_costly]},
+ groupBalance: #{group_balance},
+ minimumBalance: #{FoodsoftConfig[:minimum_balance] or 0},
+ stockit: #{@order.stockit?}
+ });
$(function() {
- #{data_to_js(@ordering_data)}
- setGroupBalance(#{group_balance});
- setMinimumBalance(#{FoodsoftConfig[:minimum_balance] or 0});
- setToleranceBehaviour(#{FoodsoftConfig[:tolerance_is_costly]});
- setStockit(#{@order.stockit?});
// create List for search-feature (using list.js, http://listjs.com)
var listjsResetPlugin = ['reset', {highlightClass: 'btn-primary'}];
var listjsDelayPlugin = ['delay', {delayedSearchTime: 500}];
@@ -21,7 +24,6 @@
});
- title t('.title'), false
-
.row-fluid
.well.pull-left
= close_button :alert
@@ -64,7 +66,7 @@
%button.add-on.btn.reset-search{:type => :button, :title => t('.reset_article_search')}
%i.icon.icon-remove
-= form_for @group_order do |f|
+= form_for @group_order, html: {class: 'group_order_form'} do |f|
= f.hidden_field :lock_version
= f.hidden_field :order_id
= f.hidden_field :updated_by_user_id
@@ -94,46 +96,55 @@
%i.icon-tag
%td{colspan: "9"}
- order_articles.each do |order_article|
- %tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units])}", valign: "top"}
- %td.name= order_article.article.name
+ %tr{class: "#{cycle('even', 'odd', name: 'articles')} order-article #{get_missing_units_css_class(@ordering_data[:order_articles][order_article.id][:missing_units], order_article.article_version)}", valign: "top"}
+ %td.name= order_article.article_version.name
- if @order.stockit?
- %td= truncate order_article.article.supplier.name, length: 15
- %td= h order_article.article.origin
+ %td= truncate order_article.article_version.article.supplier.name, length: 15
+ %td= h order_article.article_version.origin
%td= number_to_currency(@ordering_data[:order_articles][order_article.id][:price])
- %td= order_article.article.unit
+ %td.group-order-unit= format_group_order_unit_with_ratios(order_article.article_version)
%td
- if @order.stockit?
= @ordering_data[:order_articles][order_article.id][:quantity_available]
- else
- %span{id: "missing_units_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:missing_units]
+ %span.missing-units= @ordering_data[:order_articles][order_article.id][:missing_units]
- %td.quantity
- %input{id: "q_#{order_article.id}", name: "group_order[group_order_articles_attributes][#{order_article.id}][quantity]", type: "hidden", value: @ordering_data[:order_articles][order_article.id][:quantity], 'data-min' => (@ordering_data[:order_articles][order_article.id][:quantity] if @order.boxfill?), 'data-max' => (@ordering_data[:order_articles][order_article.id][:quantity]+@ordering_data[:order_articles][order_article.id][:missing_units] if @order.boxfill?)}/
- %span.used{id: "q_used_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:used_quantity]
+ - quantity_data = ratio_quantity_data(order_article, order_article.article_version.group_order_unit)
+ - quantity_data['ratio-group-order-unit-supplier-unit'] = @ordering_data[:order_articles][order_article.id][:ratio_group_order_unit_supplier_unit]
+ - quantity_data['others_quantity'] = @ordering_data[:order_articles][order_article.id][:others_quantity]
+ - quantity_data['others_tolerance'] = @ordering_data[:order_articles][order_article.id][:others_tolerance]
+ - quantity_data['used_quantity'] = @ordering_data[:order_articles][order_article.id][:used_quantity]
+ - quantity_data['price'] = @ordering_data[:order_articles][order_article.id][:price]
+ - quantity_data['minimum_order_quantity'] = @ordering_data[:order_articles][order_article.id][:minimum_order_quantity] unless @ordering_data[:order_articles][order_article.id][:minimum_order_quantity].nil?
+ %td.quantity.group-order-input
+ %span.used= format_number(@ordering_data[:order_articles][order_article.id][:used_quantity])
+
- %span.unused{id: "q_unused_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:quantity] - @ordering_data[:order_articles][order_article.id][:used_quantity]
- .btn-group
- %a.btn.btn-ordering{'data-increase_quantity' => order_article.id}
+ %span.unused= format_number(@ordering_data[:order_articles][order_article.id][:quantity] - @ordering_data[:order_articles][order_article.id][:used_quantity])
+ .btn-group.numeric-step
+ %a.btn.btn-ordering.increase
%i.icon-plus
- %a.btn.btn-ordering{'data-decrease_quantity' => order_article.id}
+ %input.goa-quantity{type: "number", name: "group_order[group_order_articles_attributes][#{order_article.id}][quantity]", value: @ordering_data[:order_articles][order_article.id][:quantity], data: quantity_data, autocomplete: 'off', class: 'input-mini numeric', min: 0, step: order_article.article_version.group_order_granularity}
+ %a.btn.btn-ordering.decrease
%i.icon-minus
+ %span.numeric-step-error
+ = t('errors.step_error', granularity: order_article.article_version.group_order_granularity, min: 0)
- %td.tolerance{style: ('display:none' if @order.stockit?)}
- %input{id: "t_#{order_article.id}", name: "group_order[group_order_articles_attributes][#{order_article.id}][tolerance]", type: "hidden", value: @ordering_data[:order_articles][order_article.id][:tolerance], 'data-min' => (@ordering_data[:order_articles][order_article.id][:tolerance] if @order.boxfill?)}/
- - if (@ordering_data[:order_articles][order_article.id][:unit] > 1)
- %span.used{id: "t_used_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:used_tolerance]
+ %td.tolerance.group-order-input{style: ('display:none' if @order.stockit?)}
+ - if (requires_tolerance_input?(order_article, @ordering_data))
+ %span.used= format_number(@ordering_data[:order_articles][order_article.id][:used_tolerance])
+
- %span.unused{id: "t_unused_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:tolerance] - @ordering_data[:order_articles][order_article.id][:used_tolerance]
+ %span.unused= format_number(@ordering_data[:order_articles][order_article.id][:tolerance] - @ordering_data[:order_articles][order_article.id][:used_tolerance])
.btn-group
- %a.btn.btn-ordering{'data-increase_tolerance' => order_article.id}
+ %a.btn.btn-ordering.increase
%i.icon-plus
- %a.btn.btn-ordering{'data-decrease_tolerance' => order_article.id}
+ %input.goa-tolerance{type: "number", name: "group_order[group_order_articles_attributes][#{order_article.id}][tolerance]", data: quantity_data, value: @ordering_data[:order_articles][order_article.id][:tolerance], autocomplete: 'off', class: 'input-mini numeric', min: 0, step: order_article.article_version.group_order_granularity, max: [@ordering_data[:order_articles][order_article.id][:ratio_group_order_unit_supplier_unit], @ordering_data[:order_articles][order_article.id][:minimum_order_quantity].presence || 0].max}
+ %a.btn.btn-ordering.decrease
%i.icon-minus
%td{id: "td_price_#{order_article.id}", style: "text-align:right; padding-right:10px; width:4em"}
- %span{id: "price_#{order_article.id}_display"}= number_to_currency(@ordering_data[:order_articles][order_article.id][:total_price])
+ %span{id: "price_#{order_article.id}_display", data: {price: @ordering_data[:order_articles][order_article.id][:total_price]}}= number_to_currency(@ordering_data[:order_articles][order_article.id][:total_price])
.article-info
- .article-name= order_article.article.name
+ .article-name= order_article.article_version.name
.pull-right
= t('.units_full') + ':'
%span{id: "units_#{order_article.id}"}= order_article.units_to_order
@@ -145,11 +156,11 @@
%span{id: "t_total_#{order_article.id}"}= @ordering_data[:order_articles][order_article.id][:tolerance] + @ordering_data[:order_articles][order_article.id][:others_tolerance]
%br/
.pull-left
- #{heading_helper Article, :manufacturer}: #{order_article.article.manufacturer}
+ #{heading_helper Article, :manufacturer}: #{order_article.article_version.manufacturer}
%br/
- #{heading_helper Article, :units}: #{@order.stockit? ? order_article.article.quantity_available : @ordering_data[:order_articles][order_article.id][:unit]} * #{h order_article.article.unit}
+ #{heading_helper Article, :units}: #{@order.stockit? ? order_article.article_version.article.quantity_available : format_supplier_order_unit_with_ratios(order_article.article_version)}
%br/
- #{heading_helper Article, :note}: #{order_article.article.note}
+ #{heading_helper Article, :note}: #{order_article.article_version.note}
%br/
#order-footer
#info-box
diff --git a/app/views/group_orders/show.html.haml b/app/views/group_orders/show.html.haml
index 8c9678d7..47f0ca6a 100644
--- a/app/views/group_orders/show.html.haml
+++ b/app/views/group_orders/show.html.haml
@@ -72,19 +72,19 @@
- r = get_order_results(oa, @group_order.id)
%tr{class: cycle('even', 'odd', name: 'articles') + " " + order_article_class_name(r[:quantity], r[:tolerance], r[:result])}
%td{style: "width:40%"}
- = oa.article.name
- - unless oa.article.note.blank?
+ = oa.article_version.name
+ - unless oa.article_version. note.blank?
= image_tag("lamp_grey.png", {alt: t('.articles.show_note'), size: "15x16", border: "0", onmouseover: "$('#note_#{oa.id}').show();", onmouseout: "$('#note_#{oa.id}').hide();"})
- %td= "#{oa.price.unit_quantity} x #{oa.article.unit}"
- %td= number_to_currency(oa.price.fc_price)
+ %td= format_group_order_unit_with_ratios(oa.article_version)
+ %td= number_to_currency(oa.article_version.fc_group_order_price)
%td
= r[:quantity]
- = "+ #{r[:tolerance]}" if oa.price.unit_quantity > 1
+ = "+ #{r[:tolerance]}" if oa.article_version.unit_quantity > 1
%td= r[:result] > 0 ? r[:result] : "0"
%td= number_to_currency(r[:sub_total])
- - unless oa.article.note.blank?
+ - unless oa.article_version. note.blank?
%tr{id: "note_#{oa.id}", class: "note even", style: "display:none"}
- %td{colspan: "6"}=h oa.article.note
+ %td{colspan: "6"}=h oa.article_version. note
%tr{class: cycle('even', 'odd', name: 'articles')}
%th{colspan: "5"}= heading_helper GroupOrder, :price
%th= number_to_currency(@group_order.price)
diff --git a/app/views/mailer/order_received.text.haml b/app/views/mailer/order_received.text.haml
index 79f01c5d..8ff80c25 100644
--- a/app/views/mailer/order_received.text.haml
+++ b/app/views/mailer/order_received.text.haml
@@ -3,11 +3,11 @@
- unless @abundant_articles.empty?
= raw "===\n" + t('.abundant_articles') + ":"
- @abundant_articles.each do |order_article|
- - article = order_article.article
+ - article = order_article.article_version
= raw t('.article_details', name: article.name, ordered: order_article.units_to_order, received: order_article.units_received, unit: article.unit)
- unless @scarce_articles.empty?
= raw "===\n" + t('.scarce_articles') + ":"
- @scarce_articles.each do |order_article|
- - article = order_article.article
+ - article = order_article.article_version
= raw t('.article_details', name: article.name, ordered: order_article.units_to_order, received: order_article.units_received, unit: article.unit)
diff --git a/app/views/mailer/order_result.text.haml b/app/views/mailer/order_result.text.haml
index 077207d7..3900fa86 100644
--- a/app/views/mailer/order_result.text.haml
+++ b/app/views/mailer/order_result.text.haml
@@ -3,6 +3,6 @@
= raw t '.text1', pickup: I18n.l(@order.pickup)
= raw t '.text2'
- for group_order_article in @group_order.group_order_articles.ordered.includes(:order_article)
- - article = group_order_article.order_article.article
+ - article = group_order_article.order_article.article_version
\- #{article.name}: #{group_order_article.result} x #{article.unit} = #{group_order_article.result * article.fc_price}
= raw t '.text3', sum: @group_order.price, order_url: group_order_url(@group_order), foodcoop: FoodsoftConfig[:name]
diff --git a/app/views/order_articles/_edit.html.haml b/app/views/order_articles/_edit.html.haml
index 047e1391..41e09e7f 100644
--- a/app/views/order_articles/_edit.html.haml
+++ b/app/views/order_articles/_edit.html.haml
@@ -2,28 +2,33 @@
.modal-header
= close_button :modal
%h3= t '.title'
- .modal-body
+ .modal-body.article-form
- if params[:without_units]
= hidden_field_tag :without_units, true
- else
.fold-line
- = form.input :units_to_order, hint: '', input_html: {class: 'input-nano'}
+ = form.input :units_to_order, hint: '', input_html: {class: 'input-mini', step: 0.001}
-#= form.input :units_billed, label: 'invoice', input_html: {class: 'input-nano'}
- = form.input :units_received, input_html: {class: 'input-nano'},
+ = form.input :units_received, input_html: {class: 'input-mini', step: 0.001},
label: t('activerecord.attributes.order_article.units_received_short')
%p.help-block= t 'simple_form.hints.order_article.units_to_order'
.foo{style: 'clear:both'}
- = simple_fields_for :article, @order_article.article do |f|
+ = simple_fields_for @order_article.article_version do |f|
= f.input :name
- - if @order_article.article.is_a?(StockArticle)
+ - if @order_article.article_version.is_a?(StockArticle)
%div.alert= t '.stock_alert'
- else
- = simple_fields_for :article_price, @order_article.article_price do |fprice|
- = render partial: 'shared/article_fields_units', locals: {f_unit: f, f_uq: fprice}
- = render partial: 'shared/article_fields_price', locals: {f: fprice}
+ = simple_fields_for :article_version, @order_article.article_version do |fprice|
+ :javascript
+ const articleUnitRatioTemplate$ = $($.parseHTML("#{escape_javascript(render(partial: 'shared/article_unit_ratio', locals: {article_unit_ratio: @empty_article_unit_ratio, f: fprice, article_unit_ratio_counter: -1}))}"));
+ const units = #{raw(@all_units.to_json)};
+ new ArticleForm(articleUnitRatioTemplate$, $('.article-form').parents('form'), units, #{FoodsoftConfig[:price_markup].to_f});
+ = render partial: 'shared/js_templates/unit_conversion_popover_template'
+ = render partial: 'shared/article_fields_units', locals: {f: fprice, article: @order_article.article_version}
+ = render partial: 'shared/article_fields_price', locals: {f: fprice, article: @order_article.article_version}
= form.input :update_global_price, as: :boolean
= f.input :order_number
diff --git a/app/views/order_articles/edit.js.haml b/app/views/order_articles/edit.js.haml
index 979e34cb..e06d68ae 100644
--- a/app/views/order_articles/edit.js.haml
+++ b/app/views/order_articles/edit.js.haml
@@ -1,2 +1,2 @@
-$('#modalContainer').html('#{j(render("edit"))}');
-$('#modalContainer').modal();
\ No newline at end of file
+$('#modalContainer').addClass('modal-xl').html('#{j(render("edit"))}');
+$('#modalContainer').modal();
diff --git a/app/views/order_articles/new.js.haml b/app/views/order_articles/new.js.haml
index 4a44300f..3168a44d 100644
--- a/app/views/order_articles/new.js.haml
+++ b/app/views/order_articles/new.js.haml
@@ -1,2 +1,2 @@
$('#modalContainer').html('#{j(render("new"))}');
-$('#modalContainer').modal();
\ No newline at end of file
+$('#modalContainer').modal();
diff --git a/app/views/orders/_articles.html.haml b/app/views/orders/_articles.html.haml
index 1c800cc7..97270f3a 100644
--- a/app/views/orders/_articles.html.haml
+++ b/app/views/orders/_articles.html.haml
@@ -18,26 +18,26 @@
%i.icon-tag
%td{:colspan => "9"}
- order_articles.each do |order_article|
- - net_price = order_article.price.price
- - gross_price = order_article.price.gross_price
- - unit_quantity = order_article.price.unit_quantity
+ - article = order_article.price
+ - net_price = article.price
+ - gross_price = article.gross_price
- units = order_article.units
- - total_net += units * unit_quantity * net_price
- - total_gross += units * unit_quantity * gross_price
+ - total_net += units * article.price
+ - total_gross += units * article.gross_price
%tr{:class => cycle('even', 'odd', :name => 'articles') + ' ' + order_article_class(order_article)}
- %td.name=h order_article.article.name
- %td= order_article.article.unit
+ %td.name=h order_article.article_version.name
+ %td= format_group_order_unit_with_ratios(order_article.article_version)
%td= "#{number_to_currency(net_price)} / #{number_to_currency(gross_price)}"
- if order.stockit?
%td= units
- else
- - if unit_quantity > 1 or order_article.tolerance > 0
+ - if order_article.tolerance > 0
%td= "#{order_article.quantity} + #{order_article.tolerance}"
- else
%td= "#{order_article.quantity}"
%td{title: units_history_line(order_article, plain: true)}
= units
- = pkg_helper order_article.price
+ = pkg_helper article
%p
= t '.prices_sum'
= "#{number_to_currency(total_net)} / #{number_to_currency(total_gross)}"
diff --git a/app/views/orders/_edit_amount.html.haml b/app/views/orders/_edit_amount.html.haml
index 22e6c4a4..ad334c02 100644
--- a/app/views/orders/_edit_amount.html.haml
+++ b/app/views/orders/_edit_amount.html.haml
@@ -4,25 +4,25 @@
- cssclass += " unavailable" if (order_article.units_billed||order_article.units_to_order)==0 and order_article.units_received.nil?
%tr{id: "order_article_#{order_article.id}", class: cssclass, valign: "top"}
- order_title = []
- - order_title.append Article.human_attribute_name(:manufacturer)+': ' + order_article.article.manufacturer unless order_article.article.manufacturer.to_s.empty?
- - order_title.append Article.human_attribute_name(:note)+': ' + order_article.article.note unless order_article.article.note.to_s.empty?
- %td= order_article.article.order_number
- %td.name{title: order_title.join("\n")}= order_article.article.name
- %td.unit= order_article.article.unit
- %td.article_price
- = number_to_currency order_article.article_price.price
- = article_price_change_hint(order_article)
+ - order_title.append Article.human_attribute_name(:manufacturer)+': ' + order_article.article_version.manufacturer unless order_article.article_version.manufacturer.to_s.empty?
+ - order_title.append Article.human_attribute_name(:note)+': ' + order_article.article_version.note unless order_article.article_version.note.to_s.empty?
+ %td= order_article.article_version.order_number
+ %td.name{title: order_title.join("\n")}= order_article.article_version.name
+ %td.unit= format_group_order_unit_with_ratios(order_article.article_version)
+ %td.article_version
+ = number_to_currency order_article.article_version.group_order_price
+ = article_version_change_hint(order_article)
%td #{order_article.quantity} + #{order_article.tolerance}
%td
= order_article.units_to_order
- = pkg_helper order_article.article
+ = pkg_helper order_article.price
-#%td # TODO implement invoice screen
- unless order_article.units_billed.nil?
= order_article.units_billed
- = pkg_helper order_article.article, soft_uq: true
+ = pkg_helper order_article.article_version, soft_uq: true
%td.units_received_cell
= receive_input_field(form)
- = pkg_helper order_article.article_price, icon: false, soft_uq: true
+ = pkg_helper order_article.article_version, icon: false, soft_uq: true, unit: order_article.article_version.billing_unit
/ TODO add almost invisible text_field for entering single units
%td.units_delta
%td
diff --git a/app/views/orders/_edit_amounts.html.haml b/app/views/orders/_edit_amounts.html.haml
index 17666c2d..4d897d2f 100644
--- a/app/views/orders/_edit_amounts.html.haml
+++ b/app/views/orders/_edit_amounts.html.haml
@@ -1,91 +1,15 @@
+= render partial: 'shared/js_templates/unit_conversion_popover_template', locals: {set_units_data: true}
- new_articles = (@order.supplier.articles.undeleted rescue @order.articles)
-- new_article_data = articles_for_select2(new_articles, @order_articles.map(&:article_id)) {|a| "#{a.name} (#{a.unit_quantity}⨯#{a.unit})"}
+- current_article_ids = @order_articles.map { |oa| oa.article_version.article_id }
+- new_article_data = articles_for_select2(new_articles, current_article_ids) {|a| "#{a.name} (#{a.unit_quantity}⨯#{a.unit})"}
- content_for :javascript do
:javascript
-
- function update_delta(input) {
- var units = $(input).val();
- var expected = $(input).data('units-expected');
- var delta = Math.round((units-expected)*100)/100.0;
- var html;
-
- if (units.replace(/\s/g,"")=="") {
- // no value
- html = '';
- } else if (isNaN(units)) {
- html = '';
- } else if (delta == 0) {
- // equal value
- html = '';
- } else {
- if (delta < 0) {
- html = '- '+(-delta)+'';
- } else /*if (units> expected)*/ {
- html = '+ '+(delta)+'';
- }
- // show package icon only if the receive field has one
- if ($(input).hasClass('package')) {
- html += '#{j pkg_helper_icon}';
- }
- }
-
- $(input).closest('tr').find('.units_delta').html(html);
-
- // un-dim row when received is nonzero
- $(input).closest('tr').toggleClass('unavailable', expected == 0 && html=='');
- }
-
- $(document).on('change keyup', 'input[data-units-expected]', function() {
- update_delta(this);
- });
-
- $(document).on('touchclick', '#order_articles .unlocker', unlock_receive_input_field);
-
- $(document).on('click', '#set_all_to_zero', function() {
- $('tbody input').each(function(i, input) {
- $(input).val(0);
- update_delta(input);
- });
- })
-
- $(function() {
- $('input[data-units-expected]').each(function() {
- update_delta(this);
- });
-
- init_add_article('#add_article');
- });
-
- function init_add_article(sel) {
- $(sel).removeAttr('disabled').select2({
- placeholder: '#{j t('orders.receive.add_article')}',
- formatNoMatches: function(term) { return '#{j t('.no_articles_available')}';}
- // TODO implement adding a new article, like in deliveries
- }).on('change', function(e) {
- var $input = $(e.target);
- var selectedArticleId = $input.val();
- if(!selectedArticleId) {
- return false;
- }
-
- $.ajax({
- url: '#{order_order_articles_path(@order)}',
- type: 'post',
- data: JSON.stringify({order_article: {article_id: selectedArticleId}}),
- contentType: 'application/json; charset=UTF-8'
- });
-
- $input.val('').trigger('change');
- });
- $(sel).val('').trigger('change');
- }
-
- function unlock_receive_input_field() {
- $('.units_received', $(this).closest('tr')).prop('disabled', false).focus();
- $(this).closest('.input-prepend').prop('title', I18n.t('orders.edit_amount.field_unlocked_title'));
- $(this).replaceWith('');
- }
-
+ $('.units_received').each((_, field) => $(field).unitConversionField({
+ units: unitsData,
+ popoverTemplate$: $('#unit_conversion_popover_content_template'),
+ useTargetUnitForStep: false
+ }));
+ $('table#order_articles').parents('form').receiveOrderForm({packageHelperIcon: '#{j pkg_helper_icon}', newOrderArticlePath:'#{order_order_articles_path(@order)}'});
%table#order_articles.ordered-articles.table.table-striped.stupidtable{style: 'margin-bottom: 0'}
%thead
%tr
diff --git a/app/views/orders/_form.html.haml b/app/views/orders/_form.html.haml
index ec3db1f3..c73aa9fe 100644
--- a/app/views/orders/_form.html.haml
+++ b/app/views/orders/_form.html.haml
@@ -50,9 +50,7 @@
%td= truncate article.origin, length: 15, tooltip: true
%td= truncate article.manufacturer, length: 15, tooltip: true
%td
- = article.unit
- - if article.unit_quantity > 1
- %span{style: 'color: grey'} × #{article.unit_quantity}#{pkg_helper_icon}
+ = format_supplier_order_unit_with_ratios(article)
%td= "#{number_to_currency(article.price)} / #{number_to_currency(article.fc_price)}"
%tr
%td
diff --git a/app/views/orders/add_article.js.erb b/app/views/orders/add_article.js.erb
index 7343d501..e43e3a49 100644
--- a/app/views/orders/add_article.js.erb
+++ b/app/views/orders/add_article.js.erb
@@ -1,14 +1,14 @@
$('div.container-fluid').prepend(
- '<%= j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.notice', :name => @order_article.article.name)})) %>'
+ '<%= j(render(:partial => 'shared/alert_success', :locals => {:alert_message => t('.notice', :name => @order_article.article_version.name)})) %>'
);
(function() {
$('.ordered-articles tr').removeClass('success');
-
+
var article_for_adding = $(
'<%= j(render(:partial => 'edit_amount', :locals => {:order_article => @order_article})) %>'
).addClass('success');
-
+
$('.ordered-articles tbody').append(article_for_adding);
updateSort('.ordered-articles');
diff --git a/app/views/orders/receive_on_order_article_create.js.erb b/app/views/orders/receive_on_order_article_create.js.erb
index ed37975f..464ffe1e 100644
--- a/app/views/orders/receive_on_order_article_create.js.erb
+++ b/app/views/orders/receive_on_order_article_create.js.erb
@@ -2,16 +2,16 @@
// See publish/subscribe design pattern in /doc.
(function(w) {
$('#order_article_<%= @order_article.id %>').remove(); // just to be sure: remove table row which is added below
-
+
$('#order_articles tr').removeClass('success');
-
+
var order_article_entry = $(
'<%= j render(partial: 'edit_amount', locals: {order_article: @order_article}) %>'
).addClass('success');
-
+
$('#order_articles tbody').append(order_article_entry);
updateSort('#order_articles');
-
- $('#add_article_<%= @order_article.article.id %>').remove(); // remove option to add this article
+
+ $('#add_article_<%= @order_article.article_version.id %>').remove(); // remove option to add this article
})(window);
diff --git a/app/views/orders/receive_on_order_article_update.js.erb b/app/views/orders/receive_on_order_article_update.js.erb
index d4ee9a72..1c834707 100644
--- a/app/views/orders/receive_on_order_article_update.js.erb
+++ b/app/views/orders/receive_on_order_article_update.js.erb
@@ -1,35 +1,9 @@
// Handle more advanced DOM update after AJAX database manipulation.
// See publish/subscribe design pattern in /doc.
(function(w) {
- // get old element and update the cell which is reused
- var old_order_article_entry = $('#order_article_<%= @order_article.id %>');
-
- // update package info after input
- $('td.units_received_cell span.package', old_order_article_entry).remove();
- $('<%= j pkg_helper(@order_article.article_price, icon: false) %>')
- .appendTo($('td.units_received_cell', old_order_article_entry));
-
- // update package icon on input too
- $('input', old_order_article_entry).toggleClass('package', <%= @order_article.article_price.unit_quantity == 1 ? 'false' : 'true' %>);
-
- // update expected units, since unit_quantity may have been changed
- $('input', old_order_article_entry).data('units-expected', <%=
- (@order_article.units_billed or @order_article.units_to_order) *
- 1.0 * @order_article.article.unit_quantity / @order_article.article_price.unit_quantity
- %>);
-
- // render new element and inject dynamic cell
- var new_order_article_entry = $(
- '<%= j render(partial: 'edit_amount', locals: {order_article: @order_article}) %>'
- );
-
- $('td.units_received_cell', new_order_article_entry).replaceWith(
- $('td.units_received_cell', old_order_article_entry)
- );
-
- // finally replace the OrderArticle entry
- old_order_article_entry.replaceWith(new_order_article_entry);
-
- update_delta($('input.units_received', new_order_article_entry));
+ $('table#order_articles').parents('form').receiveOrderForm('replace', {
+ id: <%= @order_article.id %>,
+ newEntry: $('<%= j render(partial: 'edit_amount', locals: {order_article: @order_article}) %>')
+ });
})(window);
diff --git a/app/views/orders/show.html.haml b/app/views/orders/show.html.haml
index e76db249..da47bea2 100644
--- a/app/views/orders/show.html.haml
+++ b/app/views/orders/show.html.haml
@@ -1,3 +1,4 @@
+= render partial: 'shared/js_templates/unit_conversion_popover_template', locals: {set_units_data: true}
- title t('.title', name: @order.name)
- if current_user.role_finance? || current_user.role_invoices?
@@ -95,3 +96,5 @@
= render 'show_js'
= render 'shared/articles_by/common', order: @order
+
+
diff --git a/app/views/orders/show.js.erb b/app/views/orders/show.js.erb
index 47a69e8d..2c29b7f3 100644
--- a/app/views/orders/show.js.erb
+++ b/app/views/orders/show.js.erb
@@ -1,4 +1,11 @@
$('#articles_table').html('<%= j render(@partial, order: @order) %>');
+$('*[name="group_order_article[result]"]').each(function() {
+ $(this).unitConversionField({
+ units: unitsData,
+ popoverTemplate$: $('#unit_conversion_popover_content_template'),
+ useTargetUnitForStep: false
+ });
+});
$('.view_buttons a').each(function() {
$(this).toggleClass('active', $(this).attr('id') == 'view_<%= j params[:view] %>_btn');
diff --git a/app/views/shared/_article_fields_price.html.haml b/app/views/shared/_article_fields_price.html.haml
index ea6ee9a2..efe18036 100644
--- a/app/views/shared/_article_fields_price.html.haml
+++ b/app/views/shared/_article_fields_price.html.haml
@@ -3,32 +3,22 @@
.input-prepend
%span.add-on= t 'number.currency.format.unit'
= f.input_field :price, class: 'input-mini'
+ = f.input :price_unit, as: :select, collection: [], input_html: {'data-initial-value': article.price_unit, class: 'input-medium'}, label: t('articles.form.per'), include_blank: false
+.fold-line
= f.input :tax do
.input-append
= f.input_field :tax, class: 'input-mini'
%span.add-on %
-.fold-line
= f.input :deposit do
.input-prepend
%span.add-on= t 'number.currency.format.unit'
= f.input_field :deposit, class: 'input-mini'
+.fold-line
.control-group
%label.control-label{for: 'article_fc_price'}
= Article.human_attribute_name(:fc_price)
.controls.control-text#article_fc_price
- = number_to_currency(f.object.fc_price) rescue nil
-
--# do this inline, since it's being used in ajax forms only
-- field = f.object.class.model_name.to_s.underscore
-:javascript
- var form = $('#article_fc_price').closest('form');
- $('##{field}_price, ##{field}_tax, ##{field}_deposit', form).on('change keyup', function() {
- var price = parseFloat($('##{field}_price', form).val());
- var tax = parseFloat($('##{field}_tax', form).val());
- var deposit = parseFloat($('##{field}_deposit', form).val());
- // Article#gross_price and Article#fc_price
- var gross_price = (price + deposit) * (tax / 100 + 1);
- var fc_price = gross_price * (#{FoodsoftConfig[:price_markup].to_f} / 100 + 1);
- $('#article_fc_price').html($.isNumeric(fc_price) ? I18n.l("currency", fc_price) : '
');
- });
+ %span.price_value
+ %span.price_per_text=t('articles.form.per')
+ %span.price_unit
diff --git a/app/views/shared/_article_fields_units.html.haml b/app/views/shared/_article_fields_units.html.haml
index b9704cef..67885336 100644
--- a/app/views/shared/_article_fields_units.html.haml
+++ b/app/views/shared/_article_fields_units.html.haml
@@ -1,6 +1,33 @@
--# use the local 'f', or supply 'f_uq' and 'f_unit' for more control (like in balancing)
+-# var parcel = $($.parseHTML("#{escape_javascript(render('shared/parcel'))}"));
+-# var parcels = #{raw @parcels.to_json};
+
+.fold-line.d-flex
+ = f.hidden_field :unit, id: 'article_unit_hidden', value: ''
+ -# title: unit in which article can be ordered from supplier
+ - supplier_order_unit_override = article.supplier_order_unit
+ - supplier_order_unit_override = 'XPP' if supplier_order_unit_override.nil? && article.unit.nil?
+ = f.input :supplier_order_unit, as: :select, collection: @article_units, selected: supplier_order_unit_override, include_blank: t('.custom_unit'), input_html: {class: 'input-medium'}
+ -# title: free text unit
+ = f.input :unit, input_html: {class: 'input-mini ml-1', placeholder: t('.unit')}, label: false
+ %i.icon-warning-sign{title: t('warn_about_needless_custom_unit')}
.fold-line
- = (f_uq rescue f).input :unit_quantity, label: Article.human_attribute_name(:unit),
- input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit_quantity)}
- = (f_unit rescue f).input :unit, label: '×'.html_safe,
- input_html: {class: 'input-mini', title: Article.human_attribute_name(:unit)}
+ .control-group
+ %label.control-label
+ = t('.unit_ratios')
+ %table#fc_base_price{:class => "controls"}
+ %tbody
+ - ratios = article.article_unit_ratios
+ = render :partial => 'shared/article_unit_ratio', :as => 'article_unit_ratio', :collection => ratios, locals: {f: f}
+ %tfoot
+ %td{:colspan => 6}
+ = link_to t('.add_ratio'), '#', 'data-add-ratio' => true, class: 'btn', title: "add ratio"
+.fold-line
+ = f.input :minimum_order_quantity do
+ .input-append
+ = f.input_field :minimum_order_quantity, class: 'input-mini'
+ %span.add-on
+.fold-line
+ = f.input :billing_unit, as: :select, collection: [], input_html: {'data-initial-value': article.billing_unit, class: 'input-medium'}, include_blank: false
+.fold-line
+ = f.input :group_order_granularity, input_html: {class: 'input-mini'}
+ = f.input :group_order_unit, as: :select, collection: [], input_html: {'data-initial-value': article.group_order_unit, class: 'input-medium'}, label: '×'.html_safe, include_blank: false
diff --git a/app/views/shared/_article_unit_ratio.haml b/app/views/shared/_article_unit_ratio.haml
new file mode 100644
index 00000000..a4532933
--- /dev/null
+++ b/app/views/shared/_article_unit_ratio.haml
@@ -0,0 +1,19 @@
+- original_ratio = defined?(original_ratios) ? original_ratios&.to_a&.dig(article_unit_ratio_counter) : nil
+- changed = !original_ratio.nil? && (original_ratio.quantity != article_unit_ratio.quantity || original_ratio.unit != article_unit_ratio.unit)
+%tr{:class => 'no-indent-left', :style => changed ? "background-color: yellow" : ""}
+ = f.fields_for(:article_unit_ratios, article_unit_ratio) do |sub_form|
+ -# title: unit quantifier
+ %td
+ -# TODO: Check, if there was any justified reason for this article_unit_ratio_counter logic:
+ -#= hidden_field f.object.class.to_s.underscore + "[article_unit_ratios_attributes][#{article_unit_ratio_counter + 1}]", :sort, value: article_unit_ratio_counter + 1
+ = sub_form.input :sort, as: :hidden
+
+ = sub_form.input :quantity, {input_html: {class: 'input-mini'}, label: false}
+ %td= '×'.html_safe
+ %td= sub_form.input :unit, as: :select, include_blank: true, collection: @article_units, label: false, input_html: {class: 'input-medium'}
+ %td.ml-1= t('.per')
+ %td
+ %span.unit_multiplier.mr-1
+ %td.actions{style: 'width:1em'}
+ = link_to t('.remove'), method: :delete, 'data-remove-ratio' => true, title: t('.remove'), class: 'btn btn-danger btn-mini' do
+ = glyph :remove
diff --git a/app/views/shared/_article_price_info.html.haml b/app/views/shared/_article_version_info.html.haml
similarity index 100%
rename from app/views/shared/_article_price_info.html.haml
rename to app/views/shared/_article_version_info.html.haml
diff --git a/app/views/shared/articles_by/_article_single.html.haml b/app/views/shared/articles_by/_article_single.html.haml
index 6c563d64..6e8aa352 100644
--- a/app/views/shared/articles_by/_article_single.html.haml
+++ b/app/views/shared/articles_by/_article_single.html.haml
@@ -3,11 +3,9 @@
%tr.list-heading
%th{:colspan => "4"}>
%h4.name.pull-left
- = order_article.article.name
+ - article_version = order_article.article_version
+ = article_version.name
%small
- = "(#{order_article.article.unit}, #{number_to_currency order_article.price.fc_price}"
- - pkg_info = pkg_helper(order_article.price)
- = ", #{pkg_info}".html_safe unless pkg_info.blank?
- )
+ = "#{format_billing_unit_with_ratios(article_version)}, #{number_to_currency(article_version.convert_quantity(article_version.fc_price, article_version.billing_unit, article_version.supplier_order_unit))}"
- for goa in order_article.group_order_articles.ordered
= render 'shared/articles_by/article_single_goa', goa: goa
diff --git a/app/views/shared/articles_by/_article_single_goa.html.haml b/app/views/shared/articles_by/_article_single_goa.html.haml
index 1f727007..cdade7ab 100644
--- a/app/views/shared/articles_by/_article_single_goa.html.haml
+++ b/app/views/shared/articles_by/_article_single_goa.html.haml
@@ -1,5 +1,6 @@
%tr{class: if goa.result == 0 then 'unavailable' end, id: "goa_#{goa.id}"}
+ - article_version = goa.order_article.article_version
%td{:style => "width:70%"}= goa.group_order.ordergroup_name
- %td.center= "#{goa.quantity} + #{goa.tolerance}"
+ %td.center= "#{article_version.convert_quantity(goa.quantity, article_version.group_order_unit, article_version.billing_unit)} + #{article_version.convert_quantity(goa.tolerance, article_version.group_order_unit, article_version.billing_unit)}"
%td.center.input-delta= group_order_article_edit_result(goa)
%td.price{data: {value: goa.total_price}}= number_to_currency(goa.total_price)
diff --git a/app/views/shared/articles_by/_articles.html.haml b/app/views/shared/articles_by/_articles.html.haml
index c2bb0695..a1e8d44c 100644
--- a/app/views/shared/articles_by/_articles.html.haml
+++ b/app/views/shared/articles_by/_articles.html.haml
@@ -8,7 +8,7 @@
%acronym{:title => t('shared.articles.received_desc')}= t 'shared.articles.received'
%th= t 'shared.articles_by.price'
- - for order_article in order.order_articles.ordered.includes([:article, :article_price])
+ - for order_article in order.order_articles.ordered.includes(:article_version)
= render 'shared/articles_by/article_single', order_article: order_article
%tr
%td{colspan: 4}
diff --git a/app/views/shared/articles_by/_group_single_goa.html.haml b/app/views/shared/articles_by/_group_single_goa.html.haml
index 88189126..59a1b80c 100644
--- a/app/views/shared/articles_by/_group_single_goa.html.haml
+++ b/app/views/shared/articles_by/_group_single_goa.html.haml
@@ -1,10 +1,10 @@
%tr{class: [cycle('even', 'odd', :name => 'articles'), if goa.result == 0 then 'unavailable' end], id: "goa_#{goa.id}"}
- %td.name= goa.order_article.article.name
- %td= goa.order_article.article.unit
- %td.center= "#{goa.quantity} + #{goa.tolerance}"
+ - article_version = goa.order_article.article_version
+ %td.name= goa.order_article.article_version.name
+ %td= format_billing_unit_with_ratios(article_version)
+ %td.center= "#{article_version.convert_quantity(goa.quantity, article_version.group_order_unit, article_version.billing_unit)} + #{article_version.convert_quantity(goa.tolerance, article_version.group_order_unit, article_version.billing_unit)}"
%td.center.input-delta= group_order_article_edit_result(goa)
%td.symbol ×
- %td= number_to_currency(goa.order_article.price.fc_price)
+ %td= number_to_currency(article_version.convert_quantity(article_version.fc_price, article_version.billing_unit, article_version.supplier_order_unit))
%td.symbol =
%td.price{data: {value: goa.total_price}}= number_to_currency(goa.total_price)
- %td= pkg_helper goa.order_article.price
diff --git a/app/views/shared/articles_by/_groups.html.haml b/app/views/shared/articles_by/_groups.html.haml
index 4bd9c285..8787a503 100644
--- a/app/views/shared/articles_by/_groups.html.haml
+++ b/app/views/shared/articles_by/_groups.html.haml
@@ -11,7 +11,6 @@
%th= heading_helper Article, :fc_price, short: true
%th.symbol
%th= t 'shared.articles_by.price'
- %th= #heading_helper Article, :unit_quantity, short: true
- for group_order in order.group_orders.ordered
= render 'shared/articles_by/group_single', group_order: group_order
diff --git a/app/views/shared/js_templates/_unit_conversion_popover_template.haml b/app/views/shared/js_templates/_unit_conversion_popover_template.haml
new file mode 100644
index 00000000..69274718
--- /dev/null
+++ b/app/views/shared/js_templates/_unit_conversion_popover_template.haml
@@ -0,0 +1,12 @@
+- if defined?(set_units_data) && set_units_data
+ - content_for :javascript do
+ :javascript
+ const unitsData = #{raw(ArticleUnit.as_hash.to_json)};
+%template#unit_conversion_popover_content_template
+ %div.popover_contents.text-right{"data-title" => t('helpers.unit_conversion_fields.title')}
+ .d-flex
+ %select.unit.mr-1{"data-ignore-onchange" => true}
+ %input.quantity.input-mini.numeric{:type => 'number', :min => 0}
+ .conversion-result.mt-1.mb-1
+ %input.cancel.btn{:type => 'button', :value => t('helpers.unit_conversion_fields.cancel')}
+ %input.apply.btn.btn-primary{:type => 'button', :value => t('helpers.unit_conversion_fields.apply')}
diff --git a/app/views/stock_takings/_stock_change.html.haml b/app/views/stock_takings/_stock_change.html.haml
index c82bd232..313fec66 100644
--- a/app/views/stock_takings/_stock_change.html.haml
+++ b/app/views/stock_takings/_stock_change.html.haml
@@ -4,6 +4,6 @@
= StockArticle.human_attribute_name :amount
(#{stock_change.stock_article.quantity_available})
= form.text_field :quantity, :size => 5, :autocomplete => 'off'
- %span{:data => {:toggle => :tooltip, :title => "#{render(:partial => 'shared/article_price_info', :locals => {:article => stock_change.stock_article})}"}}
+ %span{:data => {:toggle => :tooltip, :title => "#{render(:partial => 'shared/article_version_info', :locals => {:article => stock_change.stock_article})}"}}
%b= stock_change.stock_article.name
= "(#{number_to_currency(stock_change.stock_article.price)} / #{stock_change.stock_article.unit})"
diff --git a/app/views/stockit/_form.html.haml b/app/views/stockit/_form.html.haml
index 9048b5a1..83a7b8a7 100644
--- a/app/views/stockit/_form.html.haml
+++ b/app/views/stockit/_form.html.haml
@@ -1,11 +1,34 @@
-= simple_form_for stock_article, remote: true, :validate => true do |f|
+= simple_form_for stock_article.latest_article_version, :url => stock_article.new_record? ? stock_articles_path(stock_article, foodcoop: FoodsoftConfig.scope) : stock_article_path(stock_article), remote: true, validate: true do |f|
+ :javascript
+ const articleUnitRatioTemplate$ = $($.parseHTML("#{escape_javascript(render(partial: 'shared/article_unit_ratio', locals: {article_unit_ratio: @empty_article_unit_ratio, f: f, article_unit_ratio_counter: -1}))}"));
+ const units = #{raw(@all_units.to_json)};
+ new ArticleForm(articleUnitRatioTemplate$, $('.article-form').parents('form'), units, #{FoodsoftConfig[:price_markup].to_f});
+ = render partial: 'shared/js_templates/unit_conversion_popover_template'
.modal-header
= close_button :modal
%h3= title
- .modal-body
- = f.association :supplier
+ .modal-body.article-form
+ = f.hidden_field :id
+ = f.simple_fields_for :article do |a|
+ = a.input :supplier_id, collection: Supplier.all, selected: stock_article.supplier_id
= f.input :name
- = f.input :unit
+ .fold-line.d-flex
+ = f.hidden_field :unit, id: 'article_unit_hidden', value: ''
+ -# title: unit in which article can be ordered from supplier
+ = f.input :supplier_order_unit, as: :select, collection: @article_units, value: stock_article.supplier_order_unit, include_blank: t('.custom_unit'), input_html: {class: 'input-medium'}
+ -# title: free text unit
+ = f.input :unit, input_html: {class: 'input-mini ml-1'}, label: false
+ .fold-line
+ .control-group
+ %label.control-label{for: 'unit_ratios'}
+ = "Unit ratios"
+ %table#fc_base_price{:class => "controls"}
+ %tbody
+ - ratios = stock_article.article_unit_ratios
+ = render :partial => 'shared/article_unit_ratio', :as => 'article_unit_ratio', :collection => ratios, locals: {f: f}
+ %tfoot
+ %td{:colspan => 6}
+ = link_to t('.add_ratio'), '#', 'data-add-ratio' => true, class: 'btn', title: "add ratio"
= f.input :note
- if stock_article.new_record?
= f.input :price
diff --git a/app/views/stockit/_stock_article.html.haml b/app/views/stockit/_stock_article.html.haml
index 4df32342..789fefe9 100644
--- a/app/views/stockit/_stock_article.html.haml
+++ b/app/views/stockit/_stock_article.html.haml
@@ -3,7 +3,7 @@
%td= stock_article.quantity
%td= stock_article.quantity_ordered
%td.main_info= stock_article.quantity_available
- %td= stock_article.unit
+ %td= format_supplier_order_unit(stock_article)
%td= stock_article.price
%td= number_to_percentage stock_article.tax
%td= link_to stock_article.supplier.name, stock_article.supplier
diff --git a/app/views/supplier_shares/_share_link.html.haml b/app/views/supplier_shares/_share_link.html.haml
new file mode 100644
index 00000000..b0fce48c
--- /dev/null
+++ b/app/views/supplier_shares/_share_link.html.haml
@@ -0,0 +1,16 @@
+- if external_uuid.nil?
+ = t('.not_shared')
+- else
+ - share_url = api_v1_shared_supplier_articles_url(external_uuid)
+ %div#copy-share-link-button.user-select-none{:role => 'button'}
+ %span= share_url
+ %i.icon-copy
+
+ :javascript
+ document
+ .getElementById('copy-share-link-button')
+ .addEventListener('click', () => {
+ navigator.clipboard.writeText('#{share_url}');
+ alert('#{t(".share_link_copied_to_clipboard")}');
+ });
+
diff --git a/app/views/supplier_shares/update.js.haml b/app/views/supplier_shares/update.js.haml
new file mode 100644
index 00000000..caf08dba
--- /dev/null
+++ b/app/views/supplier_shares/update.js.haml
@@ -0,0 +1,2 @@
+$('#share_link').html('#{j(render(partial: 'share_link', locals: {external_uuid: @supplier.external_uuid}))}');
+$('.btn.share').toggle();
diff --git a/app/views/suppliers/_form.html.haml b/app/views/suppliers/_form.html.haml
index d8d94d9d..3099b513 100644
--- a/app/views/suppliers/_form.html.haml
+++ b/app/views/suppliers/_form.html.haml
@@ -1,8 +1,4 @@
= simple_form_for @supplier do |f|
- - if @supplier.shared_supplier
- .alert.alert-info
- = t 'suppliers.shared_supplier_note'
- = f.hidden_field :shared_supplier_id
= f.input :name
= f.input :address
= f.input :phone
@@ -21,9 +17,9 @@
= f.input :order_howto, as: :text, input_html: {rows: 5}
= f.input :note, as: :text, input_html: {rows: 5}
= f.input :min_order_quantity
+ = f.input :supplier_remote_source
= render 'shared/custom_form_fields', f: f, type: :supplier
- - if @supplier.shared_supplier
- = f.input :shared_sync_method, collection: shared_sync_method_collection(@supplier.shared_supplier), input_html: {class: 'input-xlarge'}, include_blank: false, disabled: @supplier.shared_supplier.shared_sync_methods.count < 2
+ = f.input :shared_sync_method, collection: shared_sync_method_collection, input_html: {class: 'input-xlarge'}, include_blank: true
.form-actions
= f.submit class: 'btn'
= link_to t('ui.or_cancel'), suppliers_path
diff --git a/app/views/suppliers/_import_search_results.haml b/app/views/suppliers/_import_search_results.haml
new file mode 100644
index 00000000..cbc6bbc8
--- /dev/null
+++ b/app/views/suppliers/_import_search_results.haml
@@ -0,0 +1,29 @@
+- if @articles.empty?
+ %p= t '.not_found'
+- else
+ = pagination_links_remote @pagination
+ %table.table.table-striped
+ %thead
+ %tr
+ %th= heading_helper Article, :name
+ %th= heading_helper Article, :origin
+ %th= heading_helper Article, :manufacturer
+ %th= heading_helper Article, :note
+ %th{:style => "width:4em"}= heading_helper Article, :price
+ %th= heading_helper Article, :supplier_order_unit
+ %th
+ %tbody
+ - @articles.each_with_index do |article, index|
+ %tr
+ %td= highlight article[:name], params.fetch(:name, '').split(' ')
+ %td= article[:origin]
+ %td= article[:manufacturer]
+ %td{title: article[:note]}= truncate(article[:note], length: 11)
+ %td= number_to_currency(article[:price])
+ %td= ArticleUnitsLib.get_translated_name_for_code(article[:supplier_order_unit])
+ %td.actions.import-actions{:data => {order_number: article[:order_number]}}
+ - article_exists = @supplier.articles.includes(:latest_article_version).undeleted.where(article_versions: {order_number: article[:order_number]}).exists?
+ %i.icon-ok{:style => article_exists ? '' : 'display:none;'}= t '.already_imported'
+ = link_to t('.action_import'), supplier_articles_path(@supplier),
+ remote: true, class: 'btn btn-small btn-success article_import_btn', data: {index: index},
+ style: article_exists ? 'display:none;' : ''
diff --git a/app/views/suppliers/index.html.haml b/app/views/suppliers/index.html.haml
index 9e4c3870..ab90257f 100644
--- a/app/views/suppliers/index.html.haml
+++ b/app/views/suppliers/index.html.haml
@@ -2,8 +2,6 @@
- content_for :actionbar do
= link_to t('.action_new'), new_supplier_path, class: 'btn btn-primary'
- - if FoodsoftConfig[:shared_lists]
- = link_to t('.action_import'), shared_suppliers_suppliers_path, class: 'btn'
%table.table.table-striped
%thead
%tr
diff --git a/app/views/suppliers/remote_articles.js.haml b/app/views/suppliers/remote_articles.js.haml
new file mode 100644
index 00000000..06a72b9f
--- /dev/null
+++ b/app/views/suppliers/remote_articles.js.haml
@@ -0,0 +1,22 @@
+:plain
+ $('#search_results').html("#{j(render("import_search_results"))}");
+ const remoteArticleData = #{raw(@articles.to_json)};
+ $("#search_results .article_import_btn").click(function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ const index = $(this).data('index');
+ const article = remoteArticleData[index];
+ const categoryId = $('#import #article_category_id').children(':selected').val();
+ if (categoryId !== '') {
+ article.article_category_id = categoryId;
+ }
+ article.article_unit_ratios_attributes = article.article_unit_ratios;
+ delete article.article_unit_ratios;
+ delete article.created_at;
+ delete article.updated_at;
+ $.post($(this).attr('href'), {article_version: article},
+ function(result) {
+ alter('success', result);
+ }
+ );
+ });
diff --git a/app/views/suppliers/shared_suppliers.html.haml b/app/views/suppliers/shared_suppliers.html.haml
deleted file mode 100644
index 8e9c3eef..00000000
--- a/app/views/suppliers/shared_suppliers.html.haml
+++ /dev/null
@@ -1,25 +0,0 @@
-- title t('.title')
-= t('.body').html_safe
-
-%table.table.table-striped
- %thead
- %tr
- %th= heading_helper Supplier, :name
- %th= heading_helper Supplier, :address
- %th= heading_helper Supplier, :note
- %th= heading_helper Supplier, :delivery_days
- %th= heading_helper Supplier, :is_subscribed
- %tbody
- - for shared_supplier in @shared_suppliers
- %tr
- %td= shared_supplier.name
- %td= shared_supplier.address
- %td= shared_supplier.note
- %td= shared_supplier.delivery_days
- %td
- - if shared_supplier.suppliers.any?
- %i.icon-ok
- = associated_supplier_names(shared_supplier)
- = link_to t('.subscribe_again'), new_supplier_path(:shared_supplier_id => shared_supplier), class: 'btn'
- - else
- = link_to t('.subscribe'), new_supplier_path(:shared_supplier_id => shared_supplier), class: 'btn'
diff --git a/app/views/suppliers/show.html.haml b/app/views/suppliers/show.html.haml
index c3fbe4c4..db506c95 100644
--- a/app/views/suppliers/show.html.haml
+++ b/app/views/suppliers/show.html.haml
@@ -7,7 +7,7 @@
.row-fluid
.span6
- - if shared_supplier = @supplier.shared_supplier
+ - unless @supplier.shared_sync_method.nil?
.alert.alert-info
= t 'suppliers.shared_supplier_note'
= t "suppliers.shared_supplier_methods.#{@supplier.shared_sync_method}"
@@ -43,6 +43,11 @@
%dd= simple_format(@supplier.note)
%dt= heading_helper(Supplier, :min_order_quantity) + ':'
%dd= @supplier.min_order_quantity
+ %dt= heading_helper(Supplier, :share_link) + ':'
+ %dd
+ %div#share_link= render partial: 'supplier_shares/share_link', locals: { external_uuid: @supplier.external_uuid }
+ = link_to t('.share'), supplier_share_path(@supplier), remote: true, class: 'btn share', method: :post, style: @supplier.external_uuid.nil? ? nil : 'display: none;'
+ = link_to t('.remove_share'), supplier_share_path(@supplier), remote: true, class: 'btn share', method: :delete, data: { confirm: t('.confirm_remove_share') }, style: @supplier.external_uuid.nil? ? 'display: none;' : nil
.clearfix
- if @current_user.role_suppliers?
diff --git a/bin/autospec b/bin/autospec
index 64dcb9cb..602d5c82 100755
--- a/bin/autospec
+++ b/bin/autospec
@@ -7,8 +7,8 @@
#
require 'pathname'
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
- Pathname.new(__FILE__).realpath)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
+ Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
diff --git a/bin/mailcatcher b/bin/mailcatcher
index a4b2c248..67d7b036 100755
--- a/bin/mailcatcher
+++ b/bin/mailcatcher
@@ -7,8 +7,8 @@
#
require 'pathname'
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
- Pathname.new(__FILE__).realpath)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
+ Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
diff --git a/bin/resque b/bin/resque
index 2e40831b..f018141c 100755
--- a/bin/resque
+++ b/bin/resque
@@ -7,8 +7,8 @@
#
require 'pathname'
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
- Pathname.new(__FILE__).realpath)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
+ Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
diff --git a/bin/resque-web b/bin/resque-web
index b49543cd..22ad07e0 100755
--- a/bin/resque-web
+++ b/bin/resque-web
@@ -7,8 +7,8 @@
#
require 'pathname'
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
- Pathname.new(__FILE__).realpath)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
+ Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
diff --git a/bin/rspec b/bin/rspec
index 0c86b5c6..8bc84617 100755
--- a/bin/rspec
+++ b/bin/rspec
@@ -7,8 +7,8 @@
#
require 'pathname'
-ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
- Pathname.new(__FILE__).realpath)
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
+ Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
diff --git a/bin/yarn b/bin/yarn
index 460dd565..d3627c34 100755
--- a/bin/yarn
+++ b/bin/yarn
@@ -1,11 +1,9 @@
#!/usr/bin/env ruby
APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
- begin
- exec "yarnpkg", *ARGV
- rescue Errno::ENOENT
- $stderr.puts "Yarn executable was not detected in the system."
- $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
- exit 1
- end
+ exec 'yarnpkg', *ARGV
+rescue Errno::ENOENT
+ warn 'Yarn executable was not detected in the system.'
+ warn 'Download Yarn at https://yarnpkg.com/en/docs/install'
+ exit 1
end
diff --git a/config.ru b/config.ru
index e0faca0f..7417cc9a 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['RAILS_RELATIVE_URL_ROOT'] || '/' do
diff --git a/config/app_config.yml.SAMPLE b/config/app_config.yml.SAMPLE
index e43705b6..e0ef9d13 100644
--- a/config/app_config.yml.SAMPLE
+++ b/config/app_config.yml.SAMPLE
@@ -172,6 +172,15 @@ development:
test:
<<: *defaults
+ shared_lists:
+ adapter: mysql2
+ encoding: utf8mb4
+ reconnect: false
+ database: db_shared
+ pool: 5
+ host: mariadb
+ username: root
+ password: secret
production:
<<: *defaults
diff --git a/config/application.rb b/config/application.rb
index 38c9a6d4..02048377 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -28,13 +28,15 @@ class Application < Rails::Application
# config.time_zone = 'Central Time (US & Canada)'
# 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.load_path += Dir[Rails.root.join('config/locales/*.yml')]
+ 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"
+ config.encoding = 'utf-8'
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
@@ -44,7 +46,7 @@ class Application < Rails::Application
# 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
@@ -76,5 +78,9 @@ class Application < Rails::Application
# Foodsoft version
VERSION = File.read(Rails.root.join('VERSION')).strip
# Current revision, or +nil+
- REVISION = (File.read(Rails.root.join('REVISION')).strip rescue nil)
+ REVISION = begin
+ File.read(Rails.root.join('REVISION')).strip
+ rescue StandardError
+ nil
+ end
end
diff --git a/config/database_shared.yml.MYSQL_SAMPLE b/config/database_shared.yml.MYSQL_SAMPLE
new file mode 100644
index 00000000..55d40c13
--- /dev/null
+++ b/config/database_shared.yml.MYSQL_SAMPLE
@@ -0,0 +1,9 @@
+test:
+ adapter: mysql2
+ encoding: utf8mb4
+ reconnect: false
+ database: test_shared
+ pool: 5
+ host: mariadb
+ username: root
+ password: secret
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 0560b38d..12571fda 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -49,7 +49,7 @@
# 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'
# Set to :debug to see everything in the log.
config.log_level = :info
@@ -89,22 +89,31 @@
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/i18n-js.yml b/config/i18n-js.yml
index 1631dc1b..3f0e650c 100644
--- a/config/i18n-js.yml
+++ b/config/i18n-js.yml
@@ -7,5 +7,7 @@ translations:
'*.number.*',
'*.date.formats.*',
# foodsoft-specific texts to keep js with normal translations
- '*.orders.edit_amount.*'
+ '*.orders.edit_amount.*',
+ '*.orders.receive.*',
+ 'no_articles_available'
]
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 958b9e9a..64541987 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -98,7 +98,7 @@
# 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.
@@ -114,7 +114,7 @@
# my take on https://github.com/doorkeeper-gem/doorkeeper/issues/465
ActiveSupport.on_load(:after_initialize) do
- Doorkeeper::ApplicationController.send :include, Concerns::Locale
- Doorkeeper::ApplicationController.send :include, Concerns::FoodcoopScope
- Doorkeeper::ApplicationController.send :include, Concerns::Auth
+ Doorkeeper::ApplicationController.include Concerns::Locale
+ Doorkeeper::ApplicationController.include Concerns::FoodcoopScope
+ Doorkeeper::ApplicationController.include Concerns::Auth
end
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 @@
# 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 @@
# 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 @@ def self.delocalized_decimal(string)
class Array
def cumulative_sum
csum = 0
- self.map { |val| csum += val }
+ map { |val| csum += val }
end
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
index b72f4220..d56f8eee 100644
--- a/config/initializers/rails6_backports.rb
+++ b/config/initializers/rails6_backports.rb
@@ -1,6 +1,6 @@
raise "Remove no-longer-needed #{__FILE__}!" if Rails::VERSION::MAJOR >= 6
-require "weakref"
+require 'weakref'
module ActiveRecord
# Backport https://github.com/rails/rails/pull/36998 and https://github.com/rails/rails/pull/36999
@@ -15,9 +15,7 @@ class Reaper
class << self
def register_pool(pool, frequency) # :nodoc:
@mutex.synchronize do
- unless @threads[frequency]&.alive?
- @threads[frequency] = spawn_thread(frequency)
- end
+ @threads[frequency] = spawn_thread(frequency) unless @threads[frequency]&.alive?
@pools[frequency] ||= []
@pools[frequency] << WeakRef.new(pool)
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 14054341..142c4849 100644
--- a/config/initializers/secret_token.rb
+++ b/config/initializers/secret_token.rb
@@ -5,20 +5,20 @@
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Foodsoft::Application.config.secret_key_base = begin
- if (token = ENV['SECRET_KEY_BASE']).present?
+ 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)
+ sf = Rails.root.join('tmp/secret_key_base')
+ if File.exist?(sf)
File.read(sf)
else
puts "=> Generating initial SECRET_KEY_BASE in #{sf}"
token = SecureRandom.hex(30)
- File.open(sf, 'w') { |f| f.write(token) }
+ File.write(sf, token)
token
end
end
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 @@
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 @@
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/locales/de.yml b/config/locales/de.yml
index 2d96146c..0bedc246 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -25,14 +25,33 @@ de:
unit_quantity: Gebindegröße
unit_quantity_short: GebGr
units: Gebinde
+ article_unit:
+ code: Einheitencode gemäß Empfehlung der United Nations Economic Commission (UNECE)
+ code_short: Code
+ symbol: Einheitenzeichen
+ description: Beschreibung (Englisch)
article_category:
description: Beschreibung
name: Name
- article_price:
+ article_version:
deposit: Pfand
price: Nettopreis
tax: MwSt
unit_quantity: Gebindegröße
+
+ availability: Artikel ist verfügbar?
+ name: Name
+ note: Notiz
+ origin: Herkunft
+ manufacturer: Produzent
+ order_number: Bestellnummer
+ article_category: Kategorie
+
+ supplier_order_unit: Liefereinheit
+ group_order_granularity: bestellbar per
+ billing_unit: Abrechnungseinheit
+ price_unit: Preiseinheit
+ minimum_order_quantity: Mindestbestellmenge
bank_account:
balance: Kontostand
bank_gateway: Bankgateway
@@ -249,6 +268,7 @@ de:
exclusion: erledigte Aufgaben können nicht wöchentlich wiederholt werden
models:
article: Artikel
+ article_version: Artikel
article_category: Kategorie
bank_account: Bankkonto
bank_gateway: Bankgateway
@@ -484,6 +504,8 @@ de:
notice_unavail: Alle gewählten Artikel wurden auf "nicht verfügbar" gesetzt
update_sync:
notice: Alle Artikel und Preise wurden aktualisiert
+ complete_units_migration:
+ notice: Artikel erfolgreich migriert
destroy_active_article:
drop: entfernen
note: "%{article} wird in laufenden Bestellungen verwendet und kann nicht gelöscht werden. Bitte zuerst den Artikel aus den Bestellungen %{drop_link}."
@@ -503,6 +525,7 @@ de:
change_supplier: Lieferant wechseln ...
download: Artikel herunterladen
edit_all: Alle bearbeiten
+ migrate_units: Alte Artikeleinheiten migrieren
ext_db:
import: Suchen/Importieren
sync: Synchronisieren
@@ -516,6 +539,24 @@ de:
search_placeholder: Name ...
title: Artikel von %{supplier} (%{count})
upload: Artikel hochladen
+ prepare_units_migration:
+ title: Artikel von %{supplier} migrieren
+ explanation: |
+ TODO: Allgemeine Erklärung
+ Es wird sehr empfohlen ein Backup herunterzuladen ehe Sie fortfahren:
+ download: Artikel-Backup herunterladen
+ migrate_units: Nächster Schritt
+ migrate_units:
+ title: Artikel von %{supplier} migrieren
+ explanation: |
+ Die Artikeleinheiten werden nun in eine neue, flexiblere Form konvertiert. Passen Sie die Konvertierungsvorschläge an, ehe sie fortfahren.
+ TODO: Besser erklären; vielleicht mit Doku verlinken?
+ column_headers:
+ apply: Konvertieren
+ original_plaintext_unit: Ursprüngliche Volltext-Einheit
+ affected_articles: Betroffene Artikel
+ contains: Beinhaltet
+ run_migration: Migration jetzt durchführen
model:
error_in_use: "%{article} kann nicht gelöscht werden. Der Artikel befindet sich in einer laufenden Bestellung!"
error_nosel: Du hast keine Artikel ausgewählt
@@ -763,6 +804,7 @@ de:
not_found:
text: Diese Seite existiert anscheinend nicht. Entschuldigung!
title: Seite nicht gefunden
+ step_error: Gib eine Zahl >=%{min} ein, die ein Vielfaches von %{granularity} ist.
feedback:
create:
notice: Das Feedback wurde erfolgreich verschickt. Vielen Dank!
@@ -1118,6 +1160,10 @@ de:
create: Einladung verschicken
tasks:
required_users: "Es fehlen %{count} Mitstreiterinnen!"
+ unit_conversion_fields:
+ title: Einheiten umrechnen
+ cancel: Abbrechen
+ apply: Anwenden
home:
apple_bar:
desc: 'Abgebildet ist das Verhältnis von erledigten Aufgaben zu dem Bestellvolumen Deiner Bestellgruppe im Vergleich zum Durchschnitt in der Foodcoop. Konkret: Pro %{amount} Bestellsumme solltest Du eine Aufgabe machen!'
@@ -1406,6 +1452,7 @@ de:
stock: Lager
suppliers: Lieferanten/Artikel
title: Artikel
+ article_units: Artikeleinheiten
finances:
accounts: Konten verwalten
balancing: Bestellungen abrechnen
@@ -1579,6 +1626,14 @@ de:
ordered_desc: Anzahl der Artikel die durch das Mitglied bestellt wurden (Menge + Toleranz)
received: Bekommen
received_desc: Anzahl der Artikel die ein Mitglied erhält
+ article_fields_units:
+ custom_unit: Benutzerdefiniert
+ unit: Einheit
+ unit_ratios: Einheiten-Umrechnung
+ add_ratio: Umrechnung hinzufügen
+ article_unit_ratio:
+ remove: Umrechnung entfernen
+ per: pro
articles_by:
price: Gesamtpreis
price_sum: Summe
@@ -1736,6 +1791,20 @@ de:
stock_changes: Verlauf des Lagerbestands
update:
notice: Lagerartikel »%{name}« aktualisiert.
+ article_units:
+ index:
+ search: Einheit/Symbol suchen
+ rows:
+ request_translation: Für diese Einheit existiert noch keine Übersetzung. Wenn Sie der Meinung sind, dass es eine gebräuchliche Einheit ist, klicken Sie hier um ein neues GitHub Issue zu erstellen.
+ create_link:
+ add: Hinzufügen
+ destroy_link:
+ delete: Entfernen
+ confirm: Wollen Sie die Artikeleinheit wirklich entfernen? (Bestehende Artikel, die diese Einheit verwenden, werden nicht verändert, aber man kann die Einheit dann nicht mehr in neuen Artikeln verwenden.)
+ create:
+ success: Artikeleinheit %{name} erfolgreich hinzugefügt.
+ destroy:
+ success: Artikeleinheit %{name} erfolgreich entfernt.
suppliers:
create:
notice: Lieferant wurde erstellt
diff --git a/config/locales/en.yml b/config/locales/en.yml
index d94c7204..79caf1df 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -25,14 +25,35 @@ en:
unit_quantity: Unit quantity
unit_quantity_short: U.Q.
units: Units
+ supplier_order_unit: Supplier order unit
+ article_unit:
+ code: Unit code according to the recommendation of the United Nations Economic Commission (UNECE)
+ code_short: Code
+ symbol: Symbol
+ description: Description
article_category:
description: Import names
name: Name
- article_price:
+ article_version:
deposit: Deposit
price: Price (net)
tax: VAT
unit_quantity: Unit quantity
+
+
+ availability: Is article available?
+ name: Name
+ note: Note
+ origin: Origin
+ manufacturer: Manufacturer
+ order_number: Order number
+ article_category: Category
+
+ supplier_order_unit: Delivery unit
+ group_order_granularity: can be ordered per
+ billing_unit: Billing unit
+ price_unit: Price unit
+ minimum_order_quantity: Minimum order quantity
bank_account:
balance: Balance
bank_gateway: Bank gateway
@@ -138,6 +159,7 @@ en:
updated_by: Last edited by
order_article:
article: Article
+ article_version: Article
missing_units: Missing units
missing_units_short: Missing
quantity: Desired amount
@@ -485,6 +507,8 @@ en:
notice_unavail: All selected articles were set to be unavailable.
update_sync:
notice: All articles and prices were updated.
+ complete_units_migration:
+ notice: Articles successfully migrated
destroy_active_article:
drop: delete
note: "%{article} is used in current orders and can not be deleted Please first ... the article from orders %{drop_link}."
@@ -496,14 +520,12 @@ en:
form:
title_edit: Edit article
title_new: Add new article
- import_search_results:
- action_import: import
- already_imported: imported
- not_found: No articles found
+ per: per
index:
change_supplier: Change supplier ...
download: Download articles
edit_all: Edit all
+ migrate_units: Migrate old article units
ext_db:
import: Import article
sync: Synchronise
@@ -517,6 +539,23 @@ en:
search_placeholder: Name ...
title: Articles from %{supplier} (%{count})
upload: Upload articles
+ prepare_units_migration:
+ explanation: |
+ TODO: General explanation
+ It's highly recommended to download a backup before continuing:
+ title: Migrate articles of %{supplier}
+ download: Download articles backup
+ migrate_units:
+ title: Migrate articles of %{supplier}
+ explanation: |
+ This will try to migrate articles to their new, more versatile format. You may adapt the suggested changes before continuing if you see fit.
+ TODO: Expand on this explanation - maybe link to some docs
+ column_headers:
+ apply: Convert
+ original_plaintext_unit: Original plaintext unit
+ affected_articles: Affected articles
+ contains: Contains
+ run_migration: Run migration now
model:
error_in_use: "%{article} can not be deleted because the article is part of a current order!"
error_nosel: You have selected no articles
@@ -765,6 +804,7 @@ en:
not_found:
text: This page does not appear to exist, sorry!
title: Page not found
+ step_error: Enter a number >=%{min}, which is a multiple of %{granularity}.
feedback:
create:
notice: Your feedback was sent successfully. Thanks a lot!
@@ -1121,6 +1161,10 @@ en:
tasks:
required_users: "%{count} members are still needed!"
task_title: "%{name} (%{duration}h)"
+ unit_conversion_fields:
+ title: Convert units
+ cancel: Cancel
+ apply: Apply
home:
apple_bar:
desc: 'This shows the proportion of completed tasks to the volume of orders for your ordergroup in comparison to the average of the Foodcoop. In practice: for every %{amount} of total orders, you should execute a task!'
@@ -1414,6 +1458,7 @@ en:
stock: Stock
suppliers: Suppliers/articles
title: Articles
+ article_units: Article units
dashboard: Dashboard
finances:
accounts: Manage accounts
@@ -1592,6 +1637,14 @@ en:
ordered_desc: Number of articles as ordered by member (amount + tolerance)
received: Received
received_desc: Number of articles that (will be) received by member
+ article_fields_units:
+ custom_unit: Custom
+ unit: Unit
+ unit_ratios: Unit ratios
+ add_ratio: Add ratio
+ article_unit_ratio:
+ remove: Remove ratio
+ per: per
articles_by:
price: Total price
price_sum: Sum
@@ -1637,8 +1690,8 @@ en:
error_notification:
default_message: Errors were found. Please check the form.
hints:
- article:
- unit: e.g. KG or 1L or 500g
+ # article:
+ # unit: e.g. KG or 1L or 500g
article_category:
description: comma-separated list of category names recognised at import/synchronisation
order_article:
@@ -1788,8 +1841,19 @@ en:
last_orders: Last orders
new_delivery: New delivery
show_deliveries: Show all
+ share: Share
+ remove_share: Remove share
+ confirm_remove_share: Do you really want to stop sharing this supplier's articles? (If you later decide to start sharing them again, you'll have to resend the new link to all subscribers who you think should have access.)
update:
notice: Supplier was updated
+ import_search_results:
+ action_import: import
+ already_imported: imported
+ not_found: No articles found
+ supplier_shares:
+ share_link:
+ not_shared: Supplier's articles are currently not shared with anyone.
+ share_link_copied_to_clipboard: Share link has been copied to the clipboard.
tasks:
accept:
notice: You have accepted the task
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 620ec3bb..ca4de0be 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -28,7 +28,7 @@ es:
article_category:
description: Importar nombres
name: Nombre
- article_price:
+ article_version:
deposit: Depósito
price: Precio (neto)
tax: IVA
@@ -268,7 +268,7 @@ es:
all_ordergroups: Todos los grupos de pedido
all_users: Todos los usuarios
all_workgroups: Todos los grupos de trabajo
- created_at: creado
+ created_at: creado
first_paragraph: Aquí puedes administrar grupos y usuarios de Foodsoft.
groupname: nombre del grupo
members: miembros
@@ -513,7 +513,7 @@ es:
status: Estado (x=saltar)
file_label: Por favor elige un archivo compatible
options:
- convert_units: Mantener unidades actuales, recomputar la cantidad y precio de unidades (como sincronizar).
+ convert_units: Mantener unidades actuales, recomputar la cantidad y precio de unidades (como sincronizar).
outlist_absent: Borrar artículos que no están en el archivo subido.
sample:
juices: Jugos
@@ -1014,7 +1014,7 @@ es:
changes_saved: Guarda los cambios.
index:
my_ordergroup:
- last_update: La última actualización fue hace %{when}
+ last_update: La última actualización fue hace %{when}
title: Mi grupo de pedido
transactions:
title: Últimas transacciones
@@ -1103,7 +1103,7 @@ es:
error_invite_invalid: Tu invitación no es válida.
error_token_invalid: La sesión ha expirado o no es válida. Prueba de nuevo.
reset_password:
- notice: Si tu email está ya registrado aquí, recibirás un mensaje con un enlace para
+ notice: Si tu email está ya registrado aquí, recibirás un mensaje con un enlace para
update_password:
notice: Tu contraseña ha sido actualizada. Prueba a conectarte ahora.
forgot_password:
@@ -1469,7 +1469,7 @@ es:
copy:
title: Copia artículo de stock
create:
- notice: Se ha creado el nuevo producto en stock "%{name}"
+ notice: Se ha creado el nuevo producto en stock "%{name}"
derive:
title: Añade un artículo en stock desde plantilla
destroy:
@@ -1577,7 +1577,7 @@ es:
accept_task: Aceptar tarea
confirm_delete_group: Estás seguro/a de que quieres borrar esta tarea y todas las tareas subsecuentes?
confirm_delete_single: Estás seguro/a de que quieres borrar esta tarea?
- confirm_delete_single_from_group: Estás seguro/a de que quieres borrar esta tarea (y mantener las tareas recurrentes relacionadas)?
+ confirm_delete_single_from_group: Estás seguro/a de que quieres borrar esta tarea (y mantener las tareas recurrentes relacionadas)?
delete_group: Borrar esta tarea y las subsecuentes
edit_group: Edita recurrencia
mark_done: Marca tarea como hecha
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 4dbdb864..d029b380 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -14,7 +14,7 @@ fr:
gross_price: Prix TTC
manufacturer: Product-rice-eur
name: Nom
- order_number: Numéro
+ order_number: Numéro
order_number_short: Numéro
origin: Lieu de production
price: Prix HT
@@ -27,7 +27,7 @@ fr:
article_category:
description: Description
name: Nom
- article_price:
+ article_version:
deposit: Consigne
price: Prix HT
tax: TVA
@@ -860,7 +860,7 @@ fr:
error_invite_invalid: Ton invitation n'est pas ou plus valide.
error_token_invalid: Ton jeton de connexion n'est pas ou plus valide, essaie de cliquer à nouveau sur le lien.
reset_password:
- notice: Tu vas maintenant recevoir un message contenant un lien qui te permettra de réinitialiser ton mot de passe.
+ notice: Tu vas maintenant recevoir un message contenant un lien qui te permettra de réinitialiser ton mot de passe.
update_password:
notice: Ton mot de passe a été mis à jour. Tu peux maintenant de connecter.
forgot_password:
@@ -1093,7 +1093,7 @@ fr:
closed: décomptée
finished: clôturée
open: en cours
- received: reçu
+ received: reçu
update:
notice: La commande a été mise à jour.
update_order_amounts:
@@ -1344,7 +1344,7 @@ fr:
notice: La description du boulot a été mise à jour.
notice_converted: Le boulot a été converti en boulot ordinaire (sans répétition).
user:
- more: Tu t'ennuies en ce moment? Il y aura sûrement du boulot pour toi %{tasks_link}.
+ more: Tu t'ennuies en ce moment? Il y aura sûrement du boulot pour toi %{tasks_link}.
tasks_link: par là-bas
title: Ton boulot
title_accepted: Boulots acceptés
@@ -1369,7 +1369,7 @@ fr:
edit:
title: Modifier l'équipe
error_last_admin_group: Impossible de supprimer la dernière cellule avec privilèges administratrices.
- error_last_admin_role: Les privilèges administratrices ne peuvent pas être retirés à la dernière cellule qui les possède.
+ error_last_admin_role: Les privilèges administratrices ne peuvent pas être retirés à la dernière cellule qui les possède.
index:
title: Équipes
update:
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 4c97dda4..698523aa 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -28,7 +28,7 @@ nl:
article_category:
description: Import namen
name: Naam
- article_price:
+ article_version:
deposit: Statiegeld
price: Netto prijs
tax: BTW
diff --git a/config/navigation.rb b/config/navigation.rb
index c8b7d088..c671c18c 100644
--- a/config/navigation.rb
+++ b/config/navigation.rb
@@ -21,31 +21,43 @@
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
+ subnav.item :article_units, I18n.t('navigation.articles.article_units'), article_units_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 c341524a..a7a3c1d3 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -4,16 +4,16 @@
# 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 }
# 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
@@ -21,7 +21,7 @@
# 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
@@ -53,3 +53,8 @@
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
+
+if ENV.fetch('RAILS_ENV') == 'development'
+ Rails.logger.info 'development => worker_timeout 3600'
+ worker_timeout 3600
+end
diff --git a/config/routes.rb b/config/routes.rb
index d1aa8886..4f54918b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,9 +1,9 @@
Rails.application.routes.draw do
- get "order_comments/new"
+ 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 +21,8 @@
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 +62,11 @@
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 +88,7 @@
resources :ordergroups, only: [:index]
- resources :workgroups, only: [:index, :edit, :update]
+ resources :workgroups, only: %i[index edit update]
end
########### Article management
@@ -111,8 +111,15 @@
end
end
+ resources :article_units do
+ collection do
+ get :search
+ end
+ end
+
resources :suppliers do
- get :shared_suppliers, on: :collection
+ get :remote_articles
+ resource :share, only: %i[create destroy], controller: :supplier_shares
resources :deliveries do
collection do
@@ -128,12 +135,13 @@
collection do
post :update_selected
get :edit_all
+ get :prepare_units_migration
+ get :migrate_units
+ post :complete_units_migration
post :update_all
get :upload
post :parse_upload
post :create_from_upload
- get :shared
- get :import
post :sync
post :update_synchronized
end
@@ -172,7 +180,7 @@
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 +190,10 @@
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 +207,14 @@
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 +226,7 @@
resources :bank_transactions, as: :transactions
end
- resources :bank_transactions, only: [:index, :show]
+ resources :bank_transactions, only: %i[index show]
end
########### Administration
@@ -246,11 +258,11 @@
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
@@ -265,23 +277,27 @@
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]
+
+ resources :shared_suppliers, param: :uuid, only: [] do
+ resources :articles, only: [:index]
+ end
end
end
############## Feedback
- resource :feedback, only: [:new, :create], controller: 'feedback'
+ resource :feedback, only: %i[new create], controller: 'feedback'
############## The rest
diff --git a/config/schedule.rb b/config/schedule.rb
index f22c1348..72e3cbcc 100644
--- a/config/schedule.rb
+++ b/config/schedule.rb
@@ -2,22 +2,22 @@
# 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"
+every :sunday, at: '7:14 am' do
+ rake 'multicoops:run TASK=foodsoft:create_upcoming_periodic_tasks'
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/config/units-of-measure/locales/unece_de.yml b/config/units-of-measure/locales/unece_de.yml
new file mode 100644
index 00000000..2719abfb
--- /dev/null
+++ b/config/units-of-measure/locales/unece_de.yml
@@ -0,0 +1,763 @@
+# This file has been autogenerated by `rake units:build_i18n_from_csv`
+---
+unece_units:
+ XCU:
+ name: Becher
+ XCN:
+ name: Behälter
+ XSH:
+ name: Beutel
+ X43:
+ name: Bigbag
+ XST:
+ name: Blatt
+ abbreviation: Bl.
+ XOK:
+ name: Block
+ XVA:
+ name: Bottich
+ XBX:
+ name: Box
+ XBH:
+ name: Bund
+ abbreviation: Bd.
+ aliases:
+ - bd
+ XBE:
+ name: Bündel
+ abbreviation: Bdl.
+ XCX:
+ name: Dose
+ XBJ:
+ name: Eimer
+ XUN:
+ name: Einheit
+ XOS:
+ name: Einweg-Palette
+ XDH:
+ name: Eurobox, CHEP-Box
+ abbreviation: Box
+ XBA:
+ name: Fass
+ XFI:
+ name: Fässchen
+ XBO:
+ name: Flasche
+ abbreviation: Fl.
+ aliases:
+ - fl
+ XBQ:
+ name: Flasche in Umverpackung
+ abbreviation: Fl.
+ XFB:
+ name: Flexibag
+ XFT:
+ name: Foodtainer
+ XJR:
+ name: Glas
+ abbreviation: Gl.
+ aliases:
+ - gl
+ XGR:
+ name: Glasgefäß
+ XOW:
+ name: Großer Sack in Palettengröße
+ X8B:
+ name: Holzkiste
+ XCV:
+ name: Hülle
+ XWA:
+ name: Intermediate Bulk Container (IBC)
+ abbreviation: IBC
+ XEI:
+ name: Isotherme Box
+ XJT:
+ name: Jutebeutel
+ XGY:
+ name: Jutesack
+ XJY:
+ name: Kanister
+ XBD:
+ name: Karton
+ XCR:
+ name: Kiste
+ XAI:
+ name: Klappverpackung
+ XPA:
+ name: Kollo
+ XBK:
+ name: Korb
+ XBI:
+ name: Kübel
+ XOV:
+ name: Mehrwegpalette
+ XNT:
+ name: Netz
+ XPK:
+ name: Packung
+ abbreviation: Pkg.
+ aliases:
+ - pkg
+ - pkt
+ XPC:
+ name: Paket
+ XPX:
+ name: Palette
+ X5M:
+ name: Papiertüte
+ XPR:
+ name: Plastikgefäß
+ XEC:
+ name: Plastiktüte
+ X6H:
+ name: Plastik-Verbundverpackung
+ X44:
+ name: Polybeutel
+ XBR:
+ name: Riegel
+ XCW:
+ name: Rollcontainer
+ XBT:
+ name: Rolle
+ XSA:
+ name: Sack
+ XBM:
+ name: Schüssel
+ XSX:
+ name: Set
+ XDN:
+ name: Spender
+ XAE:
+ name: Sprühdose
+ XSC:
+ name: Steige
+ XLU:
+ name: Stiege
+ X5L:
+ name: Stofftüte
+ XPP:
+ name: Stück
+ aliases:
+ - stk
+ - st
+ - stueck
+ XPU:
+ name: Schale
+ XBG:
+ name: Tasche
+ XP2:
+ name: Tiegel
+ XCK:
+ name: Tonne
+ XPT:
+ name: Topf
+ aliases:
+ - tiegel
+ XGI:
+ name: Träger
+ XTU:
+ name: Tube
+ PTN:
+ name: Portion
+ long_name: Portion
+ symbols: []
+ STC:
+ name: Stange
+ long_name: Stange
+ symbols: []
+ DMT:
+ name: Dezimeter
+ long_name: Dezimeter
+ symbols:
+ - dm
+ CMT:
+ name: Zentimeter
+ long_name: Zentimeter
+ symbols:
+ - cm
+ 4H:
+ name: Mikrometer
+ long_name: Mikrometer (Mikron)
+ symbols:
+ - µm
+ MMT:
+ name: Millimeter
+ long_name: Millimeter
+ symbols:
+ - mm
+ HMT:
+ name: Hektometer
+ long_name: Hektometer
+ symbols:
+ - hm
+ KMT:
+ name: Kilometer
+ long_name: Kilometer
+ symbols:
+ - km
+ A45:
+ name: Dekameter
+ long_name: Dekameter
+ symbols:
+ - dam
+ INH:
+ name: Zoll
+ long_name: Zoll
+ symbols:
+ - "″"
+ - in
+ FOT:
+ name: Fuß
+ long_name: Fuß
+ symbols:
+ - ft
+ YRD:
+ name: Yard
+ long_name: Yard
+ symbols:
+ - yd
+ SMI:
+ name: Meile
+ long_name: Meile (britisch)
+ symbols:
+ - mile
+ '77':
+ name: Millizoll
+ long_name: Millizoll
+ symbols:
+ - mil
+ MTK:
+ name: Quadratmeter
+ long_name: Quadratmeter
+ symbols:
+ - m²
+ - qm
+ DAA:
+ name: Dekar
+ long_name: Dekar
+ symbols:
+ - daa
+ KMK:
+ name: Quadratkilometer
+ long_name: Quadratkilometer
+ symbols:
+ - km²
+ CMK:
+ name: Quadratzentimeter
+ long_name: Quadratzentimeter
+ symbols:
+ - cm²
+ DMK:
+ name: Quadratdezimeter
+ long_name: Quadratdezimeter
+ symbols:
+ - dm²
+ H16:
+ name: Quadratdekameter
+ long_name: Quadratdekameter
+ symbols:
+ - dam²
+ H18:
+ name: Quadrathektometer
+ long_name: Quadrathektometer
+ symbols:
+ - hm²
+ MMK:
+ name: Quadratmillimeter
+ long_name: Quadratmillimeter
+ symbols:
+ - mm²
+ M47:
+ name: Circular mil
+ long_name: Circular mil
+ symbols:
+ - cmil
+ ARE:
+ name: Ar
+ long_name: Ar
+ symbols:
+ - a
+ HAR:
+ name: Hektar
+ long_name: Hektar
+ symbols:
+ - ha
+ INK:
+ name: Quadratzoll
+ long_name: Quadratzoll
+ symbols:
+ - in²
+ FTK:
+ name: Quadratfuß
+ long_name: Quadratfuß
+ symbols:
+ - ft²
+ YDK:
+ name: Quadratmeter
+ long_name: Quadratmeter
+ symbols:
+ - yd²
+ MIK:
+ name: Quadratmeile
+ long_name: Quadratmeile (britisch)
+ symbols:
+ - mi²
+ ACR:
+ name: Morgen
+ long_name: Morgen
+ symbols:
+ - acre
+ KGM:
+ name: Kilogramm
+ long_name: Kilogramm
+ symbols:
+ - kg
+ aliases:
+ - kilo
+ HGM:
+ name: Hektogramm
+ long_name: Hektogramm
+ symbols:
+ - hg
+ KTN:
+ name: Kilotonne
+ long_name: Kilotonne
+ symbols:
+ - kt
+ DJ:
+ name: Dekagramm
+ long_name: Dekagramm
+ symbols:
+ - dag
+ - dkg
+ DG:
+ name: Dezigramm
+ long_name: Dezigramm
+ symbols:
+ - dg
+ CGM:
+ name: Zentigramm
+ long_name: Zentigramm
+ symbols:
+ - cg
+ DTN:
+ name: Dezitonne
+ long_name: Dezitonne
+ symbols:
+ - dt
+ - dtn
+ MGM:
+ name: Milligramm
+ long_name: Milligramm
+ symbols:
+ - mg
+ 2U:
+ name: Megagramm
+ long_name: Megagramm
+ symbols:
+ - Mg
+ MC:
+ name: Mikrogramm
+ long_name: Mikrogramm
+ symbols:
+ - µg
+ GRM:
+ name: Gramm
+ long_name: Gramm
+ symbols:
+ - g
+ aliases:
+ - gr
+ - grm
+ TNE:
+ name: Tonne
+ long_name: Tonne (metrisch)
+ symbols:
+ - t
+ M86:
+ name: Pfund
+ long_name: Pfund
+ symbols:
+ - pfd
+ LBR:
+ name: Englisches Pfund
+ long_name: Englisches Pfund
+ symbols:
+ - lb
+ GRN:
+ name: Gran
+ long_name: Gran
+ symbols:
+ - gr
+ ONZ:
+ name: Unze
+ long_name: Unze (avoirdupois)
+ symbols:
+ - oz
+ CWI:
+ name: Br. Zentner
+ long_name: Hundredweight (britisch)
+ symbols:
+ - cwt
+ CWA:
+ name: Am. Zentner
+ long_name: Hundredweight (amerikanisch)
+ symbols:
+ - cwt
+ LTN:
+ name: Br. Tonne
+ long_name: Britische Tonne, Long Ton (amerikanisch)
+ symbols:
+ - ton (Br.)
+ STI:
+ name: Stein
+ long_name: Stein (britisch)
+ symbols:
+ - st
+ STN:
+ name: Am. Tonne
+ long_name: Amerikanische Tonne, Short Ton
+ symbols:
+ - ton (Am.)
+ APZ:
+ name: Feinunze
+ long_name: Feinunze oder Apothekerunze
+ symbols:
+ - tr oz
+ F13:
+ name: Slug
+ long_name: Slug
+ symbols:
+ - slug
+ SEC:
+ name: Sekunde
+ long_name: Sekunde [Zeiteinheit]
+ symbols:
+ - s
+ MIN:
+ name: Minute
+ long_name: Minute [Zeiteinheit]
+ symbols:
+ - min
+ HUR:
+ name: Stunde
+ long_name: Stunde
+ symbols:
+ - h
+ DAY:
+ name: Tag
+ long_name: Tag
+ symbols:
+ - d
+ C26:
+ name: Millisekunde
+ long_name: Millisekunde
+ symbols:
+ - ms
+ WEE:
+ name: Woche
+ long_name: Woche
+ symbols:
+ - wk
+ MON:
+ name: Monat
+ long_name: Monat
+ symbols:
+ - mo
+ ANN:
+ name: Jahr
+ long_name: Jahr
+ symbols:
+ - y
+ MTQ:
+ name: Kubikmeter
+ long_name: Kubikmeter
+ symbols:
+ - m³
+ - cbm
+ LTR:
+ name: Liter
+ long_name: Liter
+ symbols:
+ - l
+ MAL:
+ name: Megaliter
+ long_name: Megaliter
+ symbols:
+ - Ml
+ DLT:
+ name: Deziliter
+ long_name: Deziliter
+ symbols:
+ - dl
+ 4G:
+ name: Mikroliter
+ long_name: Mikroliter
+ symbols:
+ - µl
+ K6:
+ name: Kiloliter
+ long_name: Kiloliter
+ symbols:
+ - kl
+ A44:
+ name: Dekaliter
+ long_name: Dekaliter
+ symbols:
+ - dal
+ MMQ:
+ name: Kubikmillimeter
+ long_name: Kubikmillimeter
+ symbols:
+ - mm³
+ CMQ:
+ name: Kubikzentimeter
+ long_name: Kubikzentimeter
+ symbols:
+ - cm³
+ - ccm
+ DMQ:
+ name: Kubikdezimeter
+ long_name: Kubikdezimeter
+ symbols:
+ - dm³
+ MLT:
+ name: Milliliter
+ long_name: Milliliter
+ symbols:
+ - ml
+ HLT:
+ name: Hektoliter
+ long_name: Hektoliter
+ symbols:
+ - hl
+ CLT:
+ name: Zentiliter
+ long_name: Zentiliter
+ symbols:
+ - cl
+ DMA:
+ name: Kubikdekameter
+ long_name: Kubikdekameter
+ symbols:
+ - dam³
+ H19:
+ name: Kubikhektometer
+ long_name: Kubikhektometer
+ symbols:
+ - hm³
+ H20:
+ name: Kubikkilometer
+ long_name: Kubikkilometer
+ symbols:
+ - km³
+ M67:
+ name: acre-foot
+ long_name: acre-foot (basierend auf U.S. Survey Foot)
+ symbols:
+ - acre-ft
+ M68:
+ name: Cord
+ long_name: Cord (128 ft3)
+ symbols:
+ - cord
+ M69:
+ name: Kubikmeile
+ long_name: Kubikmeile (britisch)
+ symbols:
+ - mi³
+ M70:
+ name: Registertonne
+ long_name: Registertonne
+ symbols:
+ - RT
+ 5I:
+ name: Kubikfuß
+ long_name: Kubikfuß
+ symbols:
+ - std
+ INQ:
+ name: Kubikzoll
+ long_name: Kubikzoll
+ symbols:
+ - in³
+ FTQ:
+ name: Kubikfuß
+ long_name: Kubikfuß
+ symbols:
+ - ft³
+ YDQ:
+ name: Kubikmeter
+ long_name: Kubikmeter
+ symbols:
+ - yd³
+ GLI:
+ name: Gallone (Br.)
+ long_name: Gallone (britisch)
+ symbols:
+ - gal
+ GLL:
+ name: Gallone (Am.)
+ long_name: Gallone (amerikanisch)
+ symbols:
+ - gal
+ PTI:
+ name: Pint (Br.)
+ long_name: Pint (britisch)
+ symbols:
+ - pt
+ QTI:
+ name: Quart
+ long_name: quart (britisch)
+ symbols:
+ - qt
+ PTL:
+ name: flüssiges Pint (Am.)
+ long_name: flüssiges Pint (amerikanisch)
+ symbols:
+ - liq pt
+ QTL:
+ name: flüssiges Quart (Am.)
+ long_name: flüssiges Quart (amerikanisch)
+ symbols:
+ - liq qt
+ PTD:
+ name: trockenes Pint (Am.)
+ long_name: trockenes Pint (amerikanisch)
+ symbols:
+ - dry pt
+ OZI:
+ name: flüssige Unze (Br.)
+ long_name: flüssige Unze (britisch)
+ symbols:
+ - fl oz
+ J57:
+ name: Barrel (Br.)
+ long_name: Barrel (britisch)
+ symbols:
+ - bbl
+ L43:
+ name: Viertelscheffel (Br.)
+ long_name: peck (britisch)
+ symbols:
+ - pk
+ L84:
+ name: Shipping ton (Br.)
+ long_name: ton (UK shipping)
+ symbols:
+ - Shipping ton (Br.)
+ L86:
+ name: Shipping ton (Am.)
+ long_name: ton (US shipping)
+ symbols:
+ - Shipping ton (Am.)
+ OZA:
+ name: Flüssigunze
+ long_name: Flüssigunze (amerikanisch)
+ symbols:
+ - fl oz
+ BUI:
+ name: Scheffel (Br.)
+ long_name: Scheffel (britisch)
+ symbols:
+ - bushel
+ BUA:
+ name: Scheffel (Am.)
+ long_name: Scheffel (amerikanisch)
+ symbols:
+ - bu
+ BLL:
+ name: Barrel (Am.)
+ long_name: Barrel (amerikanisch)
+ symbols:
+ - barrel
+ BLD:
+ name: Dry Barrel (Am.)
+ long_name: Dry Barrel (amerikanisch)
+ symbols:
+ - bbl
+ GLD:
+ name: Dry Gallon (Am.)
+ long_name: Dry Gallon (amerikanisch)
+ symbols:
+ - dry gal
+ QTD:
+ name: Dry Quart (Am.)
+ long_name: Dry Quart (amerikanisch)
+ symbols:
+ - dry qt
+ G26:
+ name: Ster
+ long_name: Ster (Schüttraummeter)
+ symbols:
+ - st
+ G21:
+ name: Tasse
+ long_name: Tasse [Volumeneinheit]
+ symbols:
+ - cup
+ G24:
+ name: Esslöffel
+ long_name: Esslöffel
+ symbols:
+ - EL
+ G25:
+ name: Teelöffel
+ long_name: Teelöffel
+ symbols:
+ - TL
+ G23:
+ name: Viertelscheffel (Am.)
+ long_name: Viertelscheffel (amerikanisch)
+ symbols:
+ - pk
+ JOU:
+ name: Joule
+ long_name: Joule
+ symbols:
+ - J
+ J75:
+ name: Kalorie
+ long_name: Kalorie
+ symbols:
+ - cal
+ K51:
+ name: Kilokalorie
+ long_name: Kilokalorie
+ symbols:
+ - kcal
+ J55:
+ name: Wattsekunde
+ long_name: Wattsekunde
+ symbols:
+ - Ws
+ WHR:
+ name: Wattstunde
+ long_name: Wattstunde
+ symbols:
+ - Wh
+ KWH:
+ name: Kilowattstunde
+ long_name: Kilowattstunde
+ symbols:
+ - kWh
+ GWH:
+ name: Gigawattstunde
+ long_name: Gigawattstunde
+ symbols:
+ - GWh
+ WTT:
+ name: Watt
+ long_name: Watt
+ symbols:
+ - W
+ KWT:
+ name: Kilowatt
+ long_name: Kilowatt
+ symbols:
+ - kW
+ MAW:
+ name: Megawatt
+ long_name: Megawatt
+ symbols:
+ - MW
diff --git a/config/units-of-measure/locales/unece_en.yml b/config/units-of-measure/locales/unece_en.yml
new file mode 100644
index 00000000..f6eb8730
--- /dev/null
+++ b/config/units-of-measure/locales/unece_en.yml
@@ -0,0 +1,761 @@
+# This file has been autogenerated by `rake units:build_i18n_from_csv`
+---
+unece_units:
+ XCU:
+ name: Cup
+ XCN:
+ name: Container
+ XSH:
+ name: Sachet
+ X43:
+ name: Big bag
+ XST:
+ name: Sheet
+ XOK:
+ name: Block
+ XVA:
+ name: Vat
+ XBX:
+ name: Box
+ XBH:
+ name: Bunch
+ XBE:
+ name: Bundle
+ abbreviation: bdl.
+ XCX:
+ name: Can
+ XBJ:
+ name: Bucket
+ XUN:
+ name: Unit
+ XOS:
+ name: Oneway pallet
+ XDH:
+ name: Eurobox, CHEP box
+ abbreviation: box
+ XBA:
+ name: Barrel
+ XFI:
+ name: Firkin
+ XBO:
+ name: Bottle
+ abbreviation: btl.
+ aliases:
+ - btl
+ XBQ:
+ name: Protected bottle
+ abbreviation: btl.
+ XFB:
+ name: Flexibag
+ XFT:
+ name: Foodtainer
+ XJR:
+ name: Jar
+ XGR:
+ name: Glass receptacle
+ XOW:
+ name: Pallet sized large bag
+ X8B:
+ name: Wooden crate
+ XCV:
+ name: Cover
+ XWA:
+ name: Intermediate bulk container (IBC)
+ abbreviation: IBC
+ XEI:
+ name: Isothermic case
+ XJT:
+ name: Jutebag
+ XGY:
+ name: Gunny bag
+ XJY:
+ name: Jerrican
+ XBD:
+ name: Board
+ XCR:
+ name: Crate
+ XAI:
+ name: Clamshell
+ XPA:
+ name: Packet
+ XBK:
+ name: Basket
+ XBI:
+ name: Bin
+ XOV:
+ name: Returnable pallet
+ XNT:
+ name: Net
+ XPK:
+ name: Package
+ abbreviation: pkg.
+ aliases:
+ - pkg
+ XPC:
+ name: Parcel
+ XPX:
+ name: Pallet
+ X5M:
+ name: Paper bag
+ XPR:
+ name: Plastic receptacle
+ XEC:
+ name: Plastic bag
+ X6H:
+ name: Plastic composite packaging
+ X44:
+ name: Polybag
+ XBR:
+ name: Bar
+ XCW:
+ name: Roll cage
+ XBT:
+ name: Bolt
+ XSA:
+ name: Sack
+ XBM:
+ name: Bowl
+ XSX:
+ name: Set
+ XDN:
+ name: Dispenser
+ XAE:
+ name: Aerosol
+ XSC:
+ name: Shallow crate
+ XLU:
+ name: Lug
+ X5L:
+ name: Textile bag
+ XPP:
+ name: Piece
+ aliases:
+ - pc
+ XPU:
+ name: Tray
+ XBG:
+ name: Bag
+ XP2:
+ name: Pan
+ XCK:
+ name: Cask
+ XPT:
+ name: Pot
+ XGI:
+ name: Girder
+ XTU:
+ name: Tube
+ PTN:
+ name: portion
+ long_name: portion
+ symbols: []
+ STC:
+ name: stick
+ long_name: stick
+ symbols: []
+ DMT:
+ name: decimetre
+ long_name: decimetre
+ symbols:
+ - dm
+ CMT:
+ name: centimetre
+ long_name: centimetre
+ symbols:
+ - cm
+ 4H:
+ name: micrometre
+ long_name: micrometre (micron)
+ symbols:
+ - µm
+ MMT:
+ name: millimetre
+ long_name: millimetre
+ symbols:
+ - mm
+ HMT:
+ name: hectometre
+ long_name: hectometre
+ symbols:
+ - hm
+ KMT:
+ name: kilometre
+ long_name: kilometre
+ symbols:
+ - km
+ A45:
+ name: decametre
+ long_name: decametre
+ symbols:
+ - dam
+ INH:
+ name: inch
+ long_name: inch
+ symbols:
+ - in
+ FOT:
+ name: foot
+ long_name: foot
+ symbols:
+ - ft
+ YRD:
+ name: yard
+ long_name: yard
+ symbols:
+ - yd
+ SMI:
+ name: mile
+ long_name: mile (statute mile)
+ symbols:
+ - mile
+ '77':
+ name: milli-inch
+ long_name: milli-inch
+ symbols:
+ - mil
+ MTK:
+ name: square metre
+ long_name: square metre
+ symbols:
+ - m²
+ - sq m
+ DAA:
+ name: decare
+ long_name: decare
+ symbols:
+ - daa
+ KMK:
+ name: square kilometre
+ long_name: square kilometre
+ symbols:
+ - km²
+ - sq km
+ CMK:
+ name: square centimetre
+ long_name: square centimetre
+ symbols:
+ - cm²
+ - sq cm
+ DMK:
+ name: square decimetre
+ long_name: square decimetre
+ symbols:
+ - dm²
+ - sq dm
+ H16:
+ name: square decametre
+ long_name: square decametre
+ symbols:
+ - dam²
+ - sq dam
+ H18:
+ name: square hectometre
+ long_name: square hectometre
+ symbols:
+ - hm²
+ - sq hm
+ MMK:
+ name: square millimetre
+ long_name: square millimetre
+ symbols:
+ - mm²
+ - sq mm
+ M47:
+ name: circular mil
+ long_name: circular mil
+ symbols:
+ - cmil
+ ARE:
+ name: are
+ long_name: are
+ symbols:
+ - a
+ HAR:
+ name: hectare
+ long_name: hectare
+ symbols:
+ - ha
+ INK:
+ name: square inch
+ long_name: square inch
+ symbols:
+ - in²
+ - sq in
+ FTK:
+ name: square foot
+ long_name: square foot
+ symbols:
+ - ft²
+ - sq ft
+ YDK:
+ name: square yard
+ long_name: square yard
+ symbols:
+ - yd²
+ - sq yd
+ MIK:
+ name: square mile
+ long_name: square mile (statute mile)
+ symbols:
+ - mi²
+ - sq mi
+ ACR:
+ name: acre
+ long_name: acre
+ symbols:
+ - acre
+ KGM:
+ name: kilogram
+ long_name: kilogram
+ symbols:
+ - kg
+ aliases:
+ - kilo
+ HGM:
+ name: hectogram
+ long_name: hectogram
+ symbols:
+ - hg
+ KTN:
+ name: kilotonne
+ long_name: kilotonne
+ symbols:
+ - kt
+ DJ:
+ name: decagram
+ long_name: decagram
+ symbols:
+ - dag
+ - dkg
+ DG:
+ name: decigram
+ long_name: decigram
+ symbols:
+ - dg
+ CGM:
+ name: centigram
+ long_name: centigram
+ symbols:
+ - cg
+ DTN:
+ name: decitonne
+ long_name: decitonne
+ symbols:
+ - dt
+ - dtn
+ MGM:
+ name: milligram
+ long_name: milligram
+ symbols:
+ - mg
+ 2U:
+ name: megagram
+ long_name: megagram
+ symbols:
+ - Mg
+ MC:
+ name: microgram
+ long_name: microgram
+ symbols:
+ - µg
+ GRM:
+ name: gram
+ long_name: gram
+ symbols:
+ - g
+ aliases:
+ - gr
+ - grm
+ TNE:
+ name: metric ton
+ long_name: tonne (metric)
+ symbols:
+ - t
+ - mt
+ M86:
+ name: pfund
+ long_name: pfund
+ symbols:
+ - pfd
+ LBR:
+ name: pound
+ long_name: pound
+ symbols:
+ - lb
+ GRN:
+ name: grain
+ long_name: grain
+ symbols:
+ - gr
+ ONZ:
+ name: ounce
+ long_name: ounce (avoirdupois)
+ symbols:
+ - oz
+ CWI:
+ name: hundred weight
+ long_name: hundred weight (UK)
+ symbols:
+ - cwt
+ CWA:
+ name: hundred pound (cwt) / hundred weight (US)
+ long_name: hundred pound (cwt) / hundred weight (US)
+ symbols:
+ - cwt
+ LTN:
+ name: ton (UK) or long ton (US)
+ long_name: ton (UK) or long ton (US)
+ symbols:
+ - ton (UK)
+ STI:
+ name: stone
+ long_name: stone (UK)
+ symbols:
+ - st
+ STN:
+ name: ton (US) or short ton (UK/US)
+ long_name: ton (US) or short ton (UK/US)
+ symbols:
+ - ton (US)
+ APZ:
+ name: troy ounce
+ long_name: troy ounce or apothecary ounce
+ symbols:
+ - tr oz
+ F13:
+ name: slug
+ long_name: slug
+ symbols:
+ - slug
+ SEC:
+ name: second
+ long_name: second [unit of time]
+ symbols:
+ - s
+ MIN:
+ name: minute
+ long_name: minute [unit of time]
+ symbols:
+ - min
+ HUR:
+ name: hour
+ long_name: hour
+ symbols:
+ - h
+ DAY:
+ name: day
+ long_name: day
+ symbols:
+ - d
+ C26:
+ name: millisecond
+ long_name: millisecond
+ symbols:
+ - ms
+ WEE:
+ name: week
+ long_name: week
+ symbols:
+ - wk
+ MON:
+ name: month
+ long_name: month
+ symbols:
+ - mo
+ ANN:
+ name: year
+ long_name: year
+ symbols:
+ - y
+ MTQ:
+ name: cubic metre
+ long_name: cubic metre
+ symbols:
+ - m³
+ LTR:
+ name: litre
+ long_name: litre
+ symbols:
+ - l
+ MAL:
+ name: megalitre
+ long_name: megalitre
+ symbols:
+ - Ml
+ DLT:
+ name: decilitre
+ long_name: decilitre
+ symbols:
+ - dl
+ 4G:
+ name: microlitre
+ long_name: microlitre
+ symbols:
+ - µl
+ K6:
+ name: kilolitre
+ long_name: kilolitre
+ symbols:
+ - kl
+ A44:
+ name: decalitre
+ long_name: decalitre
+ symbols:
+ - dal
+ MMQ:
+ name: cubic millimetre
+ long_name: cubic millimetre
+ symbols:
+ - mm³
+ CMQ:
+ name: cubic centimetre
+ long_name: cubic centimetre
+ symbols:
+ - cm³
+ - cc
+ - ccm
+ DMQ:
+ name: cubic decimetre
+ long_name: cubic decimetre
+ symbols:
+ - dm³
+ MLT:
+ name: millilitre
+ long_name: millilitre
+ symbols:
+ - ml
+ HLT:
+ name: hectolitre
+ long_name: hectolitre
+ symbols:
+ - hl
+ CLT:
+ name: centilitre
+ long_name: centilitre
+ symbols:
+ - cl
+ DMA:
+ name: cubic decametre
+ long_name: cubic decametre
+ symbols:
+ - dam³
+ H19:
+ name: cubic hectometre
+ long_name: cubic hectometre
+ symbols:
+ - hm³
+ H20:
+ name: cubic kilometre
+ long_name: cubic kilometre
+ symbols:
+ - km³
+ M67:
+ name: acre-foot
+ long_name: acre-foot (based on U.S. survey foot)
+ symbols:
+ - acre-ft
+ M68:
+ name: cord
+ long_name: cord (128 ft3)
+ symbols:
+ - cord
+ M69:
+ name: cubic mile
+ long_name: cubic mile (UK statute)
+ symbols:
+ - mi³
+ M70:
+ name: register ton
+ long_name: register ton
+ symbols:
+ - RT
+ 5I:
+ name: standard cubic foot
+ long_name: standard cubic foot
+ symbols:
+ - std
+ INQ:
+ name: cubic inch
+ long_name: cubic inch
+ symbols:
+ - in³
+ FTQ:
+ name: cubic foot
+ long_name: cubic foot
+ symbols:
+ - ft³
+ YDQ:
+ name: cubic yard
+ long_name: cubic yard
+ symbols:
+ - yd³
+ GLI:
+ name: gallon (UK)
+ long_name: gallon (UK)
+ symbols:
+ - gal
+ GLL:
+ name: gallon (US)
+ long_name: gallon (US)
+ symbols:
+ - gal
+ PTI:
+ name: pint (UK)
+ long_name: pint (UK)
+ symbols:
+ - pt
+ QTI:
+ name: quart (UK)
+ long_name: quart (UK)
+ symbols:
+ - qt
+ PTL:
+ name: liquid pint (US)
+ long_name: liquid pint (US)
+ symbols:
+ - liq pt
+ QTL:
+ name: liquid quart (US)
+ long_name: liquid quart (US)
+ symbols:
+ - liq qt
+ PTD:
+ name: dry pint (US)
+ long_name: dry pint (US)
+ symbols:
+ - dry pt
+ OZI:
+ name: fluid ounce (UK)
+ long_name: fluid ounce (UK)
+ symbols:
+ - fl oz
+ J57:
+ name: barrel (UK petroleum)
+ long_name: barrel (UK petroleum)
+ symbols:
+ - bbl
+ L43:
+ name: peck (UK)
+ long_name: peck (UK)
+ symbols:
+ - pk (UK)
+ L84:
+ name: ton (UK shipping)
+ long_name: ton (UK shipping)
+ symbols:
+ - British shipping ton
+ L86:
+ name: ton (US shipping)
+ long_name: ton (US shipping)
+ symbols:
+ - "(US) shipping ton"
+ OZA:
+ name: fluid ounce (US)
+ long_name: fluid ounce (US)
+ symbols:
+ - fl oz
+ BUI:
+ name: bushel (UK)
+ long_name: bushel (UK)
+ symbols:
+ - bushel
+ BUA:
+ name: bushel (US)
+ long_name: bushel (US)
+ symbols:
+ - bu
+ BLL:
+ name: barrel (US)
+ long_name: barrel (US)
+ symbols:
+ - barrel
+ BLD:
+ name: dry barrel (US)
+ long_name: dry barrel (US)
+ symbols:
+ - bbl
+ GLD:
+ name: dry gallon (US)
+ long_name: dry gallon (US)
+ symbols:
+ - dry gal
+ QTD:
+ name: dry quart (US)
+ long_name: dry quart (US)
+ symbols:
+ - dry qt
+ G26:
+ name: stere
+ long_name: stere
+ symbols:
+ - st
+ G21:
+ name: cup
+ long_name: cup [unit of volume]
+ symbols:
+ - cup
+ G24:
+ name: tablespoon
+ long_name: tablespoon (US)
+ symbols:
+ - tbsp.
+ G25:
+ name: teaspoon
+ long_name: teaspoon (US)
+ symbols:
+ - tsp.
+ G23:
+ name: peck (US)
+ long_name: peck (US)
+ symbols:
+ - pk (US)
+ JOU:
+ name: joule
+ long_name: joule
+ symbols:
+ - J
+ J75:
+ name: calorie
+ long_name: calorie (mean)
+ symbols:
+ - cal
+ K51:
+ name: kilocalorie
+ long_name: kilocalorie (mean)
+ symbols:
+ - kcal
+ J55:
+ name: watt second
+ long_name: watt second
+ symbols:
+ - Ws
+ WHR:
+ name: watt hour
+ long_name: watt hour
+ symbols:
+ - Wh
+ KWH:
+ name: kilowatt hour
+ long_name: kilowatt hour
+ symbols:
+ - kWh
+ GWH:
+ name: gigawatt hour
+ long_name: gigawatt hour
+ symbols:
+ - GWh
+ WTT:
+ name: watt
+ long_name: watt
+ symbols:
+ - W
+ KWT:
+ name: kilowatt
+ long_name: kilowatt
+ symbols:
+ - kW
+ MAW:
+ name: megawatt
+ long_name: megawatt
+ symbols:
+ - MW
diff --git a/config/units-of-measure/translations_piece.csv b/config/units-of-measure/translations_piece.csv
new file mode 100644
index 00000000..d4df81de
--- /dev/null
+++ b/config/units-of-measure/translations_piece.csv
@@ -0,0 +1,69 @@
+Common code,Original name,English suggestion,Engl. abbr.,Engl. Aliases,German suggestion,German abbr.,German aliases,Description
+XCU,Cup,Cup,,,Becher,,,
+XCN,"Container, not otherwise specified as transport equipment",Container,,,Behälter,,,
+XSH,Sachet ,Sachet ,,,Beutel,,,
+X43,"Bag, super bulk",Big bag,,,Bigbag,,,A cloth plastic or paper based bag having the dimensions of the pallet on which it is constructed.
+XST,Sheet,Sheet,,,Blatt,Bl.,,
+XOK,Block,Block,,,Block,,,"A solid piece of a hard substance, such as granite, having one or more flat sides."
+XVA,Vat,Vat,,,Bottich,,,
+XBX,Box,Box,,,Box,,,
+XBH,Bunch,Bunch,,,Bund,Bd.,bd,
+XBE,Bundle ,Bundle ,bdl.,,Bündel ,Bdl.,,
+XCX,"Can, cylindrical ",Can,,,Dose,,,
+XBJ,Bucket ,Bucket ,,,Eimer ,,,
+XUN,Unit,Unit,,,Einheit,,,"A type of package composed of a single item or object, not otherwise specified as a unit of transport equipment."
+XOS,Oneway pallet,Oneway pallet,,,Einweg-Palette,,,Pallet need not be returned to the point of expedition
+XDH,"Box, Commonwealth Handling Equipment Pool (CHEP), Eurobox","Eurobox, CHEP box",box,,"Eurobox, CHEP-Box",Box,,A box mounted on a pallet base under the control of CHEP.
+XBA,Barrel ,Barrel ,,,Fass,,,
+XFI,Firkin ,Firkin ,,,Fässchen ,,,
+XBO,"Bottle, non-protected, cylindrical ",Bottle,btl.,btl,Flasche,Fl.,fl,A narrow-necked cylindrical shaped vessel without external protective packing material.
+XBQ,"Bottle, protected cylindrical",Protected bottle,btl.,,Flasche in Umverpackung,Fl.,,A narrow-necked cylindrical shaped vessel with external protective packing material.
+XFB,Flexibag,Flexibag,,,Flexibag,,,"A flexible containment bag made of plastic, typically for the transportation bulk non-hazardous cargoes using standard size shipping containers."
+XFT,Foodtainer,Foodtainer,,,Foodtainer,,,
+XJR,Jar,Jar,,,Glas,Gl.,gl,
+XGR,"Receptacle, glass ",Glass receptacle,,,Glasgefäß,,,Containment vessel made of glass for retaining substances or articles.
+XOW,"Large bag, pallet sized",Pallet sized large bag,,,Großer Sack in Palettengröße,,,"A non-rigid container made of fabric, paper, plastic, etc, with an opening at the top which can be closed and which is suitable for use on pallets"
+X8B,"Crate, wooden",Wooden crate,,,Holzkiste,,,"A receptacle, made of wood, on which goods are retained for ease of mechanical handling during transport and storage."
+XCV,Cover,Cover,,,Hülle,,,
+XWA,Intermediate bulk container,Intermediate bulk container (IBC),IBC,,Intermediate Bulk Container (IBC),IBC,,"A reusable container made of metal, plastic, textile, wood or composite materials used to facilitate transportation of bulk solids and liquids in manageable volumes."
+XEI,"Case, isothermic ",Isothermic case,,,Isotherme Box,,,
+XJT,Jutebag,Jutebag,,,Jutebeutel,,,
+XGY,"Bag, gunny",Gunny bag,,,Jutesack,,,"A sack made of gunny or burlap, used for transporting coarse commodities, such as grains, potatoes, and other agricultural products. "
+XJY,"Jerrican, cylindrical",Jerrican,,,Kanister,,,
+XBD,Board,Board,,,Karton,,,
+XCR,Crate,Crate,,,Kiste,,,
+XAI,Clamshell,Clamshell,,,Klappverpackung,,,
+XPA,Packet ,Packet ,,,Kollo,,,Small package.
+XBK,Basket ,Basket ,,,Korb ,,,
+XBI,Bin,Bin,,,Kübel,,,
+XOV,Returnable pallet,Returnable pallet,,,Mehrwegpalette,,,Pallet must be returned to the point of expedition.
+XNT,Net,Net,,,Netz,,,
+XPK,Package,Package,pkg.,pkg,Packung,Pkg.,"pkg, pkt",Standard packaging unit.
+XPC,Parcel ,Parcel ,,,Paket ,,,
+XPX,Pallet ,Pallet ,,,Palette ,,,"Platform or open-ended box, usually made of wood, on which goods are retained for ease of mechanical handling during transport and storage."
+X5M,"Bag, paper ",Paper bag,,,Papiertüte,,,
+XPR,"Receptacle, plastic ",Plastic receptacle,,,Plastikgefäß,,,Containment vessel made of plastic for retaining substances or articles.
+XEC,"Bag, plastic ",Plastic bag,,,Plastiktüte,,,
+X6H,"Composite packaging, plastic receptacle",Plastic composite packaging,,,Plastik-Verbundverpackung,,,
+X44,"Bag, polybag",Polybag,,,Polybeutel,,,"A type of plastic bag, typically used to wrap promotional pieces, publications, product samples, and/or catalogues. "
+XBR,Bar,Bar,,,Riegel,,,
+XCW,"Cage, roll ",Roll cage,,,Rollcontainer,,,
+XBT,Bolt ,Bolt ,,,Rolle,,,
+XSA,Sack ,Sack ,,,Sack ,,,
+XBM,Basin,Bowl,,,Schüssel,,,
+XSX,Set,Set,,,Set,,,
+XDN,Dispenser,Dispenser,,,Spender,,,
+XAE,Aerosol,Aerosol,,,Sprühdose,,,
+XSC,"Crate, shallow ",Shallow crate,,,Steige,,,
+XLU,Lug,Lug,,,Stiege,,,A wooden box for the transportation and storage of fruit or vegetables.
+X5L,"Bag, textile ",Textile bag,,,Stofftüte,,,
+XPP,Piece,Piece,,pc,Stück,,"stk, st, stueck",A loose or unpacked article.
+XPU,Tray ,Tray ,,,Schale,,,
+XBG,Bag,Bag,,,Tasche,,,A receptacle made of flexible material with an open or closed top.
+XP2,Pan,Pan,,,Tiegel,,,"A shallow, wide, open container, usually of metal."
+XCK,Cask ,Cask ,,,Tonne,,,
+XPT,Pot,Pot,,,Topf,,tiegel,
+XGI,Girder ,Girder ,,,Träger ,,,
+XTU,Tube ,Tube ,,,Tube,,,
+PTN,Portion,Portion,,,Portion,,port,
+STC,Sick,Stick,,,Stange,,,
diff --git a/config/units-of-measure/translations_scalar-de.csv b/config/units-of-measure/translations_scalar-de.csv
new file mode 100644
index 00000000..37730e91
--- /dev/null
+++ b/config/units-of-measure/translations_scalar-de.csv
@@ -0,0 +1,120 @@
+common code,full name,short name,symbol,alt. Symbols,aliases,description
+DMT,Dezimeter,Dezimeter,dm,,,
+CMT,Zentimeter,Zentimeter,cm,,,
+4H,Mikrometer (Mikron),Mikrometer,µm,,,
+MMT,Millimeter,Millimeter,mm,,,
+HMT,Hektometer,Hektometer,hm,,,
+KMT,Kilometer,Kilometer,km,,,
+A45,Dekameter,Dekameter,dam,,,
+INH,Zoll,Zoll,″,in,,
+FOT,Fuß,Fuß,ft,,,
+YRD,Yard,Yard,yd,,,
+SMI,Meile (britisch),Meile,mile,,,
+77,Millizoll,Millizoll,mil,,,
+MTK,Quadratmeter,Quadratmeter,m²,qm,,
+DAA,Dekar,Dekar,daa,,,
+KMK,Quadratkilometer,Quadratkilometer,km²,,,
+CMK,Quadratzentimeter,Quadratzentimeter,cm²,,,
+DMK,Quadratdezimeter,Quadratdezimeter,dm²,,,
+H16,Quadratdekameter,Quadratdekameter,dam²,,,Synonym: Ar
+H18,Quadrathektometer,Quadrathektometer,hm²,,,Synonym: Hektar
+MMK,Quadratmillimeter,Quadratmillimeter,mm²,,,
+M47,Circular mil ,Circular mil ,cmil,,,"Unit of an area, of which the size is given by a diameter of length of 1 mm (0,001 in) based on the formula: area = p·(diameter/2)²."
+ARE,Ar,Ar,a,,,Synonym: Quadratdekameter
+HAR,Hektar,Hektar,ha,,,Synonym: Quadrathektometer
+INK,Quadratzoll,Quadratzoll,in²,,,
+FTK,Quadratfuß,Quadratfuß,ft²,,,
+YDK,Quadratmeter,Quadratmeter,yd²,,,
+MIK,Quadratmeile (britisch),Quadratmeile,mi²,,,
+ACR,Morgen,Morgen,acre,,,
+KGM,Kilogramm,Kilogramm,kg,,kilo,
+HGM,Hektogramm,Hektogramm,hg,,,
+KTN,Kilotonne,Kilotonne,kt,,,
+DJ,Dekagramm,Dekagramm,dag,dkg,,
+DG,Dezigramm,Dezigramm,dg,,,
+CGM,Zentigramm,Zentigramm,cg,,,
+DTN,Dezitonne,Dezitonne,dt,dtn,,"Synonym: Zentner (A, CH)"
+MGM,Milligramm,Milligramm,mg,,,
+2U,Megagramm,Megagramm,Mg,,,
+MC,Mikrogramm,Mikrogramm,µg,,,
+GRM,Gramm,Gramm,g,,"gr, grm",
+TNE,Tonne (metrisch),Tonne,t,,,
+M86,Pfund,Pfund,pfd,,,Veraltete deutsche Masseeinheit
+LBR,Englisches Pfund,Englisches Pfund,lb,,,
+GRN,Gran,Gran,gr,,,
+ONZ,Unze (avoirdupois),Unze,oz,,,
+CWI,Hundredweight (britisch),Br. Zentner,cwt,,,
+CWA,Hundredweight (amerikanisch),Am. Zentner,cwt,,,
+LTN,"Britische Tonne, Long Ton (amerikanisch)",Br. Tonne,ton (Br.),,,Synonym: gross ton (2240 lb)
+STI,Stein (britisch),Stein,st,,,
+STN,"Amerikanische Tonne, Short Ton",Am. Tonne,ton (Am.),,,Synonym: net ton (2000 lb)
+APZ,Feinunze oder Apothekerunze,Feinunze,tr oz,,,
+F13,Slug,Slug,slug,,,A unit of mass. One slug is the mass accelerated at 1 foot per second per second by a force of 1 pound.
+SEC,Sekunde [Zeiteinheit],Sekunde,s,,,
+MIN,Minute [Zeiteinheit],Minute,min,,,
+HUR,Stunde,Stunde,h,,,
+DAY,Tag,Tag,d,,,
+C26,Millisekunde,Millisekunde,ms,,,
+WEE,Woche,Woche,wk,,,
+MON,Monat,Monat,mo,,,"Zeiteinheit gleich 1/12 eines Jahres oder 365,25 Tage."
+ANN,Jahr,Jahr,y,,,"Zeiteinheit gleich 365,25 Tage (Julianisches Jahr)"
+MTQ,Kubikmeter,Kubikmeter,m³,cbm,,
+LTR,Liter,Liter,l,,,
+MAL,Megaliter,Megaliter,Ml,,,
+DLT,Deziliter,Deziliter,dl,,,
+4G,Mikroliter,Mikroliter,µl,,,
+K6,Kiloliter,Kiloliter,kl,,,
+A44,Dekaliter,Dekaliter,dal,,,
+MMQ,Kubikmillimeter,Kubikmillimeter,mm³,,,
+CMQ,Kubikzentimeter,Kubikzentimeter,cm³,ccm,,
+DMQ,Kubikdezimeter,Kubikdezimeter,dm³,,,
+MLT,Milliliter,Milliliter,ml,,,
+HLT,Hektoliter,Hektoliter,hl,,,
+CLT,Zentiliter,Zentiliter,cl,,,
+DMA,Kubikdekameter,Kubikdekameter,dam³,,,
+H19,Kubikhektometer,Kubikhektometer,hm³,,,
+H20,Kubikkilometer,Kubikkilometer,km³,,,
+M67,acre-foot (basierend auf U.S. Survey Foot),acre-foot,acre-ft,,,"Unit of the volume, which is Am.ed in the United States to measure/gauge the capacity of reservoirs."
+M68,Cord (128 ft3),Cord,cord,,,Traditional unit of the volume of stacked firewood which has been measured with a cord.
+M69,Kubikmeile (britisch),Kubikmeile,mi³,,,Unit of volume according to the Imperial system of units.
+M70,Registertonne,Registertonne,RT,,,Traditional unit of the cargo capacity.
+5I,Kubikfuß,Kubikfuß,std,,,
+INQ,Kubikzoll,Kubikzoll,in³,,,
+FTQ,Kubikfuß,Kubikfuß,ft³,,,
+YDQ,Kubikmeter,Kubikmeter,yd³,,,
+GLI,Gallone (britisch),Gallone (Br.),gal,,,
+GLL,Gallone (amerikanisch),Gallone (Am.),gal,,,
+PTI,Pint (britisch),Pint (Br.),pt,,,
+QTI,quart (britisch),Quart,qt,,,
+PTL,flüssiges Pint (amerikanisch),flüssiges Pint (Am.),liq pt,,,
+QTL,flüssiges Quart (amerikanisch),flüssiges Quart (Am.),liq qt,,,
+PTD,trockenes Pint (amerikanisch),trockenes Pint (Am.),dry pt,,,
+OZI,flüssige Unze (britisch),flüssige Unze (Br.),fl oz,,,
+J57,Barrel (britisch),Barrel (Br.),bbl,,,
+L43,peck (britisch),Viertelscheffel (Br.),pk,,,
+L84,ton (UK shipping),Shipping ton (Br.),Shipping ton (Br.),,,
+L86,ton (US shipping),Shipping ton (Am.),Shipping ton (Am.),,,
+OZA,Flüssigunze (amerikanisch),Flüssigunze,fl oz,,,
+BUI,Scheffel (britisch),Scheffel (Br.),bushel,,,
+BUA,Scheffel (amerikanisch),Scheffel (Am.),bu,,,
+BLL,Barrel (amerikanisch),Barrel (Am.),barrel,,,
+BLD,Dry Barrel (amerikanisch),Dry Barrel (Am.),bbl,,,
+GLD,Dry Gallon (amerikanisch),Dry Gallon (Am.),dry gal,,,
+QTD,Dry Quart (amerikanisch),Dry Quart (Am.),dry qt,,,
+G26,Ster (Schüttraummeter),Ster,st,,,Kubikmeter
+G21,Tasse [Volumeneinheit],Tasse,cup,,,
+G24,Esslöffel,Esslöffel,EL,,,
+G25,Teelöffel,Teelöffel,TL,,,
+G23,Viertelscheffel (amerikanisch),Viertelscheffel (Am.),pk,,,
+JOU,Joule,Joule,J,,,
+J75,Kalorie,Kalorie,cal,,,
+K51,Kilokalorie,Kilokalorie,kcal,,,
+J55,Wattsekunde,Wattsekunde,Ws,,,
+WHR,Wattstunde,Wattstunde,Wh,,,
+KWH,Kilowattstunde,Kilowattstunde,kWh,,,
+GWH,Gigawattstunde,Gigawattstunde,GWh,,,
+WTT,Watt,Watt,W,,,
+KWT,Kilowatt,Kilowatt,kW,,,
+MAW,Megawatt,Megawatt,MW,,,A unit of power defining the rate of energy transferred or consumed when a current of 1000 amperes flows due to a potential of 1000 volts at unity power factor.
+PTN,Portion,Portion,,,,
+STC,Stange,Stange,,,,
diff --git a/config/units-of-measure/translations_scalar-en.csv b/config/units-of-measure/translations_scalar-en.csv
new file mode 100644
index 00000000..3c4baae8
--- /dev/null
+++ b/config/units-of-measure/translations_scalar-en.csv
@@ -0,0 +1,121 @@
+common code,full name,short name,symbol,alt. Symbols,aliases,description
+DMT,decimetre,decimetre,dm,,,
+CMT,centimetre,centimetre,cm,,,
+4H,micrometre (micron),micrometre,µm,,,
+MMT,millimetre,millimetre,mm,,,
+HMT,hectometre,hectometre,hm,,,
+KMT,kilometre,kilometre,km,,,
+A45,decametre,decametre,dam,,,
+INH,inch,inch,in,,,
+FOT,foot,foot,ft,,,
+YRD,yard,yard,yd,,,
+SMI,mile (statute mile),mile,mile,,,
+77,milli-inch,milli-inch,mil,,,
+MTK,square metre,square metre,m²,sq m,,
+DAA,decare,decare,daa,,,
+KMK,square kilometre,square kilometre,km²,sq km,,
+CMK,square centimetre,square centimetre,cm²,sq cm,,
+DMK,square decimetre,square decimetre,dm²,sq dm,,
+H16,square decametre,square decametre,dam²,sq dam,,Synonym: are
+H18,square hectometre,square hectometre,hm²,sq hm,,Synonym: hectare
+MMK,square millimetre,square millimetre,mm²,sq mm,,
+M47,circular mil ,circular mil ,cmil,,,"Unit of an area, of which the size is given by a diameter of length of 1 mm (0,001 in) based on the formula: area = p·(diameter/2)²."
+ARE,are,are,a,,,Synonym: square decametre
+HAR,hectare,hectare,ha,,,Synonym: square hectometre
+INK,square inch,square inch,in²,sq in,,
+FTK,square foot,square foot,ft²,sq ft,,
+YDK,square yard,square yard,yd²,sq yd,,
+MIK,square mile (statute mile),square mile,mi²,sq mi,,
+ACR,acre,acre,acre,,,
+KGM,kilogram,kilogram,kg,,kilo,A unit of mass equal to one thousand grams.
+HGM,hectogram,hectogram,hg,,,
+KTN,kilotonne,kilotonne,kt,,,
+DJ,decagram,decagram,dag,dkg,,
+DG,decigram,decigram,dg,,,
+CGM,centigram,centigram,cg,,,
+DTN,decitonne,decitonne,dt,dtn,,"Synonym: centner, metric 100 kg; quintal, metric 100 kg"
+MGM,milligram,milligram,mg,,,
+2U,megagram,megagram,Mg,,,
+MC,microgram,microgram,µg,,,
+GRM,gram,gram,g,,"gr, grm",
+TNE,tonne (metric),metric ton,t,mt,,
+M86,pfund,pfund,pfd,,,Outdated unit of the mass used in Germany.
+LBR,pound,pound,lb,,,
+GRN,grain,grain,gr,,,
+ONZ,ounce (avoirdupois),ounce,oz,,,
+CWI,hundred weight (UK),hundred weight,cwt,,,
+CWA,hundred pound (cwt) / hundred weight (US),hundred pound (cwt) / hundred weight (US),cwt,,,
+LTN,ton (UK) or long ton (US),ton (UK) or long ton (US),ton (UK),,,Synonym: gross ton (2240 lb)
+STI,stone (UK),stone,st,,,
+STN,ton (US) or short ton (UK/US),ton (US) or short ton (UK/US),ton (US),,,Synonym: net ton (2000 lb)
+APZ,troy ounce or apothecary ounce,troy ounce,tr oz,,,
+F13,slug,slug,slug,,,A unit of mass. One slug is the mass accelerated at 1 foot per second per second by a force of 1 pound.
+SEC,second [unit of time],second,s,,,
+MIN,minute [unit of time],minute,min,,,
+HUR,hour,hour,h,,,
+DAY,day,day,d,,,
+C26,millisecond,millisecond,ms,,,
+WEE,week,week,wk,,,
+MON,month,month,mo,,,"Unit of time equal to 1/12 of a year of 365,25 days."
+ANN,year,year,y,,,"Unit of time equal to 365,25 days.
+Synonym: Julian year"
+MTQ,cubic metre,cubic metre,m³,,,Synonym: metre cubed
+LTR,litre,litre,l,,,
+MAL,megalitre,megalitre,Ml,,,
+DLT,decilitre,decilitre,dl,,,
+4G,microlitre,microlitre,µl,,,
+K6,kilolitre,kilolitre,kl,,,
+A44,decalitre,decalitre,dal,,,
+MMQ,cubic millimetre,cubic millimetre,mm³,,,
+CMQ,cubic centimetre,cubic centimetre,cm³,"cc, ccm",,
+DMQ,cubic decimetre,cubic decimetre,dm³,,,
+MLT,millilitre,millilitre,ml,,,
+HLT,hectolitre,hectolitre,hl,,,
+CLT,centilitre,centilitre,cl,,,
+DMA,cubic decametre,cubic decametre,dam³,,,
+H19,cubic hectometre,cubic hectometre,hm³,,,
+H20,cubic kilometre,cubic kilometre,km³,,,
+M67,acre-foot (based on U.S. survey foot),acre-foot,acre-ft,,,"Unit of the volume, which is used in the United States to measure/gauge the capacity of reservoirs."
+M68,cord (128 ft3),cord,cord,,,Traditional unit of the volume of stacked firewood which has been measured with a cord.
+M69,cubic mile (UK statute),cubic mile,mi³,,,Unit of volume according to the Imperial system of units.
+M70,register ton,register ton,RT,,,Traditional unit of the cargo capacity.
+5I,standard cubic foot,standard cubic foot,std,,,Use standard (common code WSD)
+INQ,cubic inch,cubic inch,in³,,,Synonym: inch cubed
+FTQ,cubic foot,cubic foot,ft³,,,
+YDQ,cubic yard,cubic yard,yd³,,,
+GLI,gallon (UK),gallon (UK),gal,,,
+GLL,gallon (US),gallon (US),gal,,,
+PTI,pint (UK),pint (UK),pt,,,
+QTI,quart (UK),quart (UK),qt,,,
+PTL,liquid pint (US),liquid pint (US),liq pt,,,
+QTL,liquid quart (US),liquid quart (US),liq qt,,,
+PTD,dry pint (US),dry pint (US),dry pt,,,
+OZI,fluid ounce (UK),fluid ounce (UK),fl oz,,,
+J57,barrel (UK petroleum),barrel (UK petroleum),bbl,,,
+L43,peck (UK),peck (UK),pk (UK),,,
+L84,ton (UK shipping),ton (UK shipping),British shipping ton,,,
+L86,ton (US shipping),ton (US shipping),(US) shipping ton,,,
+OZA,fluid ounce (US),fluid ounce (US),fl oz,,,
+BUI,bushel (UK),bushel (UK),bushel,,,
+BUA,bushel (US),bushel (US),bu,,,
+BLL,barrel (US),barrel (US),barrel,,,
+BLD,dry barrel (US),dry barrel (US),bbl,,,
+GLD,dry gallon (US),dry gallon (US),dry gal,,,
+QTD,dry quart (US),dry quart (US),dry qt,,,
+G26,stere,stere,st,,,cubic metre
+G21,cup [unit of volume],cup,cup,,,
+G24,tablespoon (US),tablespoon,tbsp.,,,
+G25,teaspoon (US),teaspoon,tsp.,,,
+G23,peck (US),peck (US),pk (US),,,
+JOU,joule,joule,J,,,
+J75,calorie (mean),calorie,cal,,,
+K51,kilocalorie (mean),kilocalorie,kcal,,,
+J55,watt second,watt second,Ws,,,
+WHR,watt hour,watt hour,Wh,,,
+KWH,kilowatt hour,kilowatt hour,kWh,,,
+GWH,gigawatt hour,gigawatt hour,GWh,,,
+WTT,watt,watt,W,,,
+KWT,kilowatt,kilowatt,kW,,,
+MAW,megawatt,megawatt,MW,,,A unit of power defining the rate of energy transferred or consumed when a current of 1000 amperes flows due to a potential of 1000 volts at unity power factor.
+PTN,portion,portion,,,,
+STC,stick,stick,,,,
diff --git a/config/units-of-measure/un-ece-20-remastered.yml b/config/units-of-measure/un-ece-20-remastered.yml
new file mode 100644
index 00000000..d6e484d0
--- /dev/null
+++ b/config/units-of-measure/un-ece-20-remastered.yml
@@ -0,0 +1,22825 @@
+# This file has been autogenerated by `rake units:clean_unece20_source`
+---
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: lift
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '05'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: small spray
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '06'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: heat lot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '08'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: group
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '10'
+ Description: 'A unit of count defining the number of groups (group: set of items
+ classified together).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: outfit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '11'
+ Description: 'A unit of count defining the number of outfits (outfit: a complete
+ set of equipment / materials / objects used for a specific purpose).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: ration
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '13'
+ Description: 'A unit of count defining the number of rations (ration: a single portion
+ of provisions).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: shot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '14'
+ Description: A unit of liquid measure, especially related to spirits.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: stick, military
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '15'
+ Description: 'A unit of count defining the number of military sticks (military stick:
+ bombs or paratroops released in rapid succession from an aircraft).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: hundred fifteen kg drum
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '16'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: hundred lb drum
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '17'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: fiftyfive gallon (US) drum
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '18'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: tank truck
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '19'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: twenty foot container
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '20'
+ Description: A unit of count defining the number of shipping containers that measure
+ 20 foot in length.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: forty foot container
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '21'
+ Description: A unit of count defining the number of shipping containers that measure
+ 40 foot in length.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decilitre per gram
+ ConversionFactor: 10⁻¹ x m³/kg
+ Symbol: dl/g
+ CommonCode: '22'
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gram per cubic centimetre
+ ConversionFactor: 10³ kg/m³
+ Symbol: g/cm³
+ CommonCode: '23'
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: theoretical pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '24'
+ Description: A unit of mass defining the expected mass of material expressed as
+ the number of pounds.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per square centimetre
+ ConversionFactor: 10 kg/m²
+ Symbol: g/cm²
+ CommonCode: '25'
+ Description: ''
+ conversion:
+ factor: 10.0
+ base_units:
+ - '28'
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: actual ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '26'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: theoretical ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '27'
+ Description: A unit of mass defining the expected mass of material, expressed as
+ the number of tons.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram per square metre
+ ConversionFactor: kg/m²
+ Symbol: kg/m²
+ CommonCode: '28'
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - '28'
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: pound per thousand square foot
+ ConversionFactor: ''
+ Symbol: lb/kft²
+ CommonCode: '29'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: horse power day per air dry metric ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '30'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: catch weight
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '31'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: kilogram per air dry metric ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '32'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilopascal square metre per gram
+ ConversionFactor: 10⁶ m/s²
+ Symbol: kPa·m²/g
+ CommonCode: '33'
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilopascal per millimetre
+ ConversionFactor: 10⁶ kg/(m² x s²)
+ Symbol: kPa/mm
+ CommonCode: '34'
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per square centimetre second
+ ConversionFactor: 10⁻² m/s
+ Symbol: ml/(cm²·s)
+ CommonCode: '35'
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - MTS
+ - P87
+- Status: X
+ LevelAndCategory: 1M
+ Name: cubic foot per minute per square foot
+ ConversionFactor: ''
+ Symbol: ft³/(min/ft²)
+ CommonCode: '36'
+ Description: Conversion factor required
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce per square foot
+ ConversionFactor: 0,305 151 7 kg/m²
+ Symbol: oz/ft²
+ CommonCode: '37'
+ Description: ''
+ conversion:
+ factor: 0.3051517
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: ounce per square foot per 0,01inch
+ ConversionFactor: ''
+ Symbol: oz/(ft²/cin)
+ CommonCode: '38'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per second
+ ConversionFactor: 10⁻⁶ m³/s
+ Symbol: ml/s
+ CommonCode: '40'
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per minute
+ ConversionFactor: 1,666 67 x 10⁻⁸ m³/s
+ Symbol: ml/min
+ CommonCode: '41'
+ Description: ''
+ conversion:
+ factor: 1.66667e-08
+ base_units:
+ - MQS
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: super bulk bag
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '43'
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: fivehundred kg bulk bag
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '44'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: threehundred kg bulk bag
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '45'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: fifty lb bulk bag
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '46'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: fifty lb bag
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '47'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: bulk car load
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '48'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: theoretical kilogram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '53'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: theoretical tonne
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '54'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: sitas
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '56'
+ Description: A unit of area for tin plate equal to a surface area of 100 square
+ metres.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: mesh
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '57'
+ Description: A unit of count defining the number of strands per inch as a measure
+ of the fineness of a woven product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: net kilogram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '58'
+ Description: A unit of mass defining the total number of kilograms after deductions.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: part per million
+ ConversionFactor: 1 x 10⁻⁶
+ Symbol: ppm
+ CommonCode: '59'
+ Description: A unit of proportion equal to 10⁻⁶.
+ conversion:
+ factor: 1.0e-06
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent weight
+ ConversionFactor: 1 x 10⁻²
+ Symbol: ''
+ CommonCode: '60'
+ Description: A unit of proportion equal to 10⁻².
+ conversion:
+ factor: 0.01
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: part per billion (US)
+ ConversionFactor: 1 x 10⁻⁹
+ Symbol: ppb
+ CommonCode: '61'
+ Description: A unit of proportion equal to 10⁻⁹.
+ conversion:
+ factor: 1.0e-09
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: percent per 1000 hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '62'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: failure rate in time
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '63'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.1'
+ Name: pound per square inch, gauge
+ ConversionFactor: 7,030 696 x 10² kg/m²
+ Symbol: ''
+ CommonCode: '64'
+ Description: ''
+ conversion:
+ factor: 703.0696
+ base_units:
+ - '28'
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: oersted
+ ConversionFactor: 7,957 747 x 10 A/m
+ Symbol: Oe
+ CommonCode: '66'
+ Description: ''
+ conversion:
+ factor: 7.957747
+ base_units:
+ - AE
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: test specific scale
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '69'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: volt ampere per pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '71'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: watt per pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '72'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: ampere tum per centimetre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '73'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millipascal
+ ConversionFactor: 10⁻³ Pa
+ Symbol: mPa
+ CommonCode: '74'
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C55
+ - PAL
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: gauss
+ ConversionFactor: 10⁻⁴ T
+ Symbol: Gs
+ CommonCode: '76'
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - D33
+- Status: ''
+ LevelAndCategory: '2'
+ Name: milli-inch
+ ConversionFactor: 25,4 x 10⁻⁶ m
+ Symbol: mil
+ CommonCode: '77'
+ Description: ''
+ conversion:
+ factor: 2.54e-05
+ base_units:
+ - MTR
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: kilogauss
+ ConversionFactor: 10⁻¹ T
+ Symbol: kGs
+ CommonCode: '78'
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - D33
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per square inch absolute
+ ConversionFactor: 7,030 696 x 10² kg/m²
+ Symbol: lb/in²
+ CommonCode: '80'
+ Description: ''
+ conversion:
+ factor: 703.0696
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '1'
+ Name: henry
+ ConversionFactor: H
+ Symbol: H
+ CommonCode: '81'
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - '81'
+- Status: D
+ LevelAndCategory: '2'
+ Name: kilopound-force per square inch
+ ConversionFactor: 6,894 757 x 10⁶ Pa
+ Symbol: klbf/in²
+ CommonCode: '84'
+ Description: A unit of pressure defining the number of kilopounds force per square
+ inch.,Use kip per square inch (common code N20).
+ conversion:
+ factor: 6894757.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot pound-force
+ ConversionFactor: 1,355 818 J
+ Symbol: ft·lbf
+ CommonCode: '85'
+ Description: ''
+ conversion:
+ factor: 1.355818
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per cubic foot
+ ConversionFactor: 1,601 846 x 10¹ kg/m³
+ Symbol: lb/ft³
+ CommonCode: '87'
+ Description: ''
+ conversion:
+ factor: 16.01846
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poise
+ ConversionFactor: 0,1 Pa x s
+ Symbol: P
+ CommonCode: '89'
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: Saybold universal second
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '90'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: stokes
+ ConversionFactor: 10⁻⁴ m²/s
+ Symbol: St
+ CommonCode: '91'
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - S4
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: calorie per cubic centimetre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '92'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: calorie per gram
+ ConversionFactor: 4,186 8 x 10³ J/kg
+ Symbol: cal/g
+ CommonCode: '93'
+ Description: Use International Table (IT) calorie per gram (common code D75).
+ conversion:
+ factor: 4186.8
+ base_units:
+ - J2
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: curl unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '94'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: twenty thousand gallon (US) tankcar
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '95'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: ten thousand gallon (US) tankcar
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '96'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: ten kg drum
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '97'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: fifteen kg drum
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: '98'
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: car mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1A
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: car count
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1B
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: locomotive count
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1C
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: caboose count
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1D
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: empty car
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1E
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: train mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1F
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: fuel usage gallon (US)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1G
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: caboose mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1H
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: fixed rate
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1I
+ Description: A unit of quantity expressed as a predetermined or set rate for usage
+ of a facility or service.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: ton mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1J
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: locomotive mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1K
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: total car count
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1L
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: total car mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1M
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: quarter mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 1X
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: radian per second
+ ConversionFactor: rad/s
+ Symbol: rad/s
+ CommonCode: 2A
+ Description: Refer ISO/TC12 SI Guide
+ conversion:
+ factor: 1.0
+ base_units:
+ - 2A
+- Status: ''
+ LevelAndCategory: '1'
+ Name: radian per second squared
+ ConversionFactor: rad/s²
+ Symbol: rad/s²
+ CommonCode: 2B
+ Description: Refer ISO/TC12 SI Guide
+ conversion:
+ factor: 1.0
+ base_units:
+ - 2B
+- Status: ''
+ LevelAndCategory: '2'
+ Name: roentgen
+ ConversionFactor: 2,58 x 10⁻⁴ C/kg
+ Symbol: R
+ CommonCode: 2C
+ Description: ''
+ conversion:
+ factor: 0.000258
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: volt AC
+ ConversionFactor: V
+ Symbol: V
+ CommonCode: 2G
+ Description: A unit of electric potential in relation to alternating current (AC).
+ conversion:
+ factor: 1.0
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: volt DC
+ ConversionFactor: V
+ Symbol: V
+ CommonCode: 2H
+ Description: A unit of electric potential in relation to direct current (DC).
+ conversion:
+ factor: 1.0
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per hour
+ ConversionFactor: 2,930 711x 10⁻¹ W
+ Symbol: BtuIT/h
+ CommonCode: 2I
+ Description: ''
+ conversion:
+ factor: 2.930711
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic centimetre per second
+ ConversionFactor: 10⁻⁶ m³/s
+ Symbol: cm³/s
+ CommonCode: 2J
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic foot per hour
+ ConversionFactor: 7,865 79 x 10⁻⁶ m³/s
+ Symbol: ft³/h
+ CommonCode: 2K
+ Description: ''
+ conversion:
+ factor: 7.86579e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic foot per minute
+ ConversionFactor: 4,719 474 x 10⁻⁴ m³/s
+ Symbol: ft³/min
+ CommonCode: 2L
+ Description: ''
+ conversion:
+ factor: 0.0004719474
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1S
+ Name: centimetre per second
+ ConversionFactor: 10⁻² m/s
+ Symbol: cm/s
+ CommonCode: 2M
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '1'
+ Name: decibel
+ ConversionFactor: 0,115 129 3 Np
+ Symbol: dB
+ CommonCode: 2N
+ Description: ''
+ conversion:
+ factor: 0.1151293
+ base_units:
+ - C50
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kilobyte
+ ConversionFactor: ''
+ Symbol: kbyte
+ CommonCode: 2P
+ Description: A unit of information equal to 10³ (1000) bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilobecquerel
+ ConversionFactor: 10³ Bq
+ Symbol: kBq
+ CommonCode: 2Q
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 2S
+ Name: kilocurie
+ ConversionFactor: 3,7 x 10¹³ Bq
+ Symbol: kCi
+ CommonCode: 2R
+ Description: ''
+ conversion:
+ factor: 37000000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megagram
+ ConversionFactor: 10³ kg
+ Symbol: Mg
+ CommonCode: 2U
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - KGM
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: megagram per hour
+ ConversionFactor: ''
+ Symbol: Mg/h
+ CommonCode: 2V
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bin
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 2W
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: metre per minute
+ ConversionFactor: 0,016 666 m/s
+ Symbol: m/min
+ CommonCode: 2X
+ Description: ''
+ conversion:
+ factor: 0.016666
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: milliroentgen
+ ConversionFactor: 2,58 x 10⁻⁷ C/kg
+ Symbol: mR
+ CommonCode: 2Y
+ Description: ''
+ conversion:
+ factor: 2.58e-07
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millivolt
+ ConversionFactor: 10⁻³ V
+ Symbol: mV
+ CommonCode: 2Z
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megajoule
+ ConversionFactor: 10⁶ J
+ Symbol: MJ
+ CommonCode: 3B
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: manmonth
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 3C
+ Description: A unit of count defining the number of months for a person or persons
+ to perform an undertaking.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: pound per pound of product
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 3E
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: pound per piece of product
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 3G
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: kilogram per kilogram of product
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 3H
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: kilogram per piece of product
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 3I
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bobbin
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 4A
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: cap
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 4B
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centistokes
+ ConversionFactor: 10⁻⁶ m²/s
+ Symbol: cSt
+ CommonCode: 4C
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - S4
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: twenty pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 4E
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microlitre
+ ConversionFactor: 10⁻⁹ m³
+ Symbol: µl
+ CommonCode: 4G
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: micrometre (micron)
+ ConversionFactor: 10⁻⁶ m
+ Symbol: µm
+ CommonCode: 4H
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milliampere
+ ConversionFactor: 10⁻³ A
+ Symbol: mA
+ CommonCode: 4K
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: megabyte
+ ConversionFactor: ''
+ Symbol: Mbyte
+ CommonCode: 4L
+ Description: A unit of information equal to 10⁶ (1000000) bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per hour
+ ConversionFactor: 2,777 78 x 10⁻¹⁰ kg/s
+ Symbol: mg/h
+ CommonCode: 4M
+ Description: ''
+ conversion:
+ factor: 2.77778e-10
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megabecquerel
+ ConversionFactor: 10⁶ Bq
+ Symbol: MBq
+ CommonCode: 4N
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microfarad
+ ConversionFactor: 10⁻⁶ F
+ Symbol: µF
+ CommonCode: 4O
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - FAR
+- Status: ''
+ LevelAndCategory: '1'
+ Name: newton per metre
+ ConversionFactor: N/m
+ Symbol: N/m
+ CommonCode: 4P
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - 4P
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce inch
+ ConversionFactor: 7,200 778 x 10⁻⁴ kg x m
+ Symbol: oz·in
+ CommonCode: 4Q
+ Description: ''
+ conversion:
+ factor: 0.0007200778
+ base_units:
+ - M94
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce foot
+ ConversionFactor: 8,640 934 x 10⁻³ kg x m
+ Symbol: oz·ft
+ CommonCode: 4R
+ Description: ''
+ conversion:
+ factor: 0.008640934
+ base_units:
+ - M94
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picofarad
+ ConversionFactor: 10⁻¹² F
+ Symbol: pF
+ CommonCode: 4T
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - FAR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per hour
+ ConversionFactor: 1,259 979 x 10⁻⁴ kg/s
+ Symbol: lb/h
+ CommonCode: 4U
+ Description: ''
+ conversion:
+ factor: 0.0001259979
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton (US) per hour
+ ConversionFactor: 2,519 958 x 10⁻¹ kg/s
+ Symbol: ton (US) /h
+ CommonCode: 4W
+ Description: ''
+ conversion:
+ factor: 0.2519958
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilolitre per hour
+ ConversionFactor: 2,777 78 x 10⁻⁴ m³/s
+ Symbol: kl/h
+ CommonCode: 4X
+ Description: ''
+ conversion:
+ factor: 0.000277778
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (US) per minute
+ ConversionFactor: 2,649 79 x 10⁻³ m³/s
+ Symbol: barrel (US)/min
+ CommonCode: 5A
+ Description: ''
+ conversion:
+ factor: 0.00264979
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: batch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5B
+ Description: 'A unit of count defining the number of batches (batch: quantity of
+ material produced in one operation or number of animals or persons coming at once).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: gallon(US) per thousand
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5C
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: MMSCF/day
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5E
+ Description: A unit of volume equal to one million (1000000) cubic feet of gas per
+ day.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: pound per thousand
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5F
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: pump
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5G
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: stage
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5H
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '2'
+ Name: standard cubic foot
+ ConversionFactor: 4,672 m³
+ Symbol: std
+ CommonCode: 5I
+ Description: Use standard (common code WSD)
+ conversion:
+ factor: 4.672
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: hydraulic horse power
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5J
+ Description: A unit of power defining the hydraulic horse power delivered by a fluid
+ pump depending on the viscosity of the fluid.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: count per minute
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5K
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: seismic level
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5P
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: seismic line
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: 5Q
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: 15 °C calorie
+ ConversionFactor: 4,188 46 J
+ Symbol: cal₁₅
+ CommonCode: A1
+ Description: ''
+ conversion:
+ factor: 4.18846
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ampere square metre per joule second
+ ConversionFactor: "(A x s)/kg"
+ Symbol: A·m²/(J·s)
+ CommonCode: A10
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A10
+- Status: ''
+ LevelAndCategory: '1'
+ Name: angstrom
+ ConversionFactor: 10⁻¹⁰ m
+ Symbol: Å
+ CommonCode: A11
+ Description: ''
+ conversion:
+ factor: 1.0e-10
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '1'
+ Name: astronomical unit
+ ConversionFactor: 1,495 978 70 x 10¹¹ m
+ Symbol: ua
+ CommonCode: A12
+ Description: ''
+ conversion:
+ factor: 149597870000.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1S
+ Name: attojoule
+ ConversionFactor: 10⁻¹⁸ J
+ Symbol: aJ
+ CommonCode: A13
+ Description: ''
+ conversion:
+ factor: 1.0e-18
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '1'
+ Name: barn
+ ConversionFactor: 10⁻²⁸ m²
+ Symbol: b
+ CommonCode: A14
+ Description: ''
+ conversion:
+ factor: 1.0e-28
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '1'
+ Name: barn per electronvolt
+ ConversionFactor: 6,241 51 x 10⁻¹⁰ m²/J
+ Symbol: b/eV
+ CommonCode: A15
+ Description: ''
+ conversion:
+ factor: 6.241
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: barn per steradian electronvolt
+ ConversionFactor: 6,241 51 x 10⁻¹⁰ m²/(sr xJ)
+ Symbol: b/(sr·eV)
+ CommonCode: A16
+ Description: ''
+ conversion:
+ factor: 6.241
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: barn per steradian
+ ConversionFactor: 1 x 10⁻²⁸ m²/sr
+ Symbol: b/sr
+ CommonCode: A17
+ Description: ''
+ conversion:
+ factor: 1.0e-28
+ base_units:
+ - D24
+- Status: ''
+ LevelAndCategory: '1'
+ Name: becquerel per kilogram
+ ConversionFactor: 27,027 x 10⁻¹² Ci/kg
+ Symbol: Bq/kg
+ CommonCode: A18
+ Description: ''
+ conversion:
+ factor: 2.7027e-11
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: becquerel per cubic metre
+ ConversionFactor: Bq/m³
+ Symbol: Bq/m³
+ CommonCode: A19
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A19
+- Status: ''
+ LevelAndCategory: 1S
+ Name: ampere per centimetre
+ ConversionFactor: 10² A/m
+ Symbol: A/cm
+ CommonCode: A2
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - AE
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per second square foot degree Rankine
+ ConversionFactor: 20 441,7 W/(m² x K)
+ Symbol: BtuIT/(s·ft²·°R)
+ CommonCode: A20
+ Description: ''
+ conversion:
+ factor: 20441.7
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per pound degree Rankine
+ ConversionFactor: 4 186,8 J/(kg x K)
+ Symbol: BtuIT/(lb·°R)
+ CommonCode: A21
+ Description: ''
+ conversion:
+ factor: 4186.8
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per second foot degree Rankine
+ ConversionFactor: 6 230,64 W/(m x K)
+ Symbol: BtuIT/(s·ft·°R)
+ CommonCode: A22
+ Description: ''
+ conversion:
+ factor: 6230.64
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per hour square foot degree Rankine
+ ConversionFactor: 5,678 26 W/ (m² x K)
+ Symbol: BtuIT/(h·ft²·°R)
+ CommonCode: A23
+ Description: ''
+ conversion:
+ factor: 5.67826
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: candela per square metre
+ ConversionFactor: cd/m²
+ Symbol: cd/m²
+ CommonCode: A24
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A24
+- Status: D
+ LevelAndCategory: '2'
+ Name: cheval vapeur
+ ConversionFactor: 7,354 988 x 10² W
+ Symbol: CV
+ CommonCode: A25
+ Description: 'Synonym: metric horse power'
+ conversion:
+ factor: 735.4988
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb metre
+ ConversionFactor: A x s x m
+ Symbol: C·m
+ CommonCode: A26
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A26
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb metre squared per volt
+ ConversionFactor: A² x s⁴/kg
+ Symbol: C·m²/V
+ CommonCode: A27
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A27
+- Status: ''
+ LevelAndCategory: 1S
+ Name: coulomb per cubic centimetre
+ ConversionFactor: 10⁶ C/m³
+ Symbol: C/cm³
+ CommonCode: A28
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - A29
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb per cubic metre
+ ConversionFactor: C/m³
+ Symbol: C/m³
+ CommonCode: A29
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A29
+- Status: ''
+ LevelAndCategory: 1S
+ Name: ampere per millimetre
+ ConversionFactor: 10³ A/m
+ Symbol: A/mm
+ CommonCode: A3
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - AE
+- Status: ''
+ LevelAndCategory: 1S
+ Name: coulomb per cubic millimetre
+ ConversionFactor: 10⁹ C/m³
+ Symbol: C/mm³
+ CommonCode: A30
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units:
+ - A29
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb per kilogram second
+ ConversionFactor: A/kg
+ Symbol: C/(kg·s)
+ CommonCode: A31
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A31
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb per mole
+ ConversionFactor: A x s/mol
+ Symbol: C/mol
+ CommonCode: A32
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A32
+- Status: ''
+ LevelAndCategory: 1S
+ Name: coulomb per square centimetre
+ ConversionFactor: 10⁴ C/m²
+ Symbol: C/cm²
+ CommonCode: A33
+ Description: ''
+ conversion:
+ factor: 10000.0
+ base_units:
+ - A34
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb per square metre
+ ConversionFactor: C/m²
+ Symbol: C/m²
+ CommonCode: A34
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: coulomb per square millimetre
+ ConversionFactor: 10⁶ C/m²
+ Symbol: C/mm²
+ CommonCode: A35
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - A34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic centimetre per mole
+ ConversionFactor: 10⁻⁶ m³/mol
+ Symbol: cm³/mol
+ CommonCode: A36
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - A40
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decimetre per mole
+ ConversionFactor: 10⁻³ m³/mol
+ Symbol: dm³/mol
+ CommonCode: A37
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - A40
+- Status: ''
+ LevelAndCategory: '1'
+ Name: cubic metre per coulomb
+ ConversionFactor: m³/A x s
+ Symbol: m³/C
+ CommonCode: A38
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A38
+- Status: ''
+ LevelAndCategory: '1'
+ Name: cubic metre per kilogram
+ ConversionFactor: m³/kg
+ Symbol: m³/kg
+ CommonCode: A39
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A39
+- Status: ''
+ LevelAndCategory: 1S
+ Name: ampere per square centimetre
+ ConversionFactor: 10⁴ A/m²
+ Symbol: A/cm²
+ CommonCode: A4
+ Description: ''
+ conversion:
+ factor: 10000.0
+ base_units:
+ - A41
+- Status: ''
+ LevelAndCategory: '1'
+ Name: cubic metre per mole
+ ConversionFactor: m³/mol
+ Symbol: m³/mol
+ CommonCode: A40
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A40
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ampere per square metre
+ ConversionFactor: A/m²
+ Symbol: A/m²
+ CommonCode: A41
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A41
+- Status: ''
+ LevelAndCategory: '2'
+ Name: curie per kilogram
+ ConversionFactor: 3,7 x 10¹⁰ Bq/kg
+ Symbol: Ci/kg
+ CommonCode: A42
+ Description: ''
+ conversion:
+ factor: 37000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: deadweight tonnage
+ ConversionFactor: ''
+ Symbol: dwt
+ CommonCode: A43
+ Description: A unit of mass defining the difference between the weight of a ship
+ when completely empty and its weight when completely loaded, expressed as the
+ number of tons.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decalitre
+ ConversionFactor: 10⁻² m³
+ Symbol: dal
+ CommonCode: A44
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decametre
+ ConversionFactor: 10 m
+ Symbol: dam
+ CommonCode: A45
+ Description: ''
+ conversion:
+ factor: 10.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: decitex
+ ConversionFactor: ''
+ Symbol: dtex (g/10km)
+ CommonCode: A47
+ Description: A unit of yarn density. One decitex equals a mass of 1 gram per 10
+ kilometres of length.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Rankine
+ ConversionFactor: 5/9 x K
+ Symbol: "°R"
+ CommonCode: A48
+ Description: 'Refer ISO 80000-5 (Quantities and units — Part 5: Thermodynamics)'
+ conversion:
+ factor: 5.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: denier
+ ConversionFactor: ''
+ Symbol: den (g/9 km)
+ CommonCode: A49
+ Description: A unit of yarn density. One denier equals a mass of 1 gram per 9 kilometres
+ of length.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ampere square metre
+ ConversionFactor: A x m²
+ Symbol: A·m²
+ CommonCode: A5
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A5
+- Status: D
+ LevelAndCategory: '2'
+ Name: dyne second per cubic centimetre
+ ConversionFactor: 10 Pa x s/m
+ Symbol: dyn·s/cm³
+ CommonCode: A50
+ Description: ''
+ conversion:
+ factor: 10.0
+ base_units:
+ - C67
+- Status: D
+ LevelAndCategory: '2'
+ Name: dyne second per centimetre
+ ConversionFactor: 10⁻³ N x s/m
+ Symbol: dyn·s/cm
+ CommonCode: A51
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C58
+- Status: D
+ LevelAndCategory: '2'
+ Name: dyne second per centimetre to the fifth power
+ ConversionFactor: 10⁵ Pa x s/m³
+ Symbol: dyn·s/cm⁵
+ CommonCode: A52
+ Description: ''
+ conversion:
+ factor: 100000.0
+ base_units:
+ - C66
+- Status: ''
+ LevelAndCategory: '1'
+ Name: electronvolt
+ ConversionFactor: 1,602 176 487 x 10⁻¹⁹ J
+ Symbol: eV
+ CommonCode: A53
+ Description: ''
+ conversion:
+ factor: 1.602176487e-19
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '1'
+ Name: electronvolt per metre
+ ConversionFactor: 1,602 176 487 x 10⁻¹⁹ J/m
+ Symbol: eV/m
+ CommonCode: A54
+ Description: ''
+ conversion:
+ factor: 1.602176487e-19
+ base_units:
+ - B12
+- Status: ''
+ LevelAndCategory: '1'
+ Name: electronvolt square metre
+ ConversionFactor: 1,602 176 487 x 10⁻¹⁹ J x m²
+ Symbol: eV·m²
+ CommonCode: A55
+ Description: ''
+ conversion:
+ factor: 1.602176487e-19
+ base_units:
+ - D73
+- Status: ''
+ LevelAndCategory: '1'
+ Name: electronvolt square metre per kilogram
+ ConversionFactor: 1,602 176 487 x 10⁻¹⁹ J x m²/kg
+ Symbol: eV·m²/kg
+ CommonCode: A56
+ Description: ''
+ conversion:
+ factor: 1.602176487e-19
+ base_units:
+ - B20
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg
+ ConversionFactor: 10⁻⁷J
+ Symbol: erg
+ CommonCode: A57
+ Description: ''
+ conversion:
+ factor: 1.0e-07
+ base_units:
+ - JOU
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg per centimetre
+ ConversionFactor: 10⁻⁵ J/m
+ Symbol: erg/cm
+ CommonCode: A58
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - B12
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: 8-part cloud cover
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A59
+ Description: 'A unit of count defining the number of eighth-parts as a measure of
+ the celestial dome cloud coverage.,Synonym: OKTA , OCTA'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ampere per square metre kelvin squared
+ ConversionFactor: A/(m² x K²)
+ Symbol: A/(m²·K²)
+ CommonCode: A6
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A6
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg per cubic centimetre
+ ConversionFactor: 10⁻¹ J/m³
+ Symbol: erg/cm³
+ CommonCode: A60
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - B8
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg per gram
+ ConversionFactor: 10⁻⁴ J/kg
+ Symbol: erg/g
+ CommonCode: A61
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - J2
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg per gram second
+ ConversionFactor: 10⁻⁴ W/kg
+ Symbol: erg/g·s
+ CommonCode: A62
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - WA
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg per second
+ ConversionFactor: 10⁻⁷ W
+ Symbol: erg/s
+ CommonCode: A63
+ Description: ''
+ conversion:
+ factor: 1.0e-07
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg per second square centimetre
+ ConversionFactor: 10⁻³ W/m²
+ Symbol: erg/(s·cm²)
+ CommonCode: A64
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D54
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg per square centimetre second
+ ConversionFactor: 10⁻³ W/m²
+ Symbol: erg/(cm²·s)
+ CommonCode: A65
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D54
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg square centimetre
+ ConversionFactor: 10⁻¹¹ J x m²
+ Symbol: erg·cm²
+ CommonCode: A66
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units:
+ - D73
+- Status: D
+ LevelAndCategory: '2'
+ Name: erg square centimetre per gram
+ ConversionFactor: 10⁻⁸ J x m²/kg
+ Symbol: erg·cm²/g
+ CommonCode: A67
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - B20
+- Status: ''
+ LevelAndCategory: 1S
+ Name: exajoule
+ ConversionFactor: 10¹⁸ J
+ Symbol: EJ
+ CommonCode: A68
+ Description: ''
+ conversion:
+ factor: 1.0e+18
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '1'
+ Name: farad per metre
+ ConversionFactor: kg⁻¹ x m⁻³ x s⁴ x A²
+ Symbol: F/m
+ CommonCode: A69
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A69
+- Status: ''
+ LevelAndCategory: 1S
+ Name: ampere per square millimetre
+ ConversionFactor: 10⁶ A/m²
+ Symbol: A/mm²
+ CommonCode: A7
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - A41
+- Status: ''
+ LevelAndCategory: 1S
+ Name: femtojoule
+ ConversionFactor: 10⁻¹⁵ J
+ Symbol: fJ
+ CommonCode: A70
+ Description: ''
+ conversion:
+ factor: 1.0e-15
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: 1S
+ Name: femtometre
+ ConversionFactor: 10⁻¹⁵ m
+ Symbol: fm
+ CommonCode: A71
+ Description: ''
+ conversion:
+ factor: 1.0e-15
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per second squared
+ ConversionFactor: 0,304 8 m/s²
+ Symbol: ft/s²
+ CommonCode: A73
+ Description: ''
+ conversion:
+ factor: 0.3048
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot pound-force per second
+ ConversionFactor: 1,355 818 W
+ Symbol: ft·lbf/s
+ CommonCode: A74
+ Description: ''
+ conversion:
+ factor: 1.355818
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: freight ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A75
+ Description: A unit of information typically used for billing purposes, defined
+ as either the number of metric tons or the number of cubic metres, whichever is
+ the larger.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gal
+ ConversionFactor: 10⁻² m/s²
+ Symbol: Gal
+ CommonCode: A76
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - MSK
+ - P79
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: Gaussian CGS (Centimetre-Gram-Second system) unit of displacement
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A77
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: Gaussian CGS (Centimetre-Gram-Second system) unit of electric current
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A78
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: Gaussian CGS (Centimetre-Gram-Second system) unit of electric charge
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A79
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ampere second
+ ConversionFactor: C
+ Symbol: A·s
+ CommonCode: A8
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A8
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: Gaussian CGS (Centimetre-Gram-Second system) unit of electric field strength
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A80
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: Gaussian CGS (Centimetre-Gram-Second system) unit of electric polarization
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A81
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: Gaussian CGS (Centimetre-Gram-Second system) unit of electric potential
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A82
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: Gaussian CGS (Centimetre-Gram-Second system) unit of magnetization
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A83
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigacoulomb per cubic metre
+ ConversionFactor: 10⁹ C/m³
+ Symbol: GC/m³
+ CommonCode: A84
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigaelectronvolt
+ ConversionFactor: 10⁹ eV
+ Symbol: GeV
+ CommonCode: A85
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigahertz
+ ConversionFactor: 10⁹ Hz
+ Symbol: GHz
+ CommonCode: A86
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigaohm
+ ConversionFactor: 10⁹ Ω
+ Symbol: GΩ
+ CommonCode: A87
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigaohm metre
+ ConversionFactor: 10⁹ Ω x m
+ Symbol: GΩ·m
+ CommonCode: A88
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigapascal
+ ConversionFactor: 10⁹ Pa
+ Symbol: GPa
+ CommonCode: A89
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: rate
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: A9
+ Description: A unit of quantity expressed as a rate for usage of a facility or service.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigawatt
+ ConversionFactor: 10⁹ W
+ Symbol: GW
+ CommonCode: A90
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gon
+ ConversionFactor: 1,570 796 x 10⁻² rad
+ Symbol: gon
+ CommonCode: A91
+ Description: 'Synonym: grade'
+ conversion:
+ factor: 0.01570796
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic metre
+ ConversionFactor: 10⁻³ kg/m³
+ Symbol: g/m³
+ CommonCode: A93
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gram per mole
+ ConversionFactor: 10⁻³ kg/mol
+ Symbol: g/mol
+ CommonCode: A94
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D74
+- Status: ''
+ LevelAndCategory: '1'
+ Name: gray
+ ConversionFactor: m²/s²
+ Symbol: Gy
+ CommonCode: A95
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A95
+ - D13
+- Status: ''
+ LevelAndCategory: '1'
+ Name: gray per second
+ ConversionFactor: m²/s³
+ Symbol: Gy/s
+ CommonCode: A96
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A96
+- Status: ''
+ LevelAndCategory: 1S
+ Name: hectopascal
+ ConversionFactor: 10² Pa
+ Symbol: hPa
+ CommonCode: A97
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '1'
+ Name: henry per metre
+ ConversionFactor: H/m
+ Symbol: H/m
+ CommonCode: A98
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A98
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: bit
+ ConversionFactor: ''
+ Symbol: bit
+ CommonCode: A99
+ Description: A unit of information equal to one binary digit.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: ball
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AA
+ Description: 'A unit of count defining the number of balls (ball: object formed
+ in the shape of sphere).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: bulk pack
+ ConversionFactor: ''
+ Symbol: pk
+ CommonCode: AB
+ Description: A unit of count defining the number of items per bulk pack.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: acre
+ ConversionFactor: 4 046,873 m²
+ Symbol: acre
+ CommonCode: ACR
+ Description: ''
+ conversion:
+ factor: 4046.873
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: activity
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ACT
+ Description: 'A unit of count defining the number of activities (activity: a unit
+ of work or action).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: byte
+ ConversionFactor: ''
+ Symbol: byte
+ CommonCode: AD
+ Description: A unit of information equal to 8 bits.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ampere per metre
+ ConversionFactor: A/m
+ Symbol: A/m
+ CommonCode: AE
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - AE
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: additional minute
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AH
+ Description: A unit of time defining the number of minutes in addition to the referenced
+ minutes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: average minute per call
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AI
+ Description: A unit of count defining the number of minutes for the average interval
+ of a call.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: cop
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AJ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: fathom
+ ConversionFactor: 1,828 8 m
+ Symbol: fth
+ CommonCode: AK
+ Description: ''
+ conversion:
+ factor: 1.8288
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: access line
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AL
+ Description: A unit of count defining the number of telephone access lines.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: ampoule
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AM
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: ampere hour
+ ConversionFactor: 3,6 x 10³ C
+ Symbol: A·h
+ CommonCode: AMH
+ Description: A unit of electric charge defining the amount of charge accumulated
+ by a steady flow of one ampere for one hour.
+ conversion:
+ factor: 3600.0
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ampere
+ ConversionFactor: A
+ Symbol: A
+ CommonCode: AMP
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: '2'
+ Name: year
+ ConversionFactor: 3,155 76 x 10⁷ s
+ Symbol: y
+ CommonCode: ANN
+ Description: 'Unit of time equal to 365,25 days.,Synonym: Julian year'
+ conversion:
+ factor: 31557600.0
+ base_units:
+ - H04
+ - SEC
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: aluminium pound only
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AP
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: troy ounce or apothecary ounce
+ ConversionFactor: 3,110 348 x 10⁻³ kg
+ Symbol: tr oz
+ CommonCode: APZ
+ Description: ''
+ conversion:
+ factor: 0.003110348
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: anti-hemophilic factor (AHF) unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AQ
+ Description: A unit of measure for blood potency (US).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: suppository
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AR
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2.0'
+ Name: are
+ ConversionFactor: 10² m²
+ Symbol: a
+ CommonCode: ARE
+ Description: 'Synonym: square decametre'
+ conversion:
+ factor: 100.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: assortment
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AS
+ Description: 'A unit of count defining the number of assortments (assortment: set
+ of items grouped in a mixed collection).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: alcoholic strength by mass
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ASM
+ Description: A unit of mass defining the alcoholic strength of a liquid.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: alcoholic strength by volume
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ASU
+ Description: A unit of volume defining the alcoholic strength of a liquid (e.g.
+ spirit, wine, beer, etc), often at a specific temperature.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: standard atmosphere
+ ConversionFactor: 1 013 25 Pa
+ Symbol: atm
+ CommonCode: ATM
+ Description: ''
+ conversion:
+ factor: 101325.0
+ base_units:
+ - C55
+ - PAL
+- Status: D
+ LevelAndCategory: '2'
+ Name: technical atmosphere
+ ConversionFactor: 98 066,5 Pa
+ Symbol: at
+ CommonCode: ATT
+ Description: ''
+ conversion:
+ factor: 98066.5
+ base_units:
+ - C55
+ - PAL
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: capsule
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AV
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: powder filled vial
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AW
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: american wire gauge
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AWG
+ Description: A unit of distance used for measuring the diameter of small tubes or
+ wires such as the outer diameter of hypotermic or suture needles.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: assembly
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: AY
+ Description: 'A unit of count defining the number of assemblies (assembly: items
+ that consist of component parts).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per pound
+ ConversionFactor: 2 326 J/kg
+ Symbol: BtuIT/lb
+ CommonCode: AZ
+ Description: ''
+ conversion:
+ factor: 2326.0
+ base_units:
+ - J2
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: Btu per cubic foot
+ ConversionFactor: ''
+ Symbol: BTU/ft³
+ CommonCode: B0
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: barrel (US) per day
+ ConversionFactor: 1,840 13 x 10⁻⁶ m³/s
+ Symbol: barrel (US)/d
+ CommonCode: B1
+ Description: ''
+ conversion:
+ factor: 1.84013e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: bit per second
+ ConversionFactor: ''
+ Symbol: bit/s
+ CommonCode: B10
+ Description: A unit of information equal to one binary digit per second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per kilogram kelvin
+ ConversionFactor: J/(kg x K)
+ Symbol: J/(kg·K)
+ CommonCode: B11
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per metre
+ ConversionFactor: J/m
+ Symbol: J/m
+ CommonCode: B12
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B12
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per square metre
+ ConversionFactor: J/m²
+ Symbol: J/m²
+ CommonCode: B13
+ Description: 'Synonym: joule per metre squared'
+ conversion:
+ factor: 1.0
+ base_units:
+ - B13
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per metre to the fourth power
+ ConversionFactor: J/m⁴
+ Symbol: J/m⁴
+ CommonCode: B14
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B14
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per mole
+ ConversionFactor: J/mol
+ Symbol: J/mol
+ CommonCode: B15
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B15
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per mole kelvin
+ ConversionFactor: J/(mol x K)
+ Symbol: J/(mol·K)
+ CommonCode: B16
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B16
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: credit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B17
+ Description: A unit of count defining the number of entries made to the credit side
+ of an account.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule second
+ ConversionFactor: J x s
+ Symbol: J·s
+ CommonCode: B18
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B18
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: digit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B19
+ Description: A unit of information defining the quantity of numerals used to form
+ a number.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: bunk
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B2
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule square metre per kilogram
+ ConversionFactor: J x m²/kg
+ Symbol: J·m²/kg
+ CommonCode: B20
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B20
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kelvin per watt
+ ConversionFactor: K/W
+ Symbol: K/W
+ CommonCode: B21
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B21
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kiloampere
+ ConversionFactor: 10³ A
+ Symbol: kA
+ CommonCode: B22
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kiloampere per square metre
+ ConversionFactor: 10³ A/m²
+ Symbol: kA/m²
+ CommonCode: B23
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - A41
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kiloampere per metre
+ ConversionFactor: 10³ A/m
+ Symbol: kA/m
+ CommonCode: B24
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - AE
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilobecquerel per kilogram
+ ConversionFactor: 10³ Bq/kg
+ Symbol: kBq/kg
+ CommonCode: B25
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilocoulomb
+ ConversionFactor: 10³ C
+ Symbol: kC
+ CommonCode: B26
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilocoulomb per cubic metre
+ ConversionFactor: 10³ C/m³
+ Symbol: kC/m³
+ CommonCode: B27
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - A29
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilocoulomb per square metre
+ ConversionFactor: 10³ C/m²
+ Symbol: kC/m²
+ CommonCode: B28
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - A34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kiloelectronvolt
+ ConversionFactor: 10³ eV
+ Symbol: keV
+ CommonCode: B29
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: batting pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B3
+ Description: A unit of mass defining the number of pounds of wadded fibre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gibibit
+ ConversionFactor: ''
+ Symbol: Gibit
+ CommonCode: B30
+ Description: A unit of information equal to 2³⁰ bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram metre per second
+ ConversionFactor: kg x m/s
+ Symbol: kg·m/s
+ CommonCode: B31
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B31
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram metre squared
+ ConversionFactor: kg x m²
+ Symbol: kg·m²
+ CommonCode: B32
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B32
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram metre squared per second
+ ConversionFactor: kg x m²/s
+ Symbol: kg·m²/s
+ CommonCode: B33
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B33
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilogram per cubic decimetre
+ ConversionFactor: 10³ kg/m³
+ Symbol: kg/dm³
+ CommonCode: B34
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilogram per litre
+ ConversionFactor: 10³ kg/m³
+ Symbol: kg/l or kg/L
+ CommonCode: B35
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - GL
+ - KMQ
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per gram
+ ConversionFactor: 4 184 J/kg
+ Symbol: calth/g
+ CommonCode: B36
+ Description: ''
+ conversion:
+ factor: 4184.0
+ base_units:
+ - J2
+- Status: D
+ LevelAndCategory: '2'
+ Name: kilogram-force
+ ConversionFactor: 9,806 65 N
+ Symbol: kgf
+ CommonCode: B37
+ Description: ''
+ conversion:
+ factor: 9.80665
+ base_units: []
+- Status: D
+ LevelAndCategory: '2'
+ Name: kilogram-force metre
+ ConversionFactor: 9,806 65 N x m
+ Symbol: kgf·m
+ CommonCode: B38
+ Description: ''
+ conversion:
+ factor: 9.80665
+ base_units:
+ - NU
+- Status: D
+ LevelAndCategory: '2'
+ Name: kilogram-force metre per second
+ ConversionFactor: 9,806 65 W
+ Symbol: kgf·m/s
+ CommonCode: B39
+ Description: ''
+ conversion:
+ factor: 9.80665
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: barrel, imperial
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B4
+ Description: A unit of volume used to measure beer. One beer barrel equals 36 imperial
+ gallons.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: kilogram-force per square metre
+ ConversionFactor: 9,806 65 Pa
+ Symbol: kgf/m²
+ CommonCode: B40
+ Description: ''
+ conversion:
+ factor: 9.80665
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilojoule per kelvin
+ ConversionFactor: 10³ J/K
+ Symbol: kJ/K
+ CommonCode: B41
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - JE
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilojoule per kilogram
+ ConversionFactor: 10³ J/kg
+ Symbol: kJ/kg
+ CommonCode: B42
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - J2
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilojoule per kilogram kelvin
+ ConversionFactor: 10³ J/(kg x K)
+ Symbol: kJ/(kg·K)
+ CommonCode: B43
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilojoule per mole
+ ConversionFactor: 10³ J/mol
+ Symbol: kJ/mol
+ CommonCode: B44
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - B15
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilomole
+ ConversionFactor: 10³ mol
+ Symbol: kmol
+ CommonCode: B45
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilomole per cubic metre
+ ConversionFactor: 10³ mol/m³
+ Symbol: kmol/m³
+ CommonCode: B46
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C36
+ - M33
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilonewton
+ ConversionFactor: 10³ N
+ Symbol: kN
+ CommonCode: B47
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilonewton metre
+ ConversionFactor: 10³ N x m
+ Symbol: kN·m
+ CommonCode: B48
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kiloohm
+ ConversionFactor: 10³ Ω
+ Symbol: kΩ
+ CommonCode: B49
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - OHM
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: billet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B5
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kiloohm metre
+ ConversionFactor: 10³ Ω x m
+ Symbol: kΩ·m
+ CommonCode: B50
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C61
+- Status: D
+ LevelAndCategory: '2'
+ Name: kilopond
+ ConversionFactor: 9,806 65 N
+ Symbol: kp
+ CommonCode: B51
+ Description: 'Synonym: kilogram-force'
+ conversion:
+ factor: 9.80665
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilosecond
+ ConversionFactor: 10³ s
+ Symbol: ks
+ CommonCode: B52
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilosiemens
+ ConversionFactor: 10³ S
+ Symbol: kS
+ CommonCode: B53
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - NQ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilosiemens per metre
+ ConversionFactor: 10³ S/m
+ Symbol: kS/m
+ CommonCode: B54
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilovolt per metre
+ ConversionFactor: 10³ V/m
+ Symbol: kV/m
+ CommonCode: B55
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D50
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kiloweber per metre
+ ConversionFactor: 10³ Wb/m
+ Symbol: kWb/m
+ CommonCode: B56
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D59
+- Status: ''
+ LevelAndCategory: '2'
+ Name: light year
+ ConversionFactor: 9,460 73 x 10¹⁵ m
+ Symbol: ly
+ CommonCode: B57
+ Description: A unit of length defining the distance that light travels in a vacuum
+ in one year.
+ conversion:
+ factor: 9.46073e+15
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per mole
+ ConversionFactor: 10⁻³ m³/mol
+ Symbol: l/mol
+ CommonCode: B58
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - A40
+- Status: ''
+ LevelAndCategory: 1S
+ Name: lumen hour
+ ConversionFactor: 3,6 x 10³ s x cd x sr
+ Symbol: lm·h
+ CommonCode: B59
+ Description: ''
+ conversion:
+ factor: 3600.0
+ base_units:
+ - B62
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: bun
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B6
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: lumen per square metre
+ ConversionFactor: cd x sr/m²
+ Symbol: lm/m²
+ CommonCode: B60
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B60
+- Status: ''
+ LevelAndCategory: '1'
+ Name: lumen per watt
+ ConversionFactor: cd x sr/W
+ Symbol: lm/W
+ CommonCode: B61
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B61
+- Status: ''
+ LevelAndCategory: '1'
+ Name: lumen second
+ ConversionFactor: s x cd x sr
+ Symbol: lm·s
+ CommonCode: B62
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B62
+- Status: ''
+ LevelAndCategory: 1S
+ Name: lux hour
+ ConversionFactor: 3,6 x 10³ s x cd x sr / m²
+ Symbol: lx·h
+ CommonCode: B63
+ Description: ''
+ conversion:
+ factor: 3600.0
+ base_units:
+ - B64
+- Status: ''
+ LevelAndCategory: '1'
+ Name: lux second
+ ConversionFactor: s x cd x sr / m²
+ Symbol: lx·s
+ CommonCode: B64
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B64
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: maxwell
+ ConversionFactor: 10⁻⁸ Wb
+ Symbol: Mx
+ CommonCode: B65
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - WEB
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megaampere per square metre
+ ConversionFactor: 10⁶ A/m²
+ Symbol: MA/m²
+ CommonCode: B66
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - A41
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megabecquerel per kilogram
+ ConversionFactor: 10⁶ Bq/kg
+ Symbol: MBq/kg
+ CommonCode: B67
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gigabit
+ ConversionFactor: ''
+ Symbol: Gbit
+ CommonCode: B68
+ Description: A unit of information equal to 10⁹ bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megacoulomb per cubic metre
+ ConversionFactor: 10⁶ C/m³
+ Symbol: MC/m³
+ CommonCode: B69
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - A29
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: cycle
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B7
+ Description: 'A unit of count defining the number of cycles (cycle: a recurrent
+ period of definite duration).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megacoulomb per square metre
+ ConversionFactor: 10⁶ C/m²
+ Symbol: MC/m²
+ CommonCode: B70
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - A34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megaelectronvolt
+ ConversionFactor: 10⁶ eV
+ Symbol: MeV
+ CommonCode: B71
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megagram per cubic metre
+ ConversionFactor: 10³ kg/m³
+ Symbol: Mg/m³
+ CommonCode: B72
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: meganewton
+ ConversionFactor: 10⁶ N
+ Symbol: MN
+ CommonCode: B73
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: meganewton metre
+ ConversionFactor: 10⁶ N x m
+ Symbol: MN·m
+ CommonCode: B74
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megaohm
+ ConversionFactor: 10⁶ Ω
+ Symbol: MΩ
+ CommonCode: B75
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - OHM
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megaohm metre
+ ConversionFactor: 10⁶ Ω x m
+ Symbol: MΩ·m
+ CommonCode: B76
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - C61
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megasiemens per metre
+ ConversionFactor: 10⁶ S/m
+ Symbol: MS/m
+ CommonCode: B77
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megavolt
+ ConversionFactor: 10⁶ V
+ Symbol: MV
+ CommonCode: B78
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megavolt per metre
+ ConversionFactor: 10⁶ V/m
+ Symbol: MV/m
+ CommonCode: B79
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - D50
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per cubic metre
+ ConversionFactor: J/m³
+ Symbol: J/m³
+ CommonCode: B8
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B8
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gigabit per second
+ ConversionFactor: ''
+ Symbol: Gbit/s
+ CommonCode: B80
+ Description: A unit of information equal to 10⁹ bits (binary digits) per second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal metre squared reciprocal second
+ ConversionFactor: m⁻²/s
+ Symbol: m⁻²/s
+ CommonCode: B81
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B81
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: inch per linear foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B82
+ Description: A unit of length defining the number of inches per linear foot.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: metre to the fourth power
+ ConversionFactor: m⁴
+ Symbol: m⁴
+ CommonCode: B83
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - B83
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microampere
+ ConversionFactor: 10⁻⁶ A
+ Symbol: µA
+ CommonCode: B84
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microbar
+ ConversionFactor: 10⁻¹ Pa
+ Symbol: µbar
+ CommonCode: B85
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microcoulomb
+ ConversionFactor: 10⁻⁶ C
+ Symbol: µC
+ CommonCode: B86
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microcoulomb per cubic metre
+ ConversionFactor: 10⁻⁶ C/m³
+ Symbol: µC/m³
+ CommonCode: B87
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - A29
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microcoulomb per square metre
+ ConversionFactor: 10⁻⁶ C/m²
+ Symbol: µC/m²
+ CommonCode: B88
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - A34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microfarad per metre
+ ConversionFactor: 10⁻⁶ F/m
+ Symbol: µF/m
+ CommonCode: B89
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: batt
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: B9
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microhenry
+ ConversionFactor: 10⁻⁶ H
+ Symbol: µH
+ CommonCode: B90
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - '81'
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microhenry per metre
+ ConversionFactor: 10⁻⁶ H/m
+ Symbol: µH/m
+ CommonCode: B91
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - A98
+- Status: ''
+ LevelAndCategory: 1S
+ Name: micronewton
+ ConversionFactor: 10⁻⁶ N
+ Symbol: µN
+ CommonCode: B92
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: micronewton metre
+ ConversionFactor: 10⁻⁶ N x m
+ Symbol: µN·m
+ CommonCode: B93
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microohm
+ ConversionFactor: 10⁻⁶ Ω
+ Symbol: µΩ
+ CommonCode: B94
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - OHM
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microohm metre
+ ConversionFactor: 10⁻⁶ Ω x m
+ Symbol: µΩ·m
+ CommonCode: B95
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - C61
+- Status: ''
+ LevelAndCategory: 1S
+ Name: micropascal
+ ConversionFactor: 10⁻⁶ Pa
+ Symbol: µPa
+ CommonCode: B96
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microradian
+ ConversionFactor: 10⁻⁶ rad
+ Symbol: µrad
+ CommonCode: B97
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microsecond
+ ConversionFactor: 10⁻⁶ s
+ Symbol: µs
+ CommonCode: B98
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microsiemens
+ ConversionFactor: 10⁻⁶ S
+ Symbol: µS
+ CommonCode: B99
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - NQ
+- Status: ''
+ LevelAndCategory: '1'
+ Name: bar [unit of pressure]
+ ConversionFactor: 10⁵ Pa
+ Symbol: bar
+ CommonCode: BAR
+ Description: ''
+ conversion:
+ factor: 100000.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: base box
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BB
+ Description: A unit of area of 112 sheets of tin mil products (tin plate, tin free
+ steel or black plate) 14 by 20 inches, or 31,360 square inches.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: board
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BD
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bundle
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BE
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: board foot
+ ConversionFactor: ''
+ Symbol: fbm
+ CommonCode: BFT
+ Description: 'A unit of volume defining the number of cords (cord: a stack of firewood
+ of 128 cubic feet).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bag
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BG
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: brush
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BH
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: brake horse power
+ ConversionFactor: 7,457 x 10² W
+ Symbol: ''
+ CommonCode: BHP
+ Description: ''
+ conversion:
+ factor: 745.7
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: billion (EUR)
+ ConversionFactor: 10¹²
+ Symbol: ''
+ CommonCode: BIL
+ Description: 'Synonym: trillion (US)'
+ conversion:
+ factor: 1000000000000.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bucket
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BJ
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: basket
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BK
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bale
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BL
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: dry barrel (US)
+ ConversionFactor: 1,156 27 x 10⁻¹ m³
+ Symbol: bbl (US)
+ CommonCode: BLD
+ Description: ''
+ conversion:
+ factor: 0.115627
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (US)
+ ConversionFactor: 158,987 3 x 10⁻³ m³
+ Symbol: barrel (US)
+ CommonCode: BLL
+ Description: ''
+ conversion:
+ factor: 0.1589873
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bottle
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BO
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: hundred board foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BP
+ Description: A unit of volume equal to one hundred board foot.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: beats per minute
+ ConversionFactor: 1.667 x 10-2 /s
+ Symbol: ''
+ CommonCode: BPM
+ Description: The number of beats per minute.
+ conversion:
+ factor: 1.0
+ base_units:
+ - BPM
+ - OPM
+- Status: ''
+ LevelAndCategory: '1'
+ Name: becquerel
+ ConversionFactor: 27,027 x 10⁻¹² Ci
+ Symbol: Bq
+ CommonCode: BQL
+ Description: ''
+ conversion:
+ factor: 2.7027e-11
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bar [unit of packaging]
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BR
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bolt
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BT
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table)
+ ConversionFactor: 1,055 056 x 10³ J
+ Symbol: BtuIT
+ CommonCode: BTU
+ Description: ''
+ conversion:
+ factor: 1055.056
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (US)
+ ConversionFactor: 3,523 907 x 10⁻² m³
+ Symbol: bu (US)
+ CommonCode: BUA
+ Description: ''
+ conversion:
+ factor: 0.03523907
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (UK)
+ ConversionFactor: 3,636 872 x 10⁻² m³
+ Symbol: bushel (UK)
+ CommonCode: BUI
+ Description: ''
+ conversion:
+ factor: 0.03636872
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: base weight
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BW
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: box
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BX
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: million BTUs
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: BZ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: call
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C0
+ Description: 'A unit of count defining the number of calls (call: communication
+ session or visitation).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: composite product pound (total weight)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C1
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millifarad
+ ConversionFactor: 10⁻³ F
+ Symbol: mF
+ CommonCode: C10
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - FAR
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligal
+ ConversionFactor: 10⁻⁵ m/s²
+ Symbol: mGal
+ CommonCode: C11
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milligram per metre
+ ConversionFactor: 10⁻⁶ kg/m
+ Symbol: mg/m
+ CommonCode: C12
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milligray
+ ConversionFactor: 10⁻³ Gy
+ Symbol: mGy
+ CommonCode: C13
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millihenry
+ ConversionFactor: 10⁻³ H
+ Symbol: mH
+ CommonCode: C14
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - '81'
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millijoule
+ ConversionFactor: 10⁻³ J
+ Symbol: mJ
+ CommonCode: C15
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millimetre per second
+ ConversionFactor: 10⁻³ m/s
+ Symbol: mm/s
+ CommonCode: C16
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millimetre squared per second
+ ConversionFactor: 10⁻⁶ m²/s
+ Symbol: mm²/s
+ CommonCode: C17
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - S4
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millimole
+ ConversionFactor: 10⁻³ mol
+ Symbol: mmol
+ CommonCode: C18
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C34
+- Status: ''
+ LevelAndCategory: '1'
+ Name: mole per kilogram
+ ConversionFactor: mol/kg
+ Symbol: mol/kg
+ CommonCode: C19
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C19
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: carset
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C2
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millinewton
+ ConversionFactor: 10⁻³ N
+ Symbol: mN
+ CommonCode: C20
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kibibit
+ ConversionFactor: ''
+ Symbol: Kibit
+ CommonCode: C21
+ Description: A unit of information equal to 2¹⁰ (1024) bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millinewton per metre
+ ConversionFactor: 10⁻³ N/m
+ Symbol: mN/m
+ CommonCode: C22
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - 4P
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milliohm metre
+ ConversionFactor: 10⁻³ Ω x m
+ Symbol: mΩ·m
+ CommonCode: C23
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C61
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millipascal second
+ ConversionFactor: 10⁻³ Pa x s
+ Symbol: mPa·s
+ CommonCode: C24
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milliradian
+ ConversionFactor: 10⁻³ rad
+ Symbol: mrad
+ CommonCode: C25
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millisecond
+ ConversionFactor: 10⁻³ s
+ Symbol: ms
+ CommonCode: C26
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millisiemens
+ ConversionFactor: 10⁻³ S
+ Symbol: mS
+ CommonCode: C27
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - NQ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millisievert
+ ConversionFactor: 10⁻³ Sv
+ Symbol: mSv
+ CommonCode: C28
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millitesla
+ ConversionFactor: 10⁻³ T
+ Symbol: mT
+ CommonCode: C29
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D33
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microvolt per metre
+ ConversionFactor: 10⁻⁶ V/m
+ Symbol: µV/m
+ CommonCode: C3
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - D50
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millivolt per metre
+ ConversionFactor: 10⁻³ V/m
+ Symbol: mV/m
+ CommonCode: C30
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D50
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milliwatt
+ ConversionFactor: 10⁻³ W
+ Symbol: mW
+ CommonCode: C31
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milliwatt per square metre
+ ConversionFactor: 10⁻³ W/m²
+ Symbol: mW/m²
+ CommonCode: C32
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milliweber
+ ConversionFactor: 10⁻³ Wb
+ Symbol: mWb
+ CommonCode: C33
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - WEB
+- Status: ''
+ LevelAndCategory: '1'
+ Name: mole
+ ConversionFactor: mol
+ Symbol: mol
+ CommonCode: C34
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: mole per cubic decimetre
+ ConversionFactor: 10³ mol/m³
+ Symbol: mol/dm³
+ CommonCode: C35
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C36
+ - M33
+- Status: ''
+ LevelAndCategory: '1'
+ Name: mole per cubic metre
+ ConversionFactor: mol/m³
+ Symbol: mol/m³
+ CommonCode: C36
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C36
+ - M33
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kilobit
+ ConversionFactor: ''
+ Symbol: kbit
+ CommonCode: C37
+ Description: A unit of information equal to 10³ (1000) bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: mole per litre
+ ConversionFactor: 10³ mol/m³
+ Symbol: mol/l
+ CommonCode: C38
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C36
+ - M33
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanoampere
+ ConversionFactor: 10⁻⁹ A
+ Symbol: nA
+ CommonCode: C39
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: carload
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C4
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanocoulomb
+ ConversionFactor: 10⁻⁹ C
+ Symbol: nC
+ CommonCode: C40
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanofarad
+ ConversionFactor: 10⁻⁹ F
+ Symbol: nF
+ CommonCode: C41
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanofarad per metre
+ ConversionFactor: 10⁻⁹ F/m
+ Symbol: nF/m
+ CommonCode: C42
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanohenry
+ ConversionFactor: 10⁻⁹ H
+ Symbol: nH
+ CommonCode: C43
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanohenry per metre
+ ConversionFactor: 10⁻⁹ H/m
+ Symbol: nH/m
+ CommonCode: C44
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanometre
+ ConversionFactor: 10⁻⁹ m
+ Symbol: nm
+ CommonCode: C45
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanoohm metre
+ ConversionFactor: 10⁻⁹ Ω·x m
+ Symbol: nΩ·m
+ CommonCode: C46
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanosecond
+ ConversionFactor: 10⁻⁹ s
+ Symbol: ns
+ CommonCode: C47
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanotesla
+ ConversionFactor: 10⁻⁹ T
+ Symbol: nT
+ CommonCode: C48
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanowatt
+ ConversionFactor: 10⁻⁹ W
+ Symbol: nW
+ CommonCode: C49
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: cost
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C5
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: neper
+ ConversionFactor: Np
+ Symbol: Np
+ CommonCode: C50
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C50
+- Status: ''
+ LevelAndCategory: '1'
+ Name: neper per second
+ ConversionFactor: Np/s
+ Symbol: Np/s
+ CommonCode: C51
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C51
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picometre
+ ConversionFactor: 10⁻¹² m
+ Symbol: pm
+ CommonCode: C52
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '1'
+ Name: newton metre second
+ ConversionFactor: N x m x s
+ Symbol: N·m·s
+ CommonCode: C53
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C53
+- Status: ''
+ LevelAndCategory: '1'
+ Name: newton metre squared per kilogram squared
+ ConversionFactor: N x m²/kg²
+ Symbol: N·m²/kg²
+ CommonCode: C54
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C54
+- Status: ''
+ LevelAndCategory: 1S
+ Name: newton per square metre
+ ConversionFactor: Pa
+ Symbol: N/m²
+ CommonCode: C55
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: newton per square millimetre
+ ConversionFactor: 10⁶ Pa
+ Symbol: N/mm²
+ CommonCode: C56
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '1'
+ Name: newton second
+ ConversionFactor: N x s
+ Symbol: N·s
+ CommonCode: C57
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C57
+- Status: ''
+ LevelAndCategory: '1'
+ Name: newton second per metre
+ ConversionFactor: N x s/m
+ Symbol: N·s/m
+ CommonCode: C58
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C58
+- Status: ''
+ LevelAndCategory: '1'
+ Name: octave
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C59
+ Description: A unit used in music to describe the ratio in frequency between notes.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: cell
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C6
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: ohm centimetre
+ ConversionFactor: '10⁻² Ω x m '
+ Symbol: Ω·cm
+ CommonCode: C60
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - C61
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ohm metre
+ ConversionFactor: Ω x m
+ Symbol: Ω·m
+ CommonCode: C61
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C61
+- Status: ''
+ LevelAndCategory: '1'
+ Name: one
+ ConversionFactor: '1'
+ Symbol: '1'
+ CommonCode: C62
+ Description: 'Synonym: unit'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: parsec
+ ConversionFactor: 3,085 678 x 10¹⁶ m
+ Symbol: pc
+ CommonCode: C63
+ Description: ''
+ conversion:
+ factor: 3.085678e+16
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '1'
+ Name: pascal per kelvin
+ ConversionFactor: Pa/K
+ Symbol: Pa/K
+ CommonCode: C64
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C64
+- Status: ''
+ LevelAndCategory: '1'
+ Name: pascal second
+ ConversionFactor: Pa x s
+ Symbol: Pa·s
+ CommonCode: C65
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '1'
+ Name: pascal second per cubic metre
+ ConversionFactor: Pa x s/m³
+ Symbol: Pa·s/m³
+ CommonCode: C66
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C66
+- Status: ''
+ LevelAndCategory: '1'
+ Name: pascal second per metre
+ ConversionFactor: Pa x s/m
+ Symbol: Pa· s/m
+ CommonCode: C67
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C67
+- Status: ''
+ LevelAndCategory: 1S
+ Name: petajoule
+ ConversionFactor: 10¹⁵ J
+ Symbol: PJ
+ CommonCode: C68
+ Description: ''
+ conversion:
+ factor: 1.0e+15
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '1'
+ Name: phon
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C69
+ Description: A unit of subjective sound loudness. A sound has loudness p phons if
+ it seems to the listener to be equal in loudness to the sound of a pure tone of
+ frequency 1 kilohertz and strength p decibels.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centipoise
+ ConversionFactor: 10⁻³ Pa x s
+ Symbol: cP
+ CommonCode: C7
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picoampere
+ ConversionFactor: 10⁻¹² A
+ Symbol: pA
+ CommonCode: C70
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picocoulomb
+ ConversionFactor: 10⁻¹² C
+ Symbol: pC
+ CommonCode: C71
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picofarad per metre
+ ConversionFactor: 10⁻¹² F/m
+ Symbol: pF/m
+ CommonCode: C72
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picohenry
+ ConversionFactor: 10⁻¹² H
+ Symbol: pH
+ CommonCode: C73
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - '81'
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kilobit per second
+ ConversionFactor: 10³ bit/s
+ Symbol: kbit/s
+ CommonCode: C74
+ Description: A unit of information equal to 10³ (1000) bits (binary digits) per
+ second.
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picowatt
+ ConversionFactor: 10⁻¹² W
+ Symbol: pW
+ CommonCode: C75
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picowatt per square metre
+ ConversionFactor: 10⁻¹² W/m²
+ Symbol: pW/m²
+ CommonCode: C76
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - D54
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: pound gage
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C77
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force
+ ConversionFactor: 4,448 222 N
+ Symbol: lbf
+ CommonCode: C78
+ Description: ''
+ conversion:
+ factor: 4.448222
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilovolt ampere hour
+ ConversionFactor: ''
+ Symbol: kVAh
+ CommonCode: C79
+ Description: A unit of accumulated energy of 1000 volt amperes over a period of
+ one hour.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millicoulomb per kilogram
+ ConversionFactor: 10⁻³ C/kg
+ Symbol: mC/kg
+ CommonCode: C8
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: rad
+ ConversionFactor: 10⁻² Gy
+ Symbol: rad
+ CommonCode: C80
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: radian
+ ConversionFactor: rad
+ Symbol: rad
+ CommonCode: C81
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: '1'
+ Name: radian square metre per mole
+ ConversionFactor: rad x m²/mol
+ Symbol: rad·m²/mol
+ CommonCode: C82
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C82
+- Status: ''
+ LevelAndCategory: '1'
+ Name: radian square metre per kilogram
+ ConversionFactor: rad x m²/kg
+ Symbol: rad·m²/kg
+ CommonCode: C83
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C83
+- Status: ''
+ LevelAndCategory: '1'
+ Name: radian per metre
+ ConversionFactor: rad/m
+ Symbol: rad/m
+ CommonCode: C84
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C84
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal angstrom
+ ConversionFactor: 10¹⁰ m⁻¹
+ Symbol: Å⁻¹
+ CommonCode: C85
+ Description: ''
+ conversion:
+ factor: 10000000000.0
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal cubic metre
+ ConversionFactor: m⁻³
+ Symbol: m⁻³
+ CommonCode: C86
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C86
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal cubic metre per second
+ ConversionFactor: m⁻³/s
+ Symbol: m⁻³/s
+ CommonCode: C87
+ Description: 'Synonym: reciprocal second per cubic metre'
+ conversion:
+ factor: 1.0
+ base_units:
+ - C87
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal electron volt per cubic metre
+ ConversionFactor: 6,241 46 x 10¹⁸ J⁻¹/m³
+ Symbol: eV⁻¹/m³
+ CommonCode: C88
+ Description: ''
+ conversion:
+ factor: 6.24146e+18
+ base_units:
+ - C90
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal henry
+ ConversionFactor: H⁻¹
+ Symbol: H⁻¹
+ CommonCode: C89
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C89
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: coil group
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: C9
+ Description: 'A unit of count defining the number of coil groups (coil group: groups
+ of items arranged by lengths of those items placed in a joined sequence of concentric
+ circles).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal joule per cubic metre
+ ConversionFactor: J⁻¹/m³
+ Symbol: J⁻¹/m³
+ CommonCode: C90
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C90
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal kelvin or kelvin to the power minus one
+ ConversionFactor: K⁻¹
+ Symbol: K⁻¹
+ CommonCode: C91
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C91
+ - N83
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal metre
+ ConversionFactor: m⁻¹
+ Symbol: m⁻¹
+ CommonCode: C92
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal square metre
+ ConversionFactor: m⁻²
+ Symbol: m⁻²
+ CommonCode: C93
+ Description: 'Synonym: reciprocal metre squared'
+ conversion:
+ factor: 1.0
+ base_units:
+ - C93
+- Status: ''
+ LevelAndCategory: 1S
+ Name: reciprocal minute
+ ConversionFactor: 1,666 667 x 10⁻² s
+ Symbol: min⁻¹
+ CommonCode: C94
+ Description: ''
+ conversion:
+ factor: 0.01666667
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal mole
+ ConversionFactor: mol⁻¹
+ Symbol: mol⁻¹
+ CommonCode: C95
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C95
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal pascal or pascal to the power minus one
+ ConversionFactor: Pa⁻¹
+ Symbol: Pa⁻¹
+ CommonCode: C96
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C96
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal second
+ ConversionFactor: s⁻¹
+ Symbol: s⁻¹
+ CommonCode: C97
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C97
+- Status: X
+ LevelAndCategory: '1'
+ Name: reciprocal second per cubic metre
+ ConversionFactor: s⁻¹/m³
+ Symbol: s⁻¹/m³
+ CommonCode: C98
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C98
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal second per metre squared
+ ConversionFactor: s⁻¹/m²
+ Symbol: s⁻¹/m²
+ CommonCode: C99
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C99
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: can
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CA
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: carrying capacity in metric ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CCT
+ Description: A unit of mass defining the carrying capacity, expressed as the number
+ of metric tons.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: candela
+ ConversionFactor: cd
+ Symbol: cd
+ CommonCode: CDL
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - CDL
+- Status: ''
+ LevelAndCategory: '1'
+ Name: degree Celsius
+ ConversionFactor: 1 x K
+ Symbol: "°C"
+ CommonCode: CEL
+ Description: 'Refer ISO 80000-5 (Quantities and units — Part 5: Thermodynamics)'
+ conversion:
+ factor: 1.0
+ base_units:
+ - CEL
+ - KEL
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: hundred
+ ConversionFactor: '100'
+ Symbol: ''
+ CommonCode: CEN
+ Description: A unit of count defining the number of units in multiples of 100.
+ conversion:
+ factor: 100.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: card
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CG
+ Description: 'A unit of count defining the number of units of card (card: thick
+ stiff paper or cardboard).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: centigram
+ ConversionFactor: 10⁻⁵ kg
+ Symbol: cg
+ CommonCode: CGM
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - KGM
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: container
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CH
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: cone
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CJ
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: connector
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CK
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb per kilogram
+ ConversionFactor: A x s/kg
+ Symbol: C/kg
+ CommonCode: CKG
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - CKG
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: coil
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CL
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: hundred leave
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CLF
+ Description: A unit of count defining the number of leaves, expressed in units of
+ one hundred leaves.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: centilitre
+ ConversionFactor: 10⁻⁵ m³
+ Symbol: cl
+ CommonCode: CLT
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: square centimetre
+ ConversionFactor: 10⁻⁴ m²
+ Symbol: cm²
+ CommonCode: CMK
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic centimetre
+ ConversionFactor: 10⁻⁶ m³
+ Symbol: cm³
+ CommonCode: CMQ
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S,3.5
+ Name: centimetre
+ ConversionFactor: 10⁻² m
+ Symbol: cm
+ CommonCode: CMT
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 3.2,3.8
+ Name: hundred pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CNP
+ Description: 'A unit of count defining the number of hundred-packs (hundred-pack:
+ set of one hundred items packaged together).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: cental (UK)
+ ConversionFactor: 45,359 237 kg
+ Symbol: ''
+ CommonCode: CNT
+ Description: A unit of mass equal to one hundred weight (US).
+ conversion:
+ factor: 45.359237
+ base_units:
+ - KGM
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: carboy
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CO
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: coulomb
+ ConversionFactor: A x s
+ Symbol: C
+ CommonCode: COU
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - COU
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: cartridge
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CQ
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: crate
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CR
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: case
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CS
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: carton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CT
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: content gram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CTG
+ Description: A unit of mass defining the number of grams of a named item in a product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: metric carat
+ ConversionFactor: 200 mg
+ Symbol: ''
+ CommonCode: CTM
+ Description: ''
+ conversion:
+ factor: 200.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: content ton (metric)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CTN
+ Description: A unit of mass defining the number of metric tons of a named item in
+ a product.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: cup
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CU
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: curie
+ ConversionFactor: 3,7 x 10¹⁰ Bq
+ Symbol: Ci
+ CommonCode: CUR
+ Description: ''
+ conversion:
+ factor: 37000000000.0
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: cover
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CV
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: hundred pound (cwt) / hundred weight (US)
+ ConversionFactor: 45,359 2 kg
+ Symbol: cwt (US)
+ CommonCode: CWA
+ Description: ''
+ conversion:
+ factor: 45.3592
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '2'
+ Name: hundred weight (UK)
+ ConversionFactor: 50,802 35 kg
+ Symbol: cwt (UK)
+ CommonCode: CWI
+ Description: ''
+ conversion:
+ factor: 50.80235
+ base_units:
+ - KGM
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: cylinder
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CY
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: combo
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: CZ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilowatt hour per hour
+ ConversionFactor: ''
+ Symbol: kW·h/h
+ CommonCode: D03
+ Description: A unit of accumulated energy of a thousand watts over a period of one
+ hour.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: lot [unit of weight]
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D04
+ Description: A unit of weight equal to about 1/2 ounce or 15 grams.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal second per steradian
+ ConversionFactor: s⁻¹/sr
+ Symbol: s⁻¹/sr
+ CommonCode: D1
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D1
+- Status: ''
+ LevelAndCategory: '1'
+ Name: siemens per metre
+ ConversionFactor: S/m
+ Symbol: S/m
+ CommonCode: D10
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: mebibit
+ ConversionFactor: ''
+ Symbol: Mibit
+ CommonCode: D11
+ Description: A unit of information equal to 2²⁰ (1048576) bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: siemens square metre per mole
+ ConversionFactor: S x m²/mol
+ Symbol: S·m²/mol
+ CommonCode: D12
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D12
+- Status: ''
+ LevelAndCategory: '1'
+ Name: sievert
+ ConversionFactor: m²/s²
+ Symbol: Sv
+ CommonCode: D13
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - A95
+ - D13
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand linear yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D14
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: sone
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D15
+ Description: A unit of subjective sound loudness. One sone is the loudness of a
+ pure tone of frequency one kilohertz and strength 40 decibels.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square centimetre per erg
+ ConversionFactor: 10³ m²/J
+ Symbol: cm²/erg
+ CommonCode: D16
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D20
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square centimetre per steradian erg
+ ConversionFactor: 10³ m²/(sr x J)
+ Symbol: cm²/(sr·erg)
+ CommonCode: D17
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D25
+- Status: ''
+ LevelAndCategory: '1'
+ Name: metre kelvin
+ ConversionFactor: m x K
+ Symbol: m·K
+ CommonCode: D18
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D18
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre kelvin per watt
+ ConversionFactor: m² x K/W
+ Symbol: m²·K/W
+ CommonCode: D19
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D19
+- Status: ''
+ LevelAndCategory: '1'
+ Name: reciprocal second per steradian metre squared
+ ConversionFactor: s⁻¹/(sr x m²)
+ Symbol: s⁻¹/(sr·m²)
+ CommonCode: D2
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D2
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre per joule
+ ConversionFactor: m²/J
+ Symbol: m²/J
+ CommonCode: D20
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D20
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre per kilogram
+ ConversionFactor: m²/kg
+ Symbol: m²/kg
+ CommonCode: D21
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D21
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre per mole
+ ConversionFactor: m²/mol
+ Symbol: m²/mol
+ CommonCode: D22
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D22
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: pen gram (protein)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D23
+ Description: A unit of count defining the number of grams of amino acid prescribed
+ for parenteral/enteral therapy.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre per steradian
+ ConversionFactor: m²/sr
+ Symbol: m²/sr
+ CommonCode: D24
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D24
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre per steradian joule
+ ConversionFactor: m²/(sr x J)
+ Symbol: m²/(sr·J)
+ CommonCode: D25
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D25
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre per volt second
+ ConversionFactor: m²/(V x s)
+ Symbol: m²/(V·s)
+ CommonCode: D26
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D26
+- Status: ''
+ LevelAndCategory: '1'
+ Name: steradian
+ ConversionFactor: sr
+ Symbol: sr
+ CommonCode: D27
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D27
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: syphon
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D28
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: terahertz
+ ConversionFactor: 10¹² Hz
+ Symbol: THz
+ CommonCode: D29
+ Description: ''
+ conversion:
+ factor: 1000000000000.0
+ base_units:
+ - HTZ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: terajoule
+ ConversionFactor: 10¹² J
+ Symbol: TJ
+ CommonCode: D30
+ Description: ''
+ conversion:
+ factor: 1000000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: terawatt
+ ConversionFactor: 10¹² W
+ Symbol: TW
+ CommonCode: D31
+ Description: ''
+ conversion:
+ factor: 1000000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: terawatt hour
+ ConversionFactor: 3,6 x 10¹⁵ J
+ Symbol: TW·h
+ CommonCode: D32
+ Description: ''
+ conversion:
+ factor: 3.6e+15
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '1'
+ Name: tesla
+ ConversionFactor: T
+ Symbol: T
+ CommonCode: D33
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D33
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: tex
+ ConversionFactor: 10⁻⁶ kg/m
+ Symbol: tex (g/km)
+ CommonCode: D34
+ Description: A unit of yarn density. One decitex equals a mass of 1 gram per 1 kilometre
+ of length.
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - KL
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical)
+ ConversionFactor: 4,184 J
+ Symbol: calth
+ CommonCode: D35
+ Description: ''
+ conversion:
+ factor: 4.184
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: megabit
+ ConversionFactor: ''
+ Symbol: Mbit
+ CommonCode: D36
+ Description: A unit of information equal to 10⁶ (1000000) bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per gram kelvin
+ ConversionFactor: 4,184 x 10³ J/(kg x K)
+ Symbol: calth/(g·K)
+ CommonCode: D37
+ Description: ''
+ conversion:
+ factor: 4184.0
+ base_units:
+ - B11
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per second centimetre kelvin
+ ConversionFactor: 418,4 W/(m x K)
+ Symbol: calth/(s·cm·K)
+ CommonCode: D38
+ Description: ''
+ conversion:
+ factor: 418.4
+ base_units:
+ - D53
+ - N80
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per second square centimetre kelvin
+ ConversionFactor: 4,184 x10⁴ W/(m² x K)
+ Symbol: calth/(s·cm²·K)
+ CommonCode: D39
+ Description: ''
+ conversion:
+ factor: 4.184
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand litre
+ ConversionFactor: m³
+ Symbol: ''
+ CommonCode: D40
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: tonne per cubic metre
+ ConversionFactor: 10³ kg/m³
+ Symbol: t/m³
+ CommonCode: D41
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tropical year
+ ConversionFactor: 3,155 692 5 x 10⁷ s
+ Symbol: y (tropical)
+ CommonCode: D42
+ Description: ''
+ conversion:
+ factor: 3.155692
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: unified atomic mass unit
+ ConversionFactor: 1,660 538 782 x 10⁻²⁷ kg
+ Symbol: u
+ CommonCode: D43
+ Description: ''
+ conversion:
+ factor: 1.66
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: var
+ ConversionFactor: V x A
+ Symbol: var
+ CommonCode: D44
+ Description: The name of the unit is an acronym for volt-ampere-reactive.
+ conversion:
+ factor: 1.0
+ base_units:
+ - D44
+- Status: ''
+ LevelAndCategory: '1'
+ Name: volt squared per kelvin squared
+ ConversionFactor: V²/K²
+ Symbol: V²/K²
+ CommonCode: D45
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D45
+- Status: ''
+ LevelAndCategory: '1'
+ Name: volt - ampere
+ ConversionFactor: W
+ Symbol: V·A
+ CommonCode: D46
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: volt per centimetre
+ ConversionFactor: V/m x 10²
+ Symbol: V/cm
+ CommonCode: D47
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D47
+- Status: ''
+ LevelAndCategory: '1'
+ Name: volt per kelvin
+ ConversionFactor: V/K
+ Symbol: V/K
+ CommonCode: D48
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D48
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millivolt per kelvin
+ ConversionFactor: 10⁻³ V/K
+ Symbol: mV/K
+ CommonCode: D49
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D48
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilogram per square centimetre
+ ConversionFactor: 10⁴ kg/m²
+ Symbol: kg/cm²
+ CommonCode: D5
+ Description: ''
+ conversion:
+ factor: 10000.0
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '1'
+ Name: volt per metre
+ ConversionFactor: V/m
+ Symbol: V/m
+ CommonCode: D50
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D50
+- Status: ''
+ LevelAndCategory: 1S
+ Name: volt per millimetre
+ ConversionFactor: 10³ V/m
+ Symbol: V/mm
+ CommonCode: D51
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D50
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt per kelvin
+ ConversionFactor: W/K
+ Symbol: W/K
+ CommonCode: D52
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D52
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt per metre kelvin
+ ConversionFactor: W/(m x K)
+ Symbol: W/(m·K)
+ CommonCode: D53
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt per square metre
+ ConversionFactor: W/m²
+ Symbol: W/m²
+ CommonCode: D54
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt per square metre kelvin
+ ConversionFactor: W/(m² x K)
+ Symbol: W/(m²·K)
+ CommonCode: D55
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt per square metre kelvin to the fourth power
+ ConversionFactor: W/(m² x K⁴)
+ Symbol: W/(m²·K⁴)
+ CommonCode: D56
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D56
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt per steradian
+ ConversionFactor: W/sr
+ Symbol: W/sr
+ CommonCode: D57
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D57
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt per steradian square metre
+ ConversionFactor: W/(sr x m²)
+ Symbol: W/(sr·m²)
+ CommonCode: D58
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D58
+- Status: ''
+ LevelAndCategory: '1'
+ Name: weber per metre
+ ConversionFactor: Wb/m
+ Symbol: Wb/m
+ CommonCode: D59
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D59
+- Status: ''
+ LevelAndCategory: '2'
+ Name: roentgen per second
+ ConversionFactor: 2,58 x 10⁻⁴ C/(kg x s)
+ Symbol: R/s
+ CommonCode: D6
+ Description: ''
+ conversion:
+ factor: 0.000258
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: weber per millimetre
+ ConversionFactor: 10³ Wb/m
+ Symbol: Wb/mm
+ CommonCode: D60
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D59
+- Status: ''
+ LevelAndCategory: '1'
+ Name: minute [unit of angle]
+ ConversionFactor: 2,908 882 x 10⁻⁴ rad
+ Symbol: "'"
+ CommonCode: D61
+ Description: ''
+ conversion:
+ factor: 0.0002908882
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: '1'
+ Name: second [unit of angle]
+ ConversionFactor: 4,848 137 x 10⁻⁶ rad
+ Symbol: "\""
+ CommonCode: D62
+ Description: ''
+ conversion:
+ factor: 4.848137e-06
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: book
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D63
+ Description: 'A unit of count defining the number of books (book: set of items bound
+ together or written document of a material whole).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: block
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D64
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: round
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D65
+ Description: 'A unit of count defining the number of rounds (round: A circular or
+ cylindrical object).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: cassette
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D66
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: dollar per hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D67
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: number of words
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D68
+ Description: A unit of count defining the number of words.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch to the fourth power
+ ConversionFactor: 41,623 14 x 10⁻⁸ m⁴
+ Symbol: in⁴
+ CommonCode: D69
+ Description: ''
+ conversion:
+ factor: 4.162314e-07
+ base_units:
+ - B83
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: sandwich
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D7
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (international table)
+ ConversionFactor: 4,186 8 J
+ Symbol: calIT
+ CommonCode: D70
+ Description: ''
+ conversion:
+ factor: 4.1868
+ base_units:
+ - JOU
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (international table) per second centimetre kelvin
+ ConversionFactor: 418,68 W/(m x K)
+ Symbol: calIT/(s·cm·K)
+ CommonCode: D71
+ Description: ''
+ conversion:
+ factor: 418.68
+ base_units:
+ - D53
+ - N80
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (international table) per second square centimetre kelvin
+ ConversionFactor: 4,186 8 x 10⁴ W/(m² x K)
+ Symbol: calIT/(s·cm²·K)
+ CommonCode: D72
+ Description: ''
+ conversion:
+ factor: 41868.0
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule square metre
+ ConversionFactor: J x m²
+ Symbol: J·m²
+ CommonCode: D73
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D73
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram per mole
+ ConversionFactor: kg/mol
+ Symbol: kg/mol
+ CommonCode: D74
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D74
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (international table) per gram
+ ConversionFactor: 4 186,8 J/kg
+ Symbol: calIT/g
+ CommonCode: D75
+ Description: ''
+ conversion:
+ factor: 4186.8
+ base_units:
+ - J2
+- Status: D
+ LevelAndCategory: '2'
+ Name: calorie (international table) per gram kelvin
+ ConversionFactor: 4 186,8 J/(kg x K)
+ Symbol: calIT/(g·K)
+ CommonCode: D76
+ Description: ''
+ conversion:
+ factor: 4186.8
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megacoulomb
+ ConversionFactor: 10⁶ C
+ Symbol: MC
+ CommonCode: D77
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: megajoule per second
+ ConversionFactor: ''
+ Symbol: MJ/s
+ CommonCode: D78
+ Description: A unit of accumulated energy equal to one million joules per second.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: beam
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D79
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: draize score
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D8
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microwatt
+ ConversionFactor: 10⁻⁶ W
+ Symbol: µW
+ CommonCode: D80
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microtesla
+ ConversionFactor: 10⁻⁶ T
+ Symbol: µT
+ CommonCode: D81
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - D33
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microvolt
+ ConversionFactor: 10⁻⁶ V
+ Symbol: µV
+ CommonCode: D82
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millinewton metre
+ ConversionFactor: 10⁻³ N x m
+ Symbol: mN·m
+ CommonCode: D83
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microwatt per square metre
+ ConversionFactor: 10⁻⁶ W/m²
+ Symbol: µW/m²
+ CommonCode: D85
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millicoulomb
+ ConversionFactor: 10⁻³ C
+ Symbol: mC
+ CommonCode: D86
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millimole per kilogram
+ ConversionFactor: 10⁻³ mol/kg
+ Symbol: mmol/kg
+ CommonCode: D87
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C19
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millicoulomb per cubic metre
+ ConversionFactor: 10⁻³ C/m³
+ Symbol: mC/m³
+ CommonCode: D88
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - A29
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millicoulomb per square metre
+ ConversionFactor: 10⁻³ C/m²
+ Symbol: mC/m²
+ CommonCode: D89
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - A34
+- Status: D
+ LevelAndCategory: '2,3.9'
+ Name: dyne per square centimetre
+ ConversionFactor: 10⁻¹ Pa
+ Symbol: dyn/cm²
+ CommonCode: D9
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - C55
+ - PAL
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: cubic metre (net)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D90
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: rem
+ ConversionFactor: 10⁻² Sv
+ Symbol: rem
+ CommonCode: D91
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: band
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D92
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: second per cubic metre
+ ConversionFactor: s/m³
+ Symbol: s/m³
+ CommonCode: D93
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D93
+- Status: ''
+ LevelAndCategory: '1'
+ Name: second per cubic metre radian
+ ConversionFactor: s/(rad x m³)
+ Symbol: s/(rad·m³)
+ CommonCode: D94
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D94
+- Status: ''
+ LevelAndCategory: 1S
+ Name: joule per gram
+ ConversionFactor: J/(10⁻³ x kg)
+ Symbol: J/g
+ CommonCode: D95
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D95
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: pound gross
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D96
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: pallet/unit load
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D97
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: mass pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D98
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: sleeve
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: D99
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decare
+ ConversionFactor: 10³ m²
+ Symbol: daa
+ CommonCode: DAA
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: ten day
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DAD
+ Description: A unit of time defining the number of days in multiples of 10.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: day
+ ConversionFactor: 86 400 s
+ Symbol: d
+ CommonCode: DAY
+ Description: ''
+ conversion:
+ factor: 86400.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: dry pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DB
+ Description: A unit of mass defining the number of pounds of a product, disregarding
+ the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: disk (disc)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DC
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: degree [unit of angle]
+ ConversionFactor: 1,745 329 x 10⁻² rad
+ Symbol: "°"
+ CommonCode: DD
+ Description: ''
+ conversion:
+ factor: 0.01745329
+ base_units:
+ - C81
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: deal
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: decade
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DEC
+ Description: 'A unit of count defining the number of decades (decade: quantity equal
+ to 10 or time equal to 10 years).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decigram
+ ConversionFactor: 10⁻⁴ kg
+ Symbol: dg
+ CommonCode: DG
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - KGM
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: dispenser
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DI
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decagram
+ ConversionFactor: 10⁻² kg
+ Symbol: dag
+ CommonCode: DJ
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decilitre
+ ConversionFactor: 10⁻⁴ m³
+ Symbol: dl
+ CommonCode: DLT
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decametre
+ ConversionFactor: 10³ m³
+ Symbol: dam³
+ CommonCode: DMA
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: square decimetre
+ ConversionFactor: 10⁻² m²
+ Symbol: dm²
+ CommonCode: DMK
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: standard kilolitre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DMO
+ Description: A unit of volume defining the number of kilolitres of a product at
+ a temperature of 15 degrees Celsius, especially in relation to hydrocarbon oils.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decimetre
+ ConversionFactor: 10⁻³ m³
+ Symbol: dm³
+ CommonCode: DMQ
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decimetre
+ ConversionFactor: 10⁻¹ m
+ Symbol: dm
+ CommonCode: DMT
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1S
+ Name: decinewton metre
+ ConversionFactor: 10⁻¹ N x m
+ Symbol: dN·m
+ CommonCode: DN
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: dozen piece
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DPC
+ Description: 'A unit of count defining the number of pieces in multiples of 12 (piece:
+ a single item, article or exemplar).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: dozen pair
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DPR
+ Description: 'A unit of count defining the number of pairs in multiples of 12 (pair:
+ item described by two''s).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: displacement tonnage
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DPT
+ Description: A unit of mass defining the volume of sea water a ship displaces, expressed
+ as the number of tons.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.6'
+ Name: data record
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DQ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: drum
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DR
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: dram (US)
+ ConversionFactor: 3,887 935 g
+ Symbol: ''
+ CommonCode: DRA
+ Description: 'Synonym: drachm (UK), troy dram'
+ conversion:
+ factor: 3.887935
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: dram (UK)
+ ConversionFactor: 1,771 745 g
+ Symbol: ''
+ CommonCode: DRI
+ Description: 'Synonym: avoirdupois dram'
+ conversion:
+ factor: 1.771745
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: dozen roll
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DRL
+ Description: A unit of count defining the number of rolls, expressed in twelve roll
+ units.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: drachm (UK)
+ ConversionFactor: 3,887 935 g
+ Symbol: ''
+ CommonCode: DRM
+ Description: ''
+ conversion:
+ factor: 3.887935
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: display
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DS
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: dry ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DT
+ Description: A unit of mass defining the number of tons of a product, disregarding
+ the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M,3.5
+ Name: decitonne
+ ConversionFactor: 10² kg
+ Symbol: dt or dtn
+ CommonCode: DTN
+ Description: 'Synonym: centner, metric 100 kg; quintal, metric 100 kg'
+ conversion:
+ factor: 100.0
+ base_units:
+ - KGM
+- Status: D
+ LevelAndCategory: '2'
+ Name: dyne
+ ConversionFactor: 10⁻⁵ N
+ Symbol: dyn
+ CommonCode: DU
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pennyweight
+ ConversionFactor: 1,555 174 g
+ Symbol: ''
+ CommonCode: DWT
+ Description: ''
+ conversion:
+ factor: 1.555174
+ base_units: []
+- Status: D
+ LevelAndCategory: '2'
+ Name: dyne per centimetre
+ ConversionFactor: 10⁻³ N/m
+ Symbol: dyn/cm
+ CommonCode: DX
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - 4P
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: directory book
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DY
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: dozen
+ ConversionFactor: '12'
+ Symbol: DOZ
+ CommonCode: DZN
+ Description: A unit of count defining the number of units in multiples of 12.
+ conversion:
+ factor: 12.0
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: dozen pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: DZP
+ Description: 'A unit of count defining the number of packs in multiples of 12 (pack:
+ standard packaging unit).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton per square centimetre
+ ConversionFactor: 10⁴ Pa
+ Symbol: N/cm²
+ CommonCode: E01
+ Description: A measure of pressure expressed in newtons per square centimetre.
+ conversion:
+ factor: 10000.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: megawatt hour per hour
+ ConversionFactor: ''
+ Symbol: MW·h/h
+ CommonCode: E07
+ Description: A unit of accumulated energy of a million watts over a period of one
+ hour.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: megawatt per hertz
+ ConversionFactor: ''
+ Symbol: MW/Hz
+ CommonCode: E08
+ Description: A unit of energy expressed as the load change in million watts that
+ will cause a frequency shift of one hertz.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milliampere hour
+ ConversionFactor: 3,6 C
+ Symbol: mA·h
+ CommonCode: E09
+ Description: A unit of power load delivered at the rate of one thousandth of an
+ ampere over a period of one hour.
+ conversion:
+ factor: 3.6
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree day
+ ConversionFactor: ''
+ Symbol: deg da
+ CommonCode: E10
+ Description: A unit of measure used in meteorology and engineering to measure the
+ demand for heating or cooling over a given period of days.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.5'
+ Name: gigacalorie
+ ConversionFactor: 10⁹ cal
+ Symbol: ''
+ CommonCode: E11
+ Description: A unit of heat energy equal to one thousand million calories.
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: mille
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E12
+ Description: A unit of count defining the number of cigarettes in units of 1000.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (international table)
+ ConversionFactor: 4,186 8 x 10³ J
+ Symbol: kcalIT
+ CommonCode: E14
+ Description: A unit of heat energy equal to one thousand calories.
+ conversion:
+ factor: 4186.8
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (thermochemical) per hour
+ ConversionFactor: 1,162 22 W
+ Symbol: kcalth/h
+ CommonCode: E15
+ Description: A unit of energy equal to one thousand calories per hour.
+ conversion:
+ factor: 1.16222
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: million Btu(IT) per hour
+ ConversionFactor: 293 071,1 W
+ Symbol: BtuIT/h
+ CommonCode: E16
+ Description: A unit of power equal to one million British thermal units per hour.
+ conversion:
+ factor: 293071.1
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: cubic foot per second
+ ConversionFactor: 2,831 685 x 10⁻² m³/s
+ Symbol: ft³/s
+ CommonCode: E17
+ Description: A unit of volume equal to one cubic foot passing a given point in a
+ period of one second.
+ conversion:
+ factor: 0.02831685
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per hour
+ ConversionFactor: 2,777 78 x 10⁻¹ kg/s
+ Symbol: t/h
+ CommonCode: E18
+ Description: A unit of weight or mass equal to one tonne per hour.
+ conversion:
+ factor: 0.277778
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: ping
+ ConversionFactor: 3,305 m²
+ Symbol: ''
+ CommonCode: E19
+ Description: A unit of area equal to 3.3 square metres.
+ conversion:
+ factor: 3.305
+ base_units:
+ - MTK
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: belt
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E2
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: megabit per second
+ ConversionFactor: ''
+ Symbol: Mbit/s
+ CommonCode: E20
+ Description: A unit of information equal to 10⁶ (1000000) bits (binary digits) per
+ second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: shares
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E21
+ Description: 'A unit of count defining the number of shares (share: a total or portion
+ of the parts into which a business entity’s capital is divided).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: TEU
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E22
+ Description: A unit of count defining the number of twenty-foot equivalent units
+ (TEUs) as a measure of containerized cargo capacity.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: tyre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E23
+ Description: A unit of count defining the number of tyres (a solid or air-filled
+ covering placed around a wheel rim to form a soft contact with the road, absorb
+ shock and provide traction).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: active unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E25
+ Description: A unit of count defining the number of active units within a substance.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: dose
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E27
+ Description: 'A unit of count defining the number of doses (dose: a definite quantity
+ of a medicine or drug).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: air dry ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E28
+ Description: A unit of mass defining the number of tons of a product, disregarding
+ the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: trailer
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E3
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: strand
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E30
+ Description: 'A unit of count defining the number of strands (strand: long, thin,
+ flexible, single thread, strip of fibre, constituent filament or multiples of
+ the same, twisted together).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: square metre per litre
+ ConversionFactor: ''
+ Symbol: m²/l
+ CommonCode: E31
+ Description: A unit of count defining the number of square metres per litre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: litre per hour
+ ConversionFactor: 2,777 78 x 10⁻⁷ m³/s
+ Symbol: l/h
+ CommonCode: E32
+ Description: A unit of count defining the number of litres per hour.
+ conversion:
+ factor: 2.77778e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: foot per thousand
+ ConversionFactor: 3,048 x 10⁻⁴ m
+ Symbol: ''
+ CommonCode: E33
+ Description: A unit of count defining the number of feet per thousand units.
+ conversion:
+ factor: 0.0003048
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gigabyte
+ ConversionFactor: ''
+ Symbol: Gbyte
+ CommonCode: E34
+ Description: A unit of information equal to 10⁹ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: terabyte
+ ConversionFactor: ''
+ Symbol: Tbyte
+ CommonCode: E35
+ Description: A unit of information equal to 10¹² bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: petabyte
+ ConversionFactor: ''
+ Symbol: Pbyte
+ CommonCode: E36
+ Description: A unit of information equal to 10¹⁵ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: pixel
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E37
+ Description: 'A unit of count defining the number of pixels (pixel: picture element).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: megapixel
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E38
+ Description: A unit of count equal to 10⁶ (1000000) pixels (picture elements).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: dots per inch
+ ConversionFactor: ''
+ Symbol: dpi
+ CommonCode: E39
+ Description: A unit of information defining the number of dots per linear inch as
+ a measure of the resolution or sharpness of a graphic image.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: gross kilogram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E4
+ Description: A unit of mass defining the total number of kilograms before deductions.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: part per hundred thousand
+ ConversionFactor: 1 x 10⁻⁵
+ Symbol: ppht
+ CommonCode: E40
+ Description: A unit of proportion equal to 10⁻⁵.
+ conversion:
+ factor: 1.0e-05
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilogram-force per square millimetre
+ ConversionFactor: 9,806 65 x 10⁶ Pa
+ Symbol: kgf/mm²
+ CommonCode: E41
+ Description: A unit of pressure defining the number of kilograms force per square
+ millimetre.
+ conversion:
+ factor: 9806650.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilogram-force per square centimetre
+ ConversionFactor: 9,806 65 x 10⁴ Pa
+ Symbol: kgf/cm²
+ CommonCode: E42
+ Description: A unit of pressure defining the number of kilograms force per square
+ centimetre.
+ conversion:
+ factor: 98066.5
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1M
+ Name: joule per square centimetre
+ ConversionFactor: 10⁴ J/m²
+ Symbol: J/cm²
+ CommonCode: E43
+ Description: A unit of energy defining the number of joules per square centimetre.
+ conversion:
+ factor: 10000.0
+ base_units:
+ - B13
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: kilogram-force metre per square centimetre
+ ConversionFactor: ''
+ Symbol: kgf·m/cm²
+ CommonCode: E44
+ Description: A unit of torsion defining the torque kilogram-force metre per square
+ centimetre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milliohm
+ ConversionFactor: 10⁻³ Ω
+ Symbol: mΩ
+ CommonCode: E45
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - OHM
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilowatt hour per cubic metre
+ ConversionFactor: 3,6 x 10⁶ J/m³
+ Symbol: kW·h/m³
+ CommonCode: E46
+ Description: A unit of energy consumption expressed as kilowatt hour per cubic metre.
+ conversion:
+ factor: 3600000.0
+ base_units:
+ - B8
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilowatt hour per kelvin
+ ConversionFactor: 3,6 x 10⁶ J/K
+ Symbol: kW·h/K
+ CommonCode: E47
+ Description: A unit of energy consumption expressed as kilowatt hour per kelvin.
+ conversion:
+ factor: 3600000.0
+ base_units:
+ - JE
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: service unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E48
+ Description: 'A unit of count defining the number of service units (service unit:
+ defined period / property / facility / utility of supply).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: working day
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E49
+ Description: 'A unit of count defining the number of working days (working day:
+ a day on which work is ordinarily performed).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: metric long ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E5
+ Description: Use ton (UK) or long ton (US) (common code LTN)
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: accounting unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E50
+ Description: A unit of count defining the number of accounting units.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: job
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E51
+ Description: A unit of count defining the number of jobs.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: run foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E52
+ Description: A unit of count defining the number feet per run.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: test
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E53
+ Description: A unit of count defining the number of tests.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: trip
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E54
+ Description: A unit of count defining the number of trips.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: use
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E55
+ Description: A unit of count defining the number of times an object is used.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: well
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E56
+ Description: A unit of count defining the number of wells.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: zone
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: E57
+ Description: A unit of count defining the number of zones.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: exabit per second
+ ConversionFactor: ''
+ Symbol: Ebit/s
+ CommonCode: E58
+ Description: A unit of information equal to 10¹⁸ bits (binary digits) per second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: exbibyte
+ ConversionFactor: ''
+ Symbol: Eibyte
+ CommonCode: E59
+ Description: A unit of information equal to 2⁶⁰ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: pebibyte
+ ConversionFactor: ''
+ Symbol: Pibyte
+ CommonCode: E60
+ Description: A unit of information equal to 2⁵⁰ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: tebibyte
+ ConversionFactor: ''
+ Symbol: Tibyte
+ CommonCode: E61
+ Description: A unit of information equal to 2⁴⁰ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gibibyte
+ ConversionFactor: ''
+ Symbol: Gibyte
+ CommonCode: E62
+ Description: A unit of information equal to 2³⁰ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: mebibyte
+ ConversionFactor: ''
+ Symbol: Mibyte
+ CommonCode: E63
+ Description: A unit of information equal to 2²⁰ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kibibyte
+ ConversionFactor: ''
+ Symbol: Kibyte
+ CommonCode: E64
+ Description: A unit of information equal to 2¹⁰ bytes.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: exbibit per metre
+ ConversionFactor: ''
+ Symbol: Eibit/m
+ CommonCode: E65
+ Description: A unit of information equal to 2⁶⁰ bits (binary digits) per metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: exbibit per square metre
+ ConversionFactor: ''
+ Symbol: Eibit/m²
+ CommonCode: E66
+ Description: A unit of information equal to 2⁶⁰ bits (binary digits) per square
+ metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: exbibit per cubic metre
+ ConversionFactor: ''
+ Symbol: Eibit/m³
+ CommonCode: E67
+ Description: A unit of information equal to 2⁶⁰ bits (binary digits) per cubic metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gigabyte per second
+ ConversionFactor: ''
+ Symbol: Gbyte/s
+ CommonCode: E68
+ Description: A unit of information equal to 10⁹ bytes per second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gibibit per metre
+ ConversionFactor: ''
+ Symbol: Gibit/m
+ CommonCode: E69
+ Description: A unit of information equal to 2³⁰ bits (binary digits) per metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gibibit per square metre
+ ConversionFactor: ''
+ Symbol: Gibit/m²
+ CommonCode: E70
+ Description: A unit of information equal to 2³⁰ bits (binary digits) per square
+ metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: gibibit per cubic metre
+ ConversionFactor: ''
+ Symbol: Gibit/m³
+ CommonCode: E71
+ Description: A unit of information equal to 2³⁰ bits (binary digits) per cubic metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kibibit per metre
+ ConversionFactor: ''
+ Symbol: Kibit/m
+ CommonCode: E72
+ Description: A unit of information equal to 2¹⁰ bits (binary digits) per metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kibibit per square metre
+ ConversionFactor: ''
+ Symbol: Kibit/m²
+ CommonCode: E73
+ Description: A unit of information equal to 2¹⁰ bits (binary digits) per square
+ metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kibibit per cubic metre
+ ConversionFactor: ''
+ Symbol: Kibit/m³
+ CommonCode: E74
+ Description: A unit of information equal to 2¹⁰ bits (binary digits) per cubic metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: mebibit per metre
+ ConversionFactor: ''
+ Symbol: Mibit/m
+ CommonCode: E75
+ Description: A unit of information equal to 2²⁰ bits (binary digits) per metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: mebibit per square metre
+ ConversionFactor: ''
+ Symbol: Mibit/m²
+ CommonCode: E76
+ Description: A unit of information equal to 2²⁰ bits (binary digits) per square
+ metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: mebibit per cubic metre
+ ConversionFactor: ''
+ Symbol: Mibit/m³
+ CommonCode: E77
+ Description: A unit of information equal to 2²⁰ bits (binary digits) per cubic metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: petabit
+ ConversionFactor: ''
+ Symbol: Pbit
+ CommonCode: E78
+ Description: A unit of information equal to 10¹⁵ bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: petabit per second
+ ConversionFactor: ''
+ Symbol: Pbit/s
+ CommonCode: E79
+ Description: A unit of information equal to 10¹⁵ bits (binary digits) per second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: pebibit per metre
+ ConversionFactor: ''
+ Symbol: Pibit/m
+ CommonCode: E80
+ Description: A unit of information equal to 2⁵⁰ bits (binary digits) per metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: pebibit per square metre
+ ConversionFactor: ''
+ Symbol: Pibit/m²
+ CommonCode: E81
+ Description: A unit of information equal to 2⁵⁰ bits (binary digits) per square
+ metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: pebibit per cubic metre
+ ConversionFactor: ''
+ Symbol: Pibit/m³
+ CommonCode: E82
+ Description: A unit of information equal to 2⁵⁰ bits (binary digits) per cubic metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: terabit
+ ConversionFactor: ''
+ Symbol: Tbit
+ CommonCode: E83
+ Description: A unit of information equal to 10¹² bits (binary digits).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: terabit per second
+ ConversionFactor: ''
+ Symbol: Tbit/s
+ CommonCode: E84
+ Description: A unit of information equal to 10¹² bits (binary digits) per second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: tebibit per metre
+ ConversionFactor: ''
+ Symbol: Tibit/m
+ CommonCode: E85
+ Description: A unit of information equal to 2⁴⁰ bits (binary digits) per metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: tebibit per cubic metre
+ ConversionFactor: ''
+ Symbol: Tibit/m³
+ CommonCode: E86
+ Description: A unit of information equal to 2⁴⁰ bits (binary digits) per cubic metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: tebibit per square metre
+ ConversionFactor: ''
+ Symbol: Tibit/m²
+ CommonCode: E87
+ Description: A unit of information equal to 2⁴⁰ bits (binary digits) per square
+ metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: bit per metre
+ ConversionFactor: ''
+ Symbol: bit/m
+ CommonCode: E88
+ Description: A unit of information equal to 1 bit (binary digit) per metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: bit per square metre
+ ConversionFactor: ''
+ Symbol: bit/m²
+ CommonCode: E89
+ Description: A unit of information equal to 1 bit (binary digit) per square metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: reciprocal centimetre
+ ConversionFactor: 10² m⁻¹
+ Symbol: cm⁻¹
+ CommonCode: E90
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: reciprocal day
+ ConversionFactor: 1,157 41 × 10⁻⁵ s⁻¹
+ Symbol: d⁻¹
+ CommonCode: E91
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decimetre per hour
+ ConversionFactor: 2,777 78 × 10⁻⁷ m³ x s⁻¹
+ Symbol: dm³/h
+ CommonCode: E92
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilogram per hour
+ ConversionFactor: 2,777 78 × 10⁻⁴ kg x s⁻¹
+ Symbol: kg/h
+ CommonCode: E93
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilomole per second
+ ConversionFactor: 10³ s⁻¹ x mol
+ Symbol: kmol/s
+ CommonCode: E94
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - KAT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: mole per second
+ ConversionFactor: s⁻¹ x mol
+ Symbol: mol/s
+ CommonCode: E95
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - E95
+- Status: ''
+ LevelAndCategory: 1M
+ Name: degree per second
+ ConversionFactor: 1,745 329 x 10⁻² rad x s⁻¹
+ Symbol: "°/s"
+ CommonCode: E96
+ Description: ''
+ conversion:
+ factor: 0.01745329
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millimetre per degree Celcius metre
+ ConversionFactor: 10⁻³ K⁻¹
+ Symbol: mm/(°C·m)
+ CommonCode: E97
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - C91
+ - N83
+- Status: ''
+ LevelAndCategory: 1M
+ Name: degree Celsius per kelvin
+ ConversionFactor: '1.0'
+ Symbol: "°C/K"
+ CommonCode: E98
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - E98
+ - F02
+ - H60
+ - L52
+ - M91
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectopascal per bar
+ ConversionFactor: 10⁻³
+ Symbol: hPa/bar
+ CommonCode: E99
+ Description: ''
+ conversion:
+ factor: 0.001
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: each
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: EA
+ Description: A unit of count defining the number of items regarded as separate units.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: electronic mail box
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: EB
+ Description: A unit of count defining the number of electronic mail boxes.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: each per month
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: EC
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: eleven pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: EP
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: equivalent gallon
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: EQ
+ Description: A unit of volume defining the number of gallons of product produced
+ from concentrate.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: envelope
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: EV
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: bit per cubic metre
+ ConversionFactor: ''
+ Symbol: bit/m³
+ CommonCode: F01
+ Description: A unit of information equal to 1 bit (binary digit) per cubic metre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kelvin per kelvin
+ ConversionFactor: '1.0'
+ Symbol: K/K
+ CommonCode: F02
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - E98
+ - F02
+ - H60
+ - L52
+ - M91
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilopascal per bar
+ ConversionFactor: 10⁻²
+ Symbol: kPa/bar
+ CommonCode: F03
+ Description: ''
+ conversion:
+ factor: 0.01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millibar per bar
+ ConversionFactor: 10⁻³
+ Symbol: mbar/bar
+ CommonCode: F04
+ Description: ''
+ conversion:
+ factor: 0.001
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megapascal per bar
+ ConversionFactor: 10¹
+ Symbol: MPa/bar
+ CommonCode: F05
+ Description: ''
+ conversion:
+ factor: 10.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poise per bar
+ ConversionFactor: 10⁻⁶ s
+ Symbol: P/bar
+ CommonCode: F06
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: pascal per bar
+ ConversionFactor: 10⁻⁵
+ Symbol: Pa/bar
+ CommonCode: F07
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+- Status: ''
+ LevelAndCategory: '2'
+ Name: milliampere per inch
+ ConversionFactor: 3,937 007 874 015 75 x 10⁻² A x m⁻¹
+ Symbol: mA/in
+ CommonCode: F08
+ Description: ''
+ conversion:
+ factor: 0.0393700787401575
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand cubic foot per day
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: F1
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kelvin per hour
+ ConversionFactor: 2,777 78 × 10⁻⁴ s⁻¹ x K
+ Symbol: K/h
+ CommonCode: F10
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kelvin per minute
+ ConversionFactor: 1,666 67 × 10⁻² s⁻¹ x K
+ Symbol: K/min
+ CommonCode: F11
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kelvin per second
+ ConversionFactor: s⁻¹ x K
+ Symbol: K/s
+ CommonCode: F12
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F12
+- Status: ''
+ LevelAndCategory: '2'
+ Name: slug
+ ConversionFactor: 1,459 390 x 10¹ kg
+ Symbol: slug
+ CommonCode: F13
+ Description: A unit of mass. One slug is the mass accelerated at 1 foot per second
+ per second by a force of 1 pound.
+ conversion:
+ factor: 14.5939
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per kelvin
+ ConversionFactor: 10⁻³ kg x K⁻¹
+ Symbol: g/K
+ CommonCode: F14
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - F15
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per kelvin
+ ConversionFactor: kg x K⁻¹
+ Symbol: kg/K
+ CommonCode: F15
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F15
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per kelvin
+ ConversionFactor: 10⁻⁶ kg x K⁻¹
+ Symbol: mg/K
+ CommonCode: F16
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - F15
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force per foot
+ ConversionFactor: 1,459 39 × 10¹ kg x s⁻²
+ Symbol: lbf/ft
+ CommonCode: F17
+ Description: ''
+ conversion:
+ factor: 1.45939
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram square centimetre
+ ConversionFactor: 10⁻⁴ kg m²
+ Symbol: kg·cm²
+ CommonCode: F18
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram square millimetre
+ ConversionFactor: 10⁻⁶ kg m²
+ Symbol: kg·mm²
+ CommonCode: F19
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound inch squared
+ ConversionFactor: 2,926 397 x 10⁻⁴ kg x m²
+ Symbol: lb·in²
+ CommonCode: F20
+ Description: ''
+ conversion:
+ factor: 0.0002926397
+ base_units:
+ - B32
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force inch
+ ConversionFactor: 1,129 85 × 10⁻¹ kg x m² x s⁻²
+ Symbol: lbf·in
+ CommonCode: F21
+ Description: ''
+ conversion:
+ factor: 1.12985
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force foot per ampere
+ ConversionFactor: 1,355 82 kg x m² x s⁻² x A⁻¹
+ Symbol: lbf·ft/A
+ CommonCode: F22
+ Description: ''
+ conversion:
+ factor: 1.35582
+ base_units:
+ - F90
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic decimetre
+ ConversionFactor: kg x m⁻³
+ Symbol: g/dm³
+ CommonCode: F23
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F23
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per kilomol
+ ConversionFactor: 10⁻³ kg x mol⁻¹
+ Symbol: kg/kmol
+ CommonCode: F24
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per hertz
+ ConversionFactor: 10⁻³ kg x s
+ Symbol: g/Hz
+ CommonCode: F25
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per day
+ ConversionFactor: 1,157 41 × 10⁻⁸ kg x s⁻¹
+ Symbol: g/d
+ CommonCode: F26
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per hour
+ ConversionFactor: 2,777 78 × 10⁻⁷ kg x s⁻¹
+ Symbol: g/h
+ CommonCode: F27
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per minute
+ ConversionFactor: 1,666 67 × 10⁻⁵ kg x s⁻¹
+ Symbol: g/min
+ CommonCode: F28
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per second
+ ConversionFactor: 10⁻³ kg x s⁻¹
+ Symbol: g/s
+ CommonCode: F29
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per day
+ ConversionFactor: 1,157 41 × 10⁻⁵ kg x s⁻¹
+ Symbol: kg/d
+ CommonCode: F30
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per minute
+ ConversionFactor: 1,666 67 × 10⁻² kg x s⁻¹
+ Symbol: kg/min
+ CommonCode: F31
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per day
+ ConversionFactor: 1,157 41 × 10⁻¹¹ kg x s⁻¹
+ Symbol: mg/d
+ CommonCode: F32
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per minute
+ ConversionFactor: 1,666 67 × 10⁻⁸ kg x s⁻¹
+ Symbol: mg/min
+ CommonCode: F33
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per second
+ ConversionFactor: 10⁻⁶ kg x s⁻¹
+ Symbol: mg/s
+ CommonCode: F34
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per day kelvin
+ ConversionFactor: 1,157 41 × 10⁻⁸ kg x s⁻¹ x K⁻¹
+ Symbol: g/(d·K)
+ CommonCode: F35
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per hour kelvin
+ ConversionFactor: 2,777 78 × 10⁻⁷ kg x s⁻¹ x K⁻¹
+ Symbol: g/(h·K)
+ CommonCode: F36
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per minute kelvin
+ ConversionFactor: 1,666 67 × 10⁻⁵ kg x s⁻¹ x K⁻¹
+ Symbol: g/(min·K)
+ CommonCode: F37
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per second kelvin
+ ConversionFactor: 10⁻³ kg x s⁻¹ x K⁻¹
+ Symbol: g/(s·K)
+ CommonCode: F38
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - F42
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per day kelvin
+ ConversionFactor: 1,157 41 × 10⁻⁵ kg x s⁻¹ x K⁻¹
+ Symbol: kg/(d·K)
+ CommonCode: F39
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per hour kelvin
+ ConversionFactor: 2,777 78 × 10⁻⁴ kg x s⁻¹ x K⁻¹
+ Symbol: kg/(h·K)
+ CommonCode: F40
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per minute kelvin
+ ConversionFactor: 1,666 67 × 10⁻²kg x s⁻¹ x K⁻¹
+ Symbol: kg/(min·K)
+ CommonCode: F41
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per second kelvin
+ ConversionFactor: kg x s⁻¹ x K⁻¹
+ Symbol: kg/(s·K)
+ CommonCode: F42
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F42
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per day kelvin
+ ConversionFactor: 1,157 41 × 10⁻¹¹ kg x s⁻¹ x K⁻¹
+ Symbol: mg/(d·K)
+ CommonCode: F43
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per hour kelvin
+ ConversionFactor: 2,777 78 × 10⁻¹⁰ kg x s⁻¹ x K⁻¹
+ Symbol: mg/(h·K)
+ CommonCode: F44
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per minute kelvin
+ ConversionFactor: 1,666 67 × 10⁻⁸ kg x s⁻¹ x K⁻¹
+ Symbol: mg/(min·K)
+ CommonCode: F45
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per second kelvin
+ ConversionFactor: 10⁻⁶ kg x s⁻¹ x K⁻¹
+ Symbol: mg/(s·K)
+ CommonCode: F46
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - F42
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton per millimetre
+ ConversionFactor: 10³ kg x s⁻²
+ Symbol: N/mm
+ CommonCode: F47
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force per inch
+ ConversionFactor: 1,751 27 × 10² kg x s⁻²
+ Symbol: lbf/in
+ CommonCode: F48
+ Description: ''
+ conversion:
+ factor: 1.75127
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: rod [unit of distance]
+ ConversionFactor: 5,029 210 m
+ Symbol: rd (US)
+ CommonCode: F49
+ Description: A unit of distance equal to 5.5 yards (16 feet 6 inches).
+ conversion:
+ factor: 5.02921
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1M
+ Name: micrometre per kelvin
+ ConversionFactor: 10⁻⁶ m x K⁻¹
+ Symbol: µm/K
+ CommonCode: F50
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - F52
+- Status: ''
+ LevelAndCategory: 1M
+ Name: centimetre per kelvin
+ ConversionFactor: 10⁻² m x K⁻¹
+ Symbol: cm/K
+ CommonCode: F51
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - F52
+- Status: ''
+ LevelAndCategory: 1M
+ Name: metre per kelvin
+ ConversionFactor: m x K⁻¹
+ Symbol: m/K
+ CommonCode: F52
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F52
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millimetre per kelvin
+ ConversionFactor: 10⁻³ m x K⁻¹
+ Symbol: mm/K
+ CommonCode: F53
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - F52
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milliohm per metre
+ ConversionFactor: 10⁻³ Ω/m
+ Symbol: mΩ/m
+ CommonCode: F54
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - H26
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ohm per mile (statute mile)
+ ConversionFactor: 6,213 71 × 10⁻⁴ Ω/m
+ Symbol: Ω/mi
+ CommonCode: F55
+ Description: ''
+ conversion:
+ factor: 6.21371
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: ohm per kilometre
+ ConversionFactor: 10⁻³ Ω/m
+ Symbol: Ω/km
+ CommonCode: F56
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - H26
+- Status: ''
+ LevelAndCategory: '2'
+ Name: milliampere per pound-force per square inch
+ ConversionFactor: 1,450 38 × 10⁻⁷ kg⁻¹ x m x s² x A
+ Symbol: mA/(lbf/in²)
+ CommonCode: F57
+ Description: ''
+ conversion:
+ factor: 1.45038
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: reciprocal bar
+ ConversionFactor: bar⁻¹
+ Symbol: 1/bar
+ CommonCode: F58
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F58
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milliampere per bar
+ ConversionFactor: 10⁻⁸ kg⁻¹ x m x s² x A
+ Symbol: mA/bar
+ CommonCode: F59
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - N93
+- Status: ''
+ LevelAndCategory: 1M
+ Name: degree Celsius per bar
+ ConversionFactor: 10⁻⁵ kg⁻¹ x m x s² x K
+ Symbol: "°C/bar"
+ CommonCode: F60
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - N79
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kelvin per bar
+ ConversionFactor: 10⁻⁵ kg⁻¹ x m x s² x K
+ Symbol: K/bar
+ CommonCode: F61
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - N79
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per day bar
+ ConversionFactor: 1,157 41 × 10⁻¹³ m x s
+ Symbol: g/(d·bar)
+ CommonCode: F62
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per hour bar
+ ConversionFactor: 2,777 78 × 10⁻¹² m x s
+ Symbol: g/(h·bar)
+ CommonCode: F63
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per minute bar
+ ConversionFactor: 1,666 67 × 10⁻¹⁰ m x s
+ Symbol: g/(min·bar)
+ CommonCode: F64
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per second bar
+ ConversionFactor: 10⁻⁸ m x s
+ Symbol: g/(s·bar)
+ CommonCode: F65
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - M87
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per day bar
+ ConversionFactor: 1,157 41 × 10⁻¹⁰ m x s
+ Symbol: kg/(d·bar)
+ CommonCode: F66
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per hour bar
+ ConversionFactor: 2,777 78 × 10⁻⁹ m x s
+ Symbol: kg/(h·bar)
+ CommonCode: F67
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per minute bar
+ ConversionFactor: 1,666 67 × 10⁻⁷ m x s
+ Symbol: kg/(min·bar)
+ CommonCode: F68
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per second bar
+ ConversionFactor: 10⁻⁵ m x s
+ Symbol: kg/(s·bar)
+ CommonCode: F69
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M87
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per day bar
+ ConversionFactor: 1,157 41 × 10⁻¹⁶ m x s
+ Symbol: mg/(d·bar)
+ CommonCode: F70
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per hour bar
+ ConversionFactor: 2,777 78 × 10⁻¹⁵ m x s
+ Symbol: mg/(h·bar)
+ CommonCode: F71
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per minute bar
+ ConversionFactor: 1,666 67 × 10⁻¹³ m x s
+ Symbol: mg/(min·bar)
+ CommonCode: F72
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per second bar
+ ConversionFactor: 10⁻¹¹ m x s
+ Symbol: mg/(s·bar)
+ CommonCode: F73
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units:
+ - M87
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per bar
+ ConversionFactor: 10⁻⁸ m x s²
+ Symbol: g/bar
+ CommonCode: F74
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - M74
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per bar
+ ConversionFactor: 10⁻¹¹ m x s²
+ Symbol: mg/bar
+ CommonCode: F75
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units:
+ - M74
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milliampere per millimetre
+ ConversionFactor: m⁻¹ x A
+ Symbol: mA/mm
+ CommonCode: F76
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F76
+- Status: ''
+ LevelAndCategory: 1M
+ Name: pascal second per kelvin
+ ConversionFactor: kg x m⁻¹ x s⁻¹ x K⁻¹
+ Symbol: Pa.s/K
+ CommonCode: F77
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F77
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch of water
+ ConversionFactor: 2,490 89 × 10² kg x m⁻¹ x s⁻²
+ Symbol: inH₂O
+ CommonCode: F78
+ Description: ''
+ conversion:
+ factor: 2.49089
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch of mercury
+ ConversionFactor: 3,386 39 × 10³ kg x m⁻¹ x s⁻²
+ Symbol: inHg
+ CommonCode: F79
+ Description: ''
+ conversion:
+ factor: 3.38639
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: water horse power
+ ConversionFactor: 7,460 43 x 10² W
+ Symbol: ''
+ CommonCode: F80
+ Description: A unit of power defining the amount of power required to move a given
+ volume of water against acceleration of gravity to a specified elevation (pressure
+ head).
+ conversion:
+ factor: 746.043
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1M
+ Name: bar per kelvin
+ ConversionFactor: 10⁵ kg x m⁻¹ x s⁻² x K⁻¹
+ Symbol: bar/K
+ CommonCode: F81
+ Description: ''
+ conversion:
+ factor: 100000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectopascal per kelvin
+ ConversionFactor: 10² kg x m⁻¹ x s⁻² x K⁻¹
+ Symbol: hPa/K
+ CommonCode: F82
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilopascal per kelvin
+ ConversionFactor: 10³ kg x m⁻¹ x s⁻² x K⁻¹
+ Symbol: kPa/K
+ CommonCode: F83
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millibar per kelvin
+ ConversionFactor: 10² kg x m⁻¹ x s⁻² x K⁻¹
+ Symbol: mbar/K
+ CommonCode: F84
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megapascal per kelvin
+ ConversionFactor: 10⁶ kg x m⁻¹ x s⁻² x K⁻¹
+ Symbol: MPa/K
+ CommonCode: F85
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poise per kelvin
+ ConversionFactor: 10⁻¹ kg x m⁻¹ x s⁻¹ x K⁻¹
+ Symbol: P/K
+ CommonCode: F86
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - F77
+- Status: ''
+ LevelAndCategory: 1M
+ Name: volt per litre minute
+ ConversionFactor: 1,666 67 × 10¹ kg x m⁻¹ x s⁻⁴ x A⁻¹
+ Symbol: V/(l·min)
+ CommonCode: F87
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton centimetre
+ ConversionFactor: 10⁻² kg x m² x s⁻²
+ Symbol: N·cm
+ CommonCode: F88
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton metre per degree
+ ConversionFactor: 57,295 788 kg x m² x s⁻² x rad⁻¹
+ Symbol: Nm/°
+ CommonCode: F89
+ Description: ''
+ conversion:
+ factor: 57.295788
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: fibre per cubic centimetre of air
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: F9
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton metre per ampere
+ ConversionFactor: kg x m² x s⁻² x A⁻¹
+ Symbol: N·m/A
+ CommonCode: F90
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - F90
+- Status: ''
+ LevelAndCategory: 1M
+ Name: bar litre per second
+ ConversionFactor: 10² kg x m² x s⁻³
+ Symbol: bar·l/s
+ CommonCode: F91
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: bar cubic metre per second
+ ConversionFactor: 10⁵ kg x m² x s⁻³
+ Symbol: bar·m³/s
+ CommonCode: F92
+ Description: ''
+ conversion:
+ factor: 100000.0
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectopascal litre per second
+ ConversionFactor: 10⁻¹ kg x m² x s⁻³
+ Symbol: hPa·l/s
+ CommonCode: F93
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectopascal cubic metre per second
+ ConversionFactor: 10² kg x m² x s⁻³
+ Symbol: hPa·m³/s
+ CommonCode: F94
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millibar litre per second
+ ConversionFactor: 10⁻¹ kg x m² x s⁻³
+ Symbol: mbar·l/s
+ CommonCode: F95
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millibar cubic metre per second
+ ConversionFactor: 10² kg x m² x s⁻³
+ Symbol: mbar·m³/s
+ CommonCode: F96
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megapascal litre per second
+ ConversionFactor: 10³ kg x m² x s⁻³
+ Symbol: MPa·l/s
+ CommonCode: F97
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megapascal cubic metre per second
+ ConversionFactor: 10⁶ kg x m² x s⁻³
+ Symbol: MPa·m³/s
+ CommonCode: F98
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: pascal litre per second
+ ConversionFactor: 10⁻³ kg x m² x s⁻³
+ Symbol: Pa·l/s
+ CommonCode: F99
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit
+ ConversionFactor: 5/9 x K
+ Symbol: "°F"
+ CommonCode: FAH
+ Description: 'Refer ISO 80000-5 (Quantities and units — Part 5: Thermodynamics)'
+ conversion:
+ factor: 5.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1'
+ Name: farad
+ ConversionFactor: F
+ Symbol: F
+ CommonCode: FAR
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - FAR
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: field
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: FB
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: fibre metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: FBM
+ Description: A unit of length defining the number of metres of individual fibre.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: thousand cubic foot
+ ConversionFactor: ''
+ Symbol: kft³
+ CommonCode: FC
+ Description: A unit of volume equal to one thousand cubic foot.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: million particle per cubic foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: FD
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: track foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: FE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: hundred cubic metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: FF
+ Description: A unit of volume equal to one hundred cubic metres.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: transdermal patch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: FG
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: micromole
+ ConversionFactor: 10⁻⁶ mol
+ Symbol: µmol
+ CommonCode: FH
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - C34
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: failures in time
+ ConversionFactor: 2,777 78 × 10⁻¹³ s⁻¹
+ Symbol: ''
+ CommonCode: FIT
+ Description: A unit of count defining the number of failures that can be expected
+ over a specified time interval. Failure rates of semiconductor components are
+ often specified as FIT (failures in time unit) where 1 FIT = 10⁻⁹ /h.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: flake ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: FL
+ Description: 'A unit of mass defining the number of tons of a flaked substance (flake:
+ a small flattish fragment).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: million cubic foot
+ ConversionFactor: ''
+ Symbol: Mft³
+ CommonCode: FM
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot
+ ConversionFactor: 0,304 8 m
+ Symbol: ft
+ CommonCode: FOT
+ Description: ''
+ conversion:
+ factor: 0.3048
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per square foot
+ ConversionFactor: 4,882 428 kg/m²
+ Symbol: lb/ft²
+ CommonCode: FP
+ Description: ''
+ conversion:
+ factor: 4.882428
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per minute
+ ConversionFactor: 5,08 x 10⁻³ m/s
+ Symbol: ft/min
+ CommonCode: FR
+ Description: ''
+ conversion:
+ factor: 0.00508
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per second
+ ConversionFactor: 0,304 8 m/s
+ Symbol: ft/s
+ CommonCode: FS
+ Description: ''
+ conversion:
+ factor: 0.3048
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square foot
+ ConversionFactor: 9,290 304 x 10⁻² m²
+ Symbol: ft²
+ CommonCode: FTK
+ Description: ''
+ conversion:
+ factor: 0.09290304
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic foot
+ ConversionFactor: 2,831 685 x 10⁻² m³
+ Symbol: ft³
+ CommonCode: FTQ
+ Description: ''
+ conversion:
+ factor: 0.02831685
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1M
+ Name: pascal cubic metre per second
+ ConversionFactor: kg x m² x s⁻³
+ Symbol: Pa·m³/s
+ CommonCode: G01
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G01
+- Status: ''
+ LevelAndCategory: 1M
+ Name: centimetre per bar
+ ConversionFactor: 10⁻⁷ kg⁻¹ x m² x s²
+ Symbol: cm/bar
+ CommonCode: G04
+ Description: ''
+ conversion:
+ factor: 1.0e-07
+ base_units:
+ - M53
+- Status: ''
+ LevelAndCategory: 1M
+ Name: metre per bar
+ ConversionFactor: 10⁻⁵ kg⁻¹ x m² x s²
+ Symbol: m/bar
+ CommonCode: G05
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M53
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millimetre per bar
+ ConversionFactor: 10⁻⁸ kg⁻¹ x m² x s²
+ Symbol: mm/bar
+ CommonCode: G06
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - M53
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square inch per second
+ ConversionFactor: 6,451 6 × 10⁻⁴ m² x s⁻¹
+ Symbol: in²/s
+ CommonCode: G08
+ Description: ''
+ conversion:
+ factor: 6.4516
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: square metre per second kelvin
+ ConversionFactor: m² x s⁻¹ x K⁻¹
+ Symbol: m²/(s·K)
+ CommonCode: G09
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G09
+- Status: ''
+ LevelAndCategory: '2'
+ Name: stokes per kelvin
+ ConversionFactor: 10⁻⁴ m² x s⁻¹ x K⁻¹
+ Symbol: St/K
+ CommonCode: G10
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - G09
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic centimetre bar
+ ConversionFactor: 10⁻² m⁻² x s²
+ Symbol: g/(cm³·bar)
+ CommonCode: G11
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic decimetre bar
+ ConversionFactor: 10⁻⁵ m⁻² x s²
+ Symbol: g/(dm³·bar)
+ CommonCode: G12
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per litre bar
+ ConversionFactor: 10⁻⁵ m⁻² x s²
+ Symbol: g/(l·bar)
+ CommonCode: G13
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic metre bar
+ ConversionFactor: 10⁻⁸ m⁻² x s²
+ Symbol: g/(m³·bar)
+ CommonCode: G14
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per millilitre bar
+ ConversionFactor: 10⁻² m⁻² x s²
+ Symbol: g/(ml·bar)
+ CommonCode: G15
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic centimetre bar
+ ConversionFactor: 10¹ m⁻² x s²
+ Symbol: kg/(cm³·bar)
+ CommonCode: G16
+ Description: ''
+ conversion:
+ factor: 10.0
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per litre bar
+ ConversionFactor: 10⁻² m⁻² x s²
+ Symbol: kg/(l·bar)
+ CommonCode: G17
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic metre bar
+ ConversionFactor: 10⁻⁵ m⁻² x s²
+ Symbol: kg/(m³·bar)
+ CommonCode: G18
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton metre per kilogram
+ ConversionFactor: m² x s⁻²
+ Symbol: N·m/kg
+ CommonCode: G19
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G19
+- Status: ''
+ LevelAndCategory: '2'
+ Name: US gallon per minute
+ ConversionFactor: 6,309 020 x 10⁻⁵ m³/s
+ Symbol: gal (US) /min
+ CommonCode: G2
+ Description: ''
+ conversion:
+ factor: 6.30902e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force foot per pound
+ ConversionFactor: 2,989 07 m² x s⁻²
+ Symbol: lbf·ft/lb
+ CommonCode: G20
+ Description: ''
+ conversion:
+ factor: 2.98907
+ base_units:
+ - G19
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cup [unit of volume]
+ ConversionFactor: 2,365 882 x 10⁻⁴ m³
+ Symbol: cup (US)
+ CommonCode: G21
+ Description: ''
+ conversion:
+ factor: 0.0002365882
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck
+ ConversionFactor: 8,809 768 x 10⁻³ m³
+ Symbol: pk (US)
+ CommonCode: G23
+ Description: ''
+ conversion:
+ factor: 0.008809768
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tablespoon (US)
+ ConversionFactor: 1,478 676 x 10⁻⁵ m³
+ Symbol: tablespoon (US)
+ CommonCode: G24
+ Description: ''
+ conversion:
+ factor: 1.478676e-05
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: teaspoon (US)
+ ConversionFactor: 4,928 922 x 10⁻⁶ m³
+ Symbol: teaspoon (US)
+ CommonCode: G25
+ Description: ''
+ conversion:
+ factor: 4.928922e-06
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1M
+ Name: stere
+ ConversionFactor: m³
+ Symbol: st
+ CommonCode: G26
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per kelvin
+ ConversionFactor: 10⁻⁶ m³ x K⁻¹
+ Symbol: cm³/K
+ CommonCode: G27
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - G29
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per kelvin
+ ConversionFactor: 10⁻³ m³ x K⁻¹
+ Symbol: l/K
+ CommonCode: G28
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - G29
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per kelvin
+ ConversionFactor: m³ x K⁻¹
+ Symbol: m³/K
+ CommonCode: G29
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G29
+- Status: ''
+ LevelAndCategory: '2'
+ Name: Imperial gallon per minute
+ ConversionFactor: 7,576 82 x 10⁻⁵ m³/s
+ Symbol: gal (UK) /min
+ CommonCode: G3
+ Description: ''
+ conversion:
+ factor: 7.57682e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per kelvin
+ ConversionFactor: 10⁻⁶ m³ x K⁻¹
+ Symbol: ml/K
+ CommonCode: G30
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - G29
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic centimetre
+ ConversionFactor: 10⁶ kg x m⁻³
+ Symbol: kg/cm³
+ CommonCode: G31
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - F23
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per cubic yard
+ ConversionFactor: 3,707 98 × 10⁻² kg x m⁻³
+ Symbol: oz/yd³
+ CommonCode: G32
+ Description: ''
+ conversion:
+ factor: 3.70798
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic centimetre kelvin
+ ConversionFactor: 10³ kg x m⁻³ x K⁻¹
+ Symbol: g/(cm³·K)
+ CommonCode: G33
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic decimetre kelvin
+ ConversionFactor: kg x m⁻³ x K⁻¹
+ Symbol: g/(dm³·K)
+ CommonCode: G34
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per litre kelvin
+ ConversionFactor: kg x m⁻³ x K⁻¹
+ Symbol: g/(l·K)
+ CommonCode: G35
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per cubic metre kelvin
+ ConversionFactor: 10⁻³ kg x m⁻³ x K⁻¹
+ Symbol: g/(m³·K)
+ CommonCode: G36
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per millilitre kelvin
+ ConversionFactor: 10³ kg x m⁻³ x K⁻¹
+ Symbol: g/(ml·K)
+ CommonCode: G37
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic centimetre kelvin
+ ConversionFactor: 10⁶ kg x m⁻³ x K⁻¹
+ Symbol: kg/(cm³·K)
+ CommonCode: G38
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per litre kelvin
+ ConversionFactor: 10³ kg x m⁻³ x K⁻¹
+ Symbol: kg/(l·K)
+ CommonCode: G39
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic metre kelvin
+ ConversionFactor: kg x m⁻³ x K⁻¹
+ Symbol: kg/(m³·K)
+ CommonCode: G40
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G34
+ - G35
+ - G40
+- Status: ''
+ LevelAndCategory: 1M
+ Name: square metre per second bar
+ ConversionFactor: 10⁻⁵ kg⁻¹ x m³ x s
+ Symbol: m²/(s·bar)
+ CommonCode: G41
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M82
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microsiemens per centimetre
+ ConversionFactor: 10⁻⁴ S/m
+ Symbol: µS/cm
+ CommonCode: G42
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microsiemens per metre
+ ConversionFactor: 10⁻⁶ S/m
+ Symbol: µS/m
+ CommonCode: G43
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: 1M
+ Name: nanosiemens per centimetre
+ ConversionFactor: 10⁻⁷ S/m
+ Symbol: nS/cm
+ CommonCode: G44
+ Description: ''
+ conversion:
+ factor: 1.0e-07
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: 1M
+ Name: nanosiemens per metre
+ ConversionFactor: 10⁻⁹ S/m
+ Symbol: nS/m
+ CommonCode: G45
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: '2'
+ Name: stokes per bar
+ ConversionFactor: 10⁻⁹ kg⁻¹ x m³ x s
+ Symbol: St/bar
+ CommonCode: G46
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - M82
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per day
+ ConversionFactor: 1,157 41 × 10⁻¹¹ m³ x s⁻¹
+ Symbol: cm³/d
+ CommonCode: G47
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per hour
+ ConversionFactor: 2,777 78 × 10⁻¹⁰ m³ x s⁻¹
+ Symbol: cm³/h
+ CommonCode: G48
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per minute
+ ConversionFactor: 1,666 67 × 10⁻⁸ m³ x s⁻¹
+ Symbol: cm³/min
+ CommonCode: G49
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gallon (US) per hour
+ ConversionFactor: 1,051 5 × 10⁻⁶ m³ x s⁻¹
+ Symbol: gal/h
+ CommonCode: G50
+ Description: ''
+ conversion:
+ factor: 1.0515
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per second
+ ConversionFactor: 10⁻³ m³ x s⁻¹
+ Symbol: l/s
+ CommonCode: G51
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per day
+ ConversionFactor: 1,157 41 × 10⁻⁵ m³ x s⁻¹
+ Symbol: m³/d
+ CommonCode: G52
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per minute
+ ConversionFactor: 1,666 67 × 10⁻² m³ x s⁻¹
+ Symbol: m³/min
+ CommonCode: G53
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per day
+ ConversionFactor: 1,157 41 × 10⁻¹¹ m³ x s⁻¹
+ Symbol: ml/d
+ CommonCode: G54
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per hour
+ ConversionFactor: 2,777 78 × 10⁻¹⁰ m³ x s⁻¹
+ Symbol: ml/h
+ CommonCode: G55
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic inch per hour
+ ConversionFactor: 4,551 96 × 10⁻⁹ m³ x s⁻¹
+ Symbol: in³/h
+ CommonCode: G56
+ Description: ''
+ conversion:
+ factor: 4.55196
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic inch per minute
+ ConversionFactor: 2,731 18 × 10⁻⁷ m³ x s⁻¹
+ Symbol: in³/min
+ CommonCode: G57
+ Description: ''
+ conversion:
+ factor: 2.73118
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic inch per second
+ ConversionFactor: 1,638 71 × 10⁻⁵ m³ x s⁻¹
+ Symbol: in³/s
+ CommonCode: G58
+ Description: ''
+ conversion:
+ factor: 1.63871
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milliampere per litre minute
+ ConversionFactor: 1,666 67 × 10⁻² m⁻³ x s⁻¹ x A
+ Symbol: mA/(l·min)
+ CommonCode: G59
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: volt per bar
+ ConversionFactor: 10⁻⁵ m³ x s⁻¹ x A⁻¹
+ Symbol: V/bar
+ CommonCode: G60
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - N98
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per day kelvin
+ ConversionFactor: 1,157 41 × 10⁻¹¹ m³ x s⁻¹ x K⁻¹
+ Symbol: cm³/(d·K)
+ CommonCode: G61
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per hour kelvin
+ ConversionFactor: 2,777 78 × 10⁻¹⁰ m³ x s⁻¹ x K⁻¹
+ Symbol: cm³/(h·K)
+ CommonCode: G62
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per minute kelvin
+ ConversionFactor: 1,666 67 × 10⁻⁸ m³ x s⁻¹ x K⁻¹
+ Symbol: cm³/(min·K)
+ CommonCode: G63
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per second kelvin
+ ConversionFactor: 10⁻⁶ m³ x s⁻¹ x K⁻¹
+ Symbol: cm³/(s·K)
+ CommonCode: G64
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - G72
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per day kelvin
+ ConversionFactor: 1,157 41 × 10⁻⁸ m³ x s⁻¹ x K⁻¹
+ Symbol: l/(d·K)
+ CommonCode: G65
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per hour kelvin
+ ConversionFactor: 2,777 78 × 10⁻⁷ m³ x s⁻¹ x K⁻¹
+ Symbol: l/(h·K)
+ CommonCode: G66
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per minute kelvin
+ ConversionFactor: 1,666 67 × 10⁻⁵ m³ x s⁻¹ x K⁻¹
+ Symbol: l/(min·K)
+ CommonCode: G67
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per second kelvin
+ ConversionFactor: 10⁻³ m³ x s⁻¹ x K⁻¹
+ Symbol: l/(s·K)
+ CommonCode: G68
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - G72
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per day kelvin
+ ConversionFactor: 1,157 41 × 10⁻⁵ m³ x s⁻¹ x K⁻¹
+ Symbol: m³/(d·K)
+ CommonCode: G69
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: microfiche sheet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: G7
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per hour kelvin
+ ConversionFactor: 2,777 78 × 10⁻⁴ m³ x s⁻¹ x K⁻¹
+ Symbol: m³/(h·K)
+ CommonCode: G70
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per minute kelvin
+ ConversionFactor: 1,666 67 × 10⁻² m³ x s⁻¹ x K⁻¹
+ Symbol: m³/(min·K)
+ CommonCode: G71
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per second kelvin
+ ConversionFactor: m³ x s⁻¹ x K⁻¹
+ Symbol: m³/(s·K)
+ CommonCode: G72
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - G72
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per day kelvin
+ ConversionFactor: 1,157 41 × 10⁻¹¹ m³ x s⁻¹ x K⁻¹
+ Symbol: ml/(d·K)
+ CommonCode: G73
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per hour kelvin
+ ConversionFactor: 2,777 78 × 10⁻¹⁰ m³ x s⁻¹ x K⁻¹
+ Symbol: ml/(h·K)
+ CommonCode: G74
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per minute kelvin
+ ConversionFactor: 1,666 67 × 10⁻⁸ m³ x s⁻¹ x K⁻¹
+ Symbol: ml/(min·K)
+ CommonCode: G75
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per second kelvin
+ ConversionFactor: 10⁻⁶ m³ x s⁻¹ x K⁻¹
+ Symbol: ml/(s·K)
+ CommonCode: G76
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - G72
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millimetre to the fourth power
+ ConversionFactor: 10⁻¹² m⁴
+ Symbol: mm⁴
+ CommonCode: G77
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - B83
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per day bar
+ ConversionFactor: 1,157 41 × 10⁻¹⁶ kg⁻¹ x m⁴ x s
+ Symbol: cm³/(d·bar)
+ CommonCode: G78
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per hour bar
+ ConversionFactor: 2,777 78 × 10⁻¹⁵ kg⁻¹ x m⁴ x s
+ Symbol: cm³/(h·bar)
+ CommonCode: G79
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per minute bar
+ ConversionFactor: 1,666 67 × 10⁻¹³ kg⁻¹ x m⁴ x s
+ Symbol: cm³/(min·bar)
+ CommonCode: G80
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per second bar
+ ConversionFactor: 10⁻¹¹ kg⁻¹ x m⁴ x s
+ Symbol: cm³/(s·bar)
+ CommonCode: G81
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units:
+ - N45
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per day bar
+ ConversionFactor: 1,157 41 × 10⁻¹³ kg⁻¹ x m⁴ x s
+ Symbol: l/(d·bar)
+ CommonCode: G82
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per hour bar
+ ConversionFactor: 2,777 78 × 10⁻¹² kg⁻¹ x m⁴ x s
+ Symbol: l/(h·bar)
+ CommonCode: G83
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per minute bar
+ ConversionFactor: 1,666 67 × 10⁻¹⁰ kg⁻¹ x m⁴ x s
+ Symbol: l/(min·bar)
+ CommonCode: G84
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per second bar
+ ConversionFactor: 10⁻⁸ kg⁻¹ x m⁴ x s
+ Symbol: l/(s·bar)
+ CommonCode: G85
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - N45
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per day bar
+ ConversionFactor: 1,157 41 × 10⁻¹⁰ kg⁻¹ x m⁴ x s
+ Symbol: m³/(d·bar)
+ CommonCode: G86
+ Description: ''
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per hour bar
+ ConversionFactor: 2,777 78 × 10⁻⁹ kg⁻¹ x m⁴ x s
+ Symbol: m³/(h·bar)
+ CommonCode: G87
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per minute bar
+ ConversionFactor: 1,666 67 × 10⁻⁷ kg⁻¹ x m⁴ x s
+ Symbol: m³/(min·bar)
+ CommonCode: G88
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per second bar
+ ConversionFactor: 10⁻⁵ kg⁻¹ x m⁴ x s
+ Symbol: m³/(s·bar)
+ CommonCode: G89
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - N45
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per day bar
+ ConversionFactor: 1,157 41 x 10⁻¹⁶ x kg⁻¹ x m⁴ x s
+ Symbol: ml/(d·bar)
+ CommonCode: G90
+ Description: ''
+ conversion:
+ factor: 1.15741e-16
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per hour bar
+ ConversionFactor: 2,777 78 x 10⁻¹⁵ x kg⁻¹ x m⁴ x s
+ Symbol: ml/(h·bar)
+ CommonCode: G91
+ Description: ''
+ conversion:
+ factor: 2.77778e-15
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per minute bar
+ ConversionFactor: 1,666 67 × 10⁻¹³ x kg⁻¹ x m⁴ x s
+ Symbol: ml/(min·bar)
+ CommonCode: G92
+ Description: ''
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per second bar
+ ConversionFactor: 10⁻¹¹ kg⁻¹ x m⁴ x s
+ Symbol: ml/(s·bar)
+ CommonCode: G93
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units:
+ - N45
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic centimetre per bar
+ ConversionFactor: 10⁻¹¹ kg⁻¹ x m⁴ x s²
+ Symbol: cm³/bar
+ CommonCode: G94
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units:
+ - M71
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per bar
+ ConversionFactor: 10⁻⁸ kg⁻¹ x m⁴ x s²
+ Symbol: l/bar
+ CommonCode: G95
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - M71
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per bar
+ ConversionFactor: 10⁻⁵ kg⁻¹ x m⁴ x s²
+ Symbol: m³/bar
+ CommonCode: G96
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M71
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per bar
+ ConversionFactor: 10⁻¹¹ kg⁻¹ x m⁴ x s²
+ Symbol: ml/bar
+ CommonCode: G97
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units:
+ - M71
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microhenry per kiloohm
+ ConversionFactor: 10⁻⁹ s
+ Symbol: µH/kΩ
+ CommonCode: G98
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microhenry per ohm
+ ConversionFactor: 10⁻⁶ s
+ Symbol: µH/Ω
+ CommonCode: G99
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: gallon (US) per day
+ ConversionFactor: 4,381 264 x 10⁻⁸ m³/s
+ Symbol: gal (US)/d
+ CommonCode: GB
+ Description: ''
+ conversion:
+ factor: 4.381264e-08
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gigabecquerel
+ ConversionFactor: 10⁹ Bq
+ Symbol: GBq
+ CommonCode: GBQ
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: gram per 100 gram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GC
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: gross barrel
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GD
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: gram, dry weight
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GDW
+ Description: A unit of mass defining the number of grams of a product, disregarding
+ the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per gallon (US)
+ ConversionFactor: 1,198 264 x 10² kg/m³
+ Symbol: lb/gal (US)
+ CommonCode: GE
+ Description: ''
+ conversion:
+ factor: 119.8264
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per metre (gram per 100 centimetres)
+ ConversionFactor: 10⁻³ kg/m
+ Symbol: g/m
+ CommonCode: GF
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: gram of fissile isotope
+ ConversionFactor: ''
+ Symbol: gi F/S
+ CommonCode: GFI
+ Description: 'A unit of mass defining the number of grams of a fissile isotope (fissile
+ isotope: an isotope whose nucleus is able to be split when irradiated with low
+ energy neutrons).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: great gross
+ ConversionFactor: '1728'
+ Symbol: ''
+ CommonCode: GGR
+ Description: A unit of count defining the number of units in multiples of 1728 (12
+ x 12 x 12).
+ conversion:
+ factor: 1728.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: half gallon (US)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GH
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: gill (US)
+ ConversionFactor: 1,182 941 x 10⁻⁴ m³
+ Symbol: gi (US)
+ CommonCode: GIA
+ Description: ''
+ conversion:
+ factor: 0.0001182941
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: gram, including container
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GIC
+ Description: A unit of mass defining the number of grams of a product, including
+ its container.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: gill (UK)
+ ConversionFactor: 1,420 653 x 10⁻⁴ m³
+ Symbol: gi (UK)
+ CommonCode: GII
+ Description: ''
+ conversion:
+ factor: 0.0001420653
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: gram, including inner packaging
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GIP
+ Description: A unit of mass defining the number of grams of a product, including
+ its inner packaging materials.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gram per millilitre
+ ConversionFactor: 10³ kg/m³
+ Symbol: g/ml
+ CommonCode: GJ
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - GL
+ - KMQ
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: gram per kilogram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GK
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gram per litre
+ ConversionFactor: kg/m³
+ Symbol: g/l
+ CommonCode: GL
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: dry gallon (US)
+ ConversionFactor: 4,404 884 x 10⁻³ m³
+ Symbol: dry gal (US)
+ CommonCode: GLD
+ Description: ''
+ conversion:
+ factor: 0.004404884
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gallon (UK)
+ ConversionFactor: 4,546 092 x 10⁻³ m³
+ Symbol: gal (UK)
+ CommonCode: GLI
+ Description: ''
+ conversion:
+ factor: 0.004546092
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gallon (US)
+ ConversionFactor: 3,785 412 x 10⁻³ m³
+ Symbol: gal (US)
+ CommonCode: GLL
+ Description: ''
+ conversion:
+ factor: 0.003785412
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per square metre
+ ConversionFactor: 10⁻³ kg/m²
+ Symbol: g/m²
+ CommonCode: GM
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - '28'
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: gross gallon
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GN
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: milligram per square metre
+ ConversionFactor: 10⁻⁶ kg/m²
+ Symbol: mg/m²
+ CommonCode: GO
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per cubic metre
+ ConversionFactor: 10⁻⁶ kg/m³
+ Symbol: mg/m³
+ CommonCode: GP
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microgram per cubic metre
+ ConversionFactor: 10⁻⁹ kg/m³
+ Symbol: µg/m³
+ CommonCode: GQ
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gram
+ ConversionFactor: 10⁻³ kg
+ Symbol: g
+ CommonCode: GRM
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '2'
+ Name: grain
+ ConversionFactor: 64,798 91 x 10⁻⁶ kg
+ Symbol: gr
+ CommonCode: GRN
+ Description: ''
+ conversion:
+ factor: 6.479891e-05
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: gross
+ ConversionFactor: '144'
+ Symbol: gr
+ CommonCode: GRO
+ Description: A unit of count defining the number of units in multiples of 144 (12
+ x 12).
+ conversion:
+ factor: 144.0
+- Status: D
+ LevelAndCategory: '3.4'
+ Name: gross register ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GRT
+ Description: A unit of mass equal to the total cubic footage before deductions,
+ where 1 register ton is equal to 100 cubic feet. Refer International Convention
+ on tonnage measurement of ships.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: 3.1,3.4
+ Name: gross ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GT
+ Description: 'A unit of mass equal to 2240 pounds. Refer International Convention
+ on Tonnage measurement of Ships.,Synonym: ton (UK) or long ton (US) (common code
+ LTN)'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigajoule
+ ConversionFactor: 10⁹ J
+ Symbol: GJ
+ CommonCode: GV
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: gallon per thousand cubic foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GW
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: gigawatt hour
+ ConversionFactor: 3,6 x 10¹² J
+ Symbol: GW·h
+ CommonCode: GWH
+ Description: ''
+ conversion:
+ factor: 3600000000000.0
+ base_units:
+ - JOU
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: gross yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GY
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: gage system
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: GZ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: henry per kiloohm
+ ConversionFactor: 10⁻³ s
+ Symbol: H/kΩ
+ CommonCode: H03
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: henry per ohm
+ ConversionFactor: s
+ Symbol: H/Ω
+ CommonCode: H04
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millihenry per kiloohm
+ ConversionFactor: 10⁻⁶ s
+ Symbol: mH/kΩ
+ CommonCode: H05
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millihenry per ohm
+ ConversionFactor: 10⁻³ s
+ Symbol: mH/Ω
+ CommonCode: H06
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: pascal second per bar
+ ConversionFactor: 10⁻⁵ s
+ Symbol: Pa·s/bar
+ CommonCode: H07
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microbecquerel
+ ConversionFactor: 10⁻⁶ Bq
+ Symbol: µBq
+ CommonCode: H08
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: reciprocal year
+ ConversionFactor: 3,168 81 x 10⁻⁸ s⁻¹
+ Symbol: 1/y
+ CommonCode: H09
+ Description: ''
+ conversion:
+ factor: 3.16881e-08
+ base_units:
+ - C97
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: half page – electronic
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: H1
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: reciprocal hour
+ ConversionFactor: 2,777 78 × 10⁻⁴ s⁻¹
+ Symbol: 1/h
+ CommonCode: H10
+ Description: ''
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: reciprocal month
+ ConversionFactor: 3,802 57 × 10⁻⁷ s⁻¹
+ Symbol: 1/mo
+ CommonCode: H11
+ Description: ''
+ conversion:
+ factor: 3.80257
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: degree Celsius per hour
+ ConversionFactor: 2,777 78 x 10⁻⁴ s⁻¹ K
+ Symbol: "°C/h"
+ CommonCode: H12
+ Description: ''
+ conversion:
+ factor: 0.000277778
+ base_units:
+ - H14
+- Status: ''
+ LevelAndCategory: 1M
+ Name: degree Celsius per minute
+ ConversionFactor: 1,666 67 x 10⁻² s⁻¹ K
+ Symbol: "°C/min"
+ CommonCode: H13
+ Description: ''
+ conversion:
+ factor: 0.0166667
+ base_units:
+ - H14
+- Status: ''
+ LevelAndCategory: 1M
+ Name: degree Celsius per second
+ ConversionFactor: s⁻¹ K
+ Symbol: "°C/s"
+ CommonCode: H14
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H14
+- Status: ''
+ LevelAndCategory: 1M
+ Name: square centimetre per gram
+ ConversionFactor: 10⁻¹ kg⁻¹ x m²
+ Symbol: cm²/g
+ CommonCode: H15
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: square decametre
+ ConversionFactor: 10² m²
+ Symbol: dam²
+ CommonCode: H16
+ Description: 'Synonym: are'
+ conversion:
+ factor: 100.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: 1S
+ Name: square hectometre
+ ConversionFactor: 10⁴ m²
+ Symbol: hm²
+ CommonCode: H18
+ Description: Synonym: hectare
+ conversion:
+ factor: 10000.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic hectometre
+ ConversionFactor: 10⁶ m³
+ Symbol: hm³
+ CommonCode: H19
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: half litre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: H2
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic kilometre
+ ConversionFactor: 10⁹ m³
+ Symbol: km³
+ CommonCode: H20
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: blank
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: H21
+ Description: A unit of count defining the number of blanks.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: volt square inch per pound-force
+ ConversionFactor: 1,450 377 439 8 × 10⁻⁴ m³ x s⁻¹ x A⁻¹
+ Symbol: V/(lbf/in²)
+ CommonCode: H22
+ Description: ''
+ conversion:
+ factor: 1.4503774398
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: volt per inch
+ ConversionFactor: 3,937 007 874 × 10¹ m x kg x s⁻³ x A⁻¹
+ Symbol: V/in
+ CommonCode: H23
+ Description: ''
+ conversion:
+ factor: 3.937007874
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: volt per microsecond
+ ConversionFactor: 10⁶ V/s
+ Symbol: V/µs
+ CommonCode: H24
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per kelvin
+ ConversionFactor: 10⁻² K⁻¹
+ Symbol: "%/K"
+ CommonCode: H25
+ Description: A unit of proportion, equal to 0.01, in relation to the SI base unit
+ Kelvin.
+ conversion:
+ factor: 0.01
+ base_units:
+ - C91
+ - N83
+- Status: ''
+ LevelAndCategory: 1M
+ Name: ohm per metre
+ ConversionFactor: Ω/m
+ Symbol: Ω/m
+ CommonCode: H26
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H26
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree per metre
+ ConversionFactor: 1,745 329 x 10⁻² rad/m
+ Symbol: "°/m"
+ CommonCode: H27
+ Description: ''
+ conversion:
+ factor: 0.01745329
+ base_units:
+ - C84
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microfarad per kilometre
+ ConversionFactor: 10⁻⁹ F/m
+ Symbol: µF/km
+ CommonCode: H28
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microgram per litre
+ ConversionFactor: 10⁻⁶ m⁻³ x kg
+ Symbol: µg/l
+ CommonCode: H29
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: square micrometre (square micron)
+ ConversionFactor: 10⁻¹² m²
+ Symbol: µm²
+ CommonCode: H30
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: ampere per kilogram
+ ConversionFactor: A x kg⁻¹
+ Symbol: A/kg
+ CommonCode: H31
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H31
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: ampere squared second
+ ConversionFactor: A² x s
+ Symbol: A²·s
+ CommonCode: H32
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H32
+- Status: ''
+ LevelAndCategory: 1S
+ Name: farad per kilometre
+ ConversionFactor: 10⁻³ F/m
+ Symbol: F/km
+ CommonCode: H33
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: hertz metre
+ ConversionFactor: Hz x m
+ Symbol: Hz·m
+ CommonCode: H34
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H34
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: kelvin metre per watt
+ ConversionFactor: K x m⁻¹ x kg⁻¹ x s³
+ Symbol: K·m/W
+ CommonCode: H35
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H35
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megaohm per kilometre
+ ConversionFactor: 10³ Ω/m
+ Symbol: MΩ/km
+ CommonCode: H36
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - H26
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megaohm per metre
+ ConversionFactor: 10⁶ Ω/m
+ Symbol: MΩ/m
+ CommonCode: H37
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - H26
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megaampere
+ ConversionFactor: 10⁶ A
+ Symbol: MA
+ CommonCode: H38
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: '2'
+ Name: megahertz kilometre
+ ConversionFactor: 10⁹ Hz x m
+ Symbol: MHz·km
+ CommonCode: H39
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units:
+ - H34
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: newton per ampere
+ ConversionFactor: kg x m x s⁻² x A⁻¹
+ Symbol: N/A
+ CommonCode: H40
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H40
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: newton metre watt to the power minus 0,5
+ ConversionFactor: kg x m² x s⁻² x W⁻⁰‧⁵
+ Symbol: N·m·W⁻⁰‧⁵
+ CommonCode: H41
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H41
+- Status: ''
+ LevelAndCategory: 1M
+ Name: pascal per metre
+ ConversionFactor: m⁻² kg x s⁻²
+ Symbol: Pa/m
+ CommonCode: H42
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H42
+- Status: ''
+ LevelAndCategory: 1S
+ Name: siemens per centimetre
+ ConversionFactor: 10² S/m
+ Symbol: S/cm
+ CommonCode: H43
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: 1S
+ Name: teraohm
+ ConversionFactor: 10¹² Ω
+ Symbol: TΩ
+ CommonCode: H44
+ Description: ''
+ conversion:
+ factor: 1000000000000.0
+ base_units:
+ - OHM
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: volt second per metre
+ ConversionFactor: m x kg x s⁻² x A⁻¹
+ Symbol: V·s/m
+ CommonCode: H45
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H45
+- Status: ''
+ LevelAndCategory: 1S
+ Name: volt per second
+ ConversionFactor: m² x kg x s⁻⁴ x A⁻¹
+ Symbol: V/s
+ CommonCode: H46
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H46
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: watt per cubic metre
+ ConversionFactor: m⁻¹ x kg x s⁻³
+ Symbol: W/m³
+ CommonCode: H47
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H47
+- Status: ''
+ LevelAndCategory: 1S
+ Name: attofarad
+ ConversionFactor: 10⁻¹⁸ m⁻² x kg⁻¹ x s⁴ x A²
+ Symbol: aF
+ CommonCode: H48
+ Description: ''
+ conversion:
+ factor: 1.0e-18
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: centimetre per hour
+ ConversionFactor: 0,277 777 778 × 10⁻⁶ m x s⁻¹
+ Symbol: cm/h
+ CommonCode: H49
+ Description: ''
+ conversion:
+ factor: 0.277777778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: reciprocal cubic centimetre
+ ConversionFactor: 10⁶ m⁻³
+ Symbol: cm⁻³
+ CommonCode: H50
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - C86
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: decibel per kilometre
+ ConversionFactor: 10⁻⁴ B/m
+ Symbol: dB/km
+ CommonCode: H51
+ Description: ''
+ conversion:
+ factor: 0.0001
+ base_units:
+ - P43
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: decibel per metre
+ ConversionFactor: 10⁻¹ B/m
+ Symbol: dB/m
+ CommonCode: H52
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - P43
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per bar
+ ConversionFactor: 10⁻⁵ m x s²
+ Symbol: kg/bar
+ CommonCode: H53
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - M74
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic decimetre kelvin
+ ConversionFactor: 10³ m⁻³ x kg x K⁻¹
+ Symbol: "(kg/dm³)/K"
+ CommonCode: H54
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic decimetre bar
+ ConversionFactor: 10⁻² m⁻² x s²
+ Symbol: "(kg/dm³)/bar"
+ CommonCode: H55
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: kilogram per square metre second
+ ConversionFactor: kg m⁻² x s⁻¹
+ Symbol: kg/(m²·s)
+ CommonCode: H56
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H56
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: inch per two pi radiant
+ ConversionFactor: 2,54 x 10⁻² m/(2 x π x rad)
+ Symbol: in/revolution
+ CommonCode: H57
+ Description: ''
+ conversion:
+ factor: 0.0254
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: metre per volt second
+ ConversionFactor: m⁻¹ x kg⁻¹ x s² x A
+ Symbol: m/(V·s)
+ CommonCode: H58
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H58
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: square metre per newton
+ ConversionFactor: m x kg⁻¹ x s²
+ Symbol: m²/N
+ CommonCode: H59
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H59
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per cubic metre
+ ConversionFactor: '1.0'
+ Symbol: m³/m³
+ CommonCode: H60
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - E98
+ - F02
+ - H60
+ - L52
+ - M91
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millisiemens per centimetre
+ ConversionFactor: 10⁻¹ S/m
+ Symbol: mS/cm
+ CommonCode: H61
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millivolt per minute
+ ConversionFactor: 1,666 666 667 × 10⁻⁵ m² x kg x s⁻⁴ x A⁻¹
+ Symbol: mV/min
+ CommonCode: H62
+ Description: ''
+ conversion:
+ factor: 1.666666667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milligram per square centimetre
+ ConversionFactor: 10⁻² m⁻² x kg
+ Symbol: mg/cm²
+ CommonCode: H63
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milligram per gram
+ ConversionFactor: 10⁻³ 1
+ Symbol: mg/g
+ CommonCode: H64
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per cubic metre
+ ConversionFactor: 10⁻⁶ 1
+ Symbol: ml/m³
+ CommonCode: H65
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: millimetre per year
+ ConversionFactor: 3,15576 × 10⁴ m x s⁻¹
+ Symbol: mm/y
+ CommonCode: H66
+ Description: ''
+ conversion:
+ factor: 3.15576
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: millimetre per hour
+ ConversionFactor: 0,277 777 778 × 10⁻⁷ m x s⁻¹
+ Symbol: mm/h
+ CommonCode: H67
+ Description: ''
+ conversion:
+ factor: 0.277777778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millimole per gram
+ ConversionFactor: mol x kg⁻¹
+ Symbol: mmol/g
+ CommonCode: H68
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H68
+- Status: ''
+ LevelAndCategory: 1M
+ Name: picopascal per kilometre
+ ConversionFactor: 10⁻¹⁵ m⁻² x kg x s⁻²
+ Symbol: pPa/km
+ CommonCode: H69
+ Description: ''
+ conversion:
+ factor: 1.0e-15
+ base_units: []
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: picosecond
+ ConversionFactor: 10⁻¹² s
+ Symbol: ps
+ CommonCode: H70
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per month
+ ConversionFactor: ''
+ Symbol: "%/mo"
+ CommonCode: H71
+ Description: A unit of proportion, equal to 0.01, in relation to a month.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per hectobar
+ ConversionFactor: ''
+ Symbol: "%/hbar"
+ CommonCode: H72
+ Description: A unit of proportion, equal to 0.01, in relation to 100-fold of the
+ unit bar.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per decakelvin
+ ConversionFactor: 10⁻³ K⁻¹
+ Symbol: "%/daK"
+ CommonCode: H73
+ Description: A unit of proportion, equal to 0.01, in relation to 10-fold of the
+ SI base unit Kelvin.
+ conversion:
+ factor: 0.001
+ base_units:
+ - C91
+ - N83
+- Status: ''
+ LevelAndCategory: 1M
+ Name: watt per metre
+ ConversionFactor: W m⁻¹
+ Symbol: W/m
+ CommonCode: H74
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H74
+- Status: ''
+ LevelAndCategory: 1M
+ Name: decapascal
+ ConversionFactor: 10¹ Pa
+ Symbol: daPa
+ CommonCode: H75
+ Description: ''
+ conversion:
+ factor: 10.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per millimetre
+ ConversionFactor: 10¹ kg x m⁻¹
+ Symbol: g/mm
+ CommonCode: H76
+ Description: ''
+ conversion:
+ factor: 10.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3'
+ Name: module width
+ ConversionFactor: ''
+ Symbol: MW
+ CommonCode: H77
+ Description: A unit of measure used to describe the breadth of electronic assemblies
+ as an installation standard or mounting dimension.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: conventional centimetre of water
+ ConversionFactor: 9,806 65 × 10¹ Pa
+ Symbol: cm H₂O
+ CommonCode: H78
+ Description: ''
+ conversion:
+ factor: 9.80665
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: French gauge
+ ConversionFactor: 0,333 333 333 × 10⁻³ m
+ Symbol: Fg
+ CommonCode: H79
+ Description: 'A unit of distance used for measuring the diameter of small tubes
+ such as urological instruments and catheters.,Synonym: French, Charrière, Charrière
+ gauge'
+ conversion:
+ factor: 0.333333333
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3'
+ Name: rack unit
+ ConversionFactor: 4,445 × 10⁻² m
+ Symbol: U or RU
+ CommonCode: H80
+ Description: A unit of measure used to describe the height in rack units of equipment
+ intended for mounting in a 19-inch rack or a 23-inch rack. One rack unit is 1.75
+ inches (44.45 mm) high.
+ conversion:
+ factor: 4.445
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millimetre per minute
+ ConversionFactor: 1,666 666 667 × 10⁻⁵ m x s⁻¹
+ Symbol: mm/min
+ CommonCode: H81
+ Description: ''
+ conversion:
+ factor: 1.666666667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: big point
+ ConversionFactor: 0,352 777 8 × 10⁻³ m
+ Symbol: bp
+ CommonCode: H82
+ Description: 'A unit of length defining the number of big points (big point: Adobe
+ software(US) defines the big point to be exactly 1/72 inch (0.013 888 9 inch or
+ 0.352 777 8 millimeters))'
+ conversion:
+ factor: 0.3527778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per kilogram
+ ConversionFactor: 10⁻³ m³ x kg⁻¹
+ Symbol: l/kg
+ CommonCode: H83
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram millimetre
+ ConversionFactor: 10⁻⁶ kg x m
+ Symbol: g·mm
+ CommonCode: H84
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - M94
+- Status: ''
+ LevelAndCategory: 1M
+ Name: reciprocal week
+ ConversionFactor: 1,647 989 452 868 × 10⁻⁶ s⁻¹
+ Symbol: 1/wk
+ CommonCode: H85
+ Description: ''
+ conversion:
+ factor: 1.647989452868
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: piece
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: H87
+ Description: 'A unit of count defining the number of pieces (piece: a single item,
+ article or exemplar).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megaohm kilometre
+ ConversionFactor: 10⁹ Ω x m
+ Symbol: MΩ·km
+ CommonCode: H88
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units:
+ - C61
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per ohm
+ ConversionFactor: 10⁻² Ω⁻¹
+ Symbol: "%/Ω"
+ CommonCode: H89
+ Description: A unit of proportion, equal to 0.01, in relation to the SI derived
+ unit ohm.
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per degree
+ ConversionFactor: 0,572 957 8 rad⁻¹
+ Symbol: "%/°"
+ CommonCode: H90
+ Description: A unit of proportion, equal to 0.01, in relation to an angle of one
+ degree.
+ conversion:
+ factor: 0.5729578
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per ten thousand
+ ConversionFactor: 10⁻⁶
+ Symbol: "%/10000"
+ CommonCode: H91
+ Description: A unit of proportion, equal to 0.01, in relation to multiples of ten
+ thousand.
+ conversion:
+ factor: 1.0e-06
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per one hundred thousand
+ ConversionFactor: 10⁻⁷
+ Symbol: "%/100000"
+ CommonCode: H92
+ Description: A unit of proportion, equal to 0.01, in relation to multiples of one
+ hundred thousand.
+ conversion:
+ factor: 1.0e-07
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per hundred
+ ConversionFactor: 10⁻⁴
+ Symbol: "%/100"
+ CommonCode: H93
+ Description: A unit of proportion, equal to 0.01, in relation to multiples of one
+ hundred.
+ conversion:
+ factor: 0.0001
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per thousand
+ ConversionFactor: 10⁻⁵
+ Symbol: "%/1000"
+ CommonCode: H94
+ Description: A unit of proportion, equal to 0.01, in relation to multiples of one
+ thousand.
+ conversion:
+ factor: 1.0e-05
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per volt
+ ConversionFactor: 10⁻² V⁻¹
+ Symbol: "%/V"
+ CommonCode: H95
+ Description: A unit of proportion, equal to 0.01, in relation to the SI derived
+ unit volt.
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per bar
+ ConversionFactor: 10⁻⁷ Pa⁻¹
+ Symbol: "%/bar"
+ CommonCode: H96
+ Description: A unit of proportion, equal to 0.01, in relation to an atmospheric
+ pressure of one bar.
+ conversion:
+ factor: 1.0e-07
+ base_units:
+ - C96
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per inch
+ ConversionFactor: 0,393 700 8 m⁻¹
+ Symbol: "%/in"
+ CommonCode: H98
+ Description: A unit of proportion, equal to 0.01, in relation to an inch.
+ conversion:
+ factor: 0.3937008
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per metre
+ ConversionFactor: 10⁻² m⁻¹
+ Symbol: "%/m"
+ CommonCode: H99
+ Description: A unit of proportion, equal to 0.01, in relation to a metre.
+ conversion:
+ factor: 0.01
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: hank
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HA
+ Description: A unit of length, typically for yarn.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2.0'
+ Name: hectare
+ ConversionFactor: 10⁴ m²
+ Symbol: ha
+ CommonCode: HAR
+ Description: 'Synonym: square hectometre'
+ conversion:
+ factor: 10000.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectobar
+ ConversionFactor: 10⁷ Pa
+ Symbol: hbar
+ CommonCode: HBA
+ Description: ''
+ conversion:
+ factor: 10000000.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: hundred boxes
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HBX
+ Description: A unit of count defining the number of boxes in multiples of one hundred
+ box units.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: hundred count
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HC
+ Description: A unit of count defining the number of units counted in multiples of
+ 100.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: half dozen
+ ConversionFactor: '6'
+ Symbol: ''
+ CommonCode: HD
+ Description: ''
+ conversion:
+ factor: 6.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: hundred kilogram, dry weight
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HDW
+ Description: A unit of mass defining the number of hundred kilograms of a product,
+ disregarding the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: hundredth of a carat
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: head
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HEA
+ Description: 'A unit of count defining the number of heads (head: a person or animal
+ considered as one of a number).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HF
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectogram
+ ConversionFactor: 10⁻¹ kg
+ Symbol: hg
+ CommonCode: HGM
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: hundred cubic foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HH
+ Description: A unit of volume equal to one hundred cubic foot.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred sheet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HI
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: hundred international unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HIU
+ Description: A unit of count defining the number of international units in multiples
+ of 100.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: metric horse power
+ ConversionFactor: 735,498 75 W
+ Symbol: metric hp
+ CommonCode: HJ
+ Description: ''
+ conversion:
+ factor: 735.49875
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred kilogram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HK
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: hundred kilogram, net mass
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HKM
+ Description: A unit of mass defining the number of hundred kilograms of a product,
+ after deductions.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred foot (linear)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HL
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: hectolitre
+ ConversionFactor: 10⁻¹ m³
+ Symbol: hl
+ CommonCode: HLT
+ Description: ''
+ conversion:
+ factor: 0.1
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mile per hour (statute mile)
+ ConversionFactor: 0,447 04 m/s
+ Symbol: mile/h
+ CommonCode: HM
+ Description: ''
+ conversion:
+ factor: 0.44704
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: million cubic metre
+ ConversionFactor: ''
+ Symbol: Mm³
+ CommonCode: HMQ
+ Description: A unit of volume equal to one million cubic metres.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectometre
+ ConversionFactor: 10² m
+ Symbol: hm
+ CommonCode: HMT
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - MTR
+- Status: D
+ LevelAndCategory: '2'
+ Name: conventional millimetre of mercury
+ ConversionFactor: 133,322 4 Pa
+ Symbol: mm Hg
+ CommonCode: HN
+ Description: ''
+ conversion:
+ factor: 133.3224
+ base_units:
+ - C55
+ - PAL
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred troy ounce
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HO
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: conventional millimetre of water
+ ConversionFactor: 9,806 65 Pa
+ Symbol: mm H₂O
+ CommonCode: HP
+ Description: ''
+ conversion:
+ factor: 9.80665
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: hectolitre of pure alcohol
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HPA
+ Description: A unit of volume equal to one hundred litres of pure alcohol.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred square foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HS
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: half hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HT
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: hertz
+ ConversionFactor: Hz
+ Symbol: Hz
+ CommonCode: HTZ
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - HTZ
+- Status: ''
+ LevelAndCategory: '1'
+ Name: hour
+ ConversionFactor: 3 600 s
+ Symbol: h
+ CommonCode: HUR
+ Description: ''
+ conversion:
+ factor: 3600.0
+ base_units:
+ - H04
+ - SEC
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: HY
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch pound (pound inch)
+ ConversionFactor: 1,152 12 x 10⁻² kg x m
+ Symbol: in·lb
+ CommonCode: IA
+ Description: ''
+ conversion:
+ factor: 0.0115212
+ base_units:
+ - M94
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: count per inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IC
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: person
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IE
+ Description: A unit of count defining the number of persons.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: inches of water
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IF
+ Description: Use inch of water (common code F78)
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: column inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: II
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: inch per minute
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IL
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: impression
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IM
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch
+ ConversionFactor: 25,4 x 10⁻³ m
+ Symbol: in
+ CommonCode: INH
+ Description: ''
+ conversion:
+ factor: 0.0254
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square inch
+ ConversionFactor: 6,451 6 x 10⁻⁴ m²
+ Symbol: in²
+ CommonCode: INK
+ Description: ''
+ conversion:
+ factor: 0.00064516
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic inch
+ ConversionFactor: 16,387 064 x 10⁻⁶ m³
+ Symbol: in³
+ CommonCode: INQ
+ Description: 'Synonym: inch cubed'
+ conversion:
+ factor: 1.6387064e-05
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: insurance policy
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IP
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: international sugar degree
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ISD
+ Description: A unit of measure defining the sugar content of a solution, expressed
+ in degrees.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: count per centimetre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IT
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per second
+ ConversionFactor: 0,025 4 m/s
+ Symbol: in/s
+ CommonCode: IU
+ Description: ''
+ conversion:
+ factor: 0.0254
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: international unit per gram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: IUG
+ Description: A unit of count defining the number of international units per gram.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per second squared
+ ConversionFactor: 0,025 4 m/s²
+ Symbol: in/s²
+ CommonCode: IV
+ Description: ''
+ conversion:
+ factor: 0.0254
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per millimetre
+ ConversionFactor: 10 m⁻¹
+ Symbol: "%/mm"
+ CommonCode: J10
+ Description: A unit of proportion, equal to 0.01, in relation to a millimetre.
+ conversion:
+ factor: 10.0
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: per mille per psi
+ ConversionFactor: 1,450 377 x 10⁻⁷ Pa⁻¹
+ Symbol: "‰/psi"
+ CommonCode: J12
+ Description: A unit of pressure equal to one thousandth of a psi (pound-force per
+ square inch).
+ conversion:
+ factor: 1.450377e-07
+ base_units:
+ - C96
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree API
+ ConversionFactor: ''
+ Symbol: "°API"
+ CommonCode: J13
+ Description: 'A unit of relative density as a measure of how heavy or light a petroleum
+ liquid is compared to water (API: American Petroleum Institute).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Baume (origin scale)
+ ConversionFactor: ''
+ Symbol: "°Bé"
+ CommonCode: J14
+ Description: A traditional unit of relative density for liquids. Named after Antoine
+ Baumé.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Baume (US heavy)
+ ConversionFactor: ''
+ Symbol: "°Bé (US heavy)"
+ CommonCode: J15
+ Description: A unit of relative density for liquids heavier than water.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Baume (US light)
+ ConversionFactor: ''
+ Symbol: "°Bé (US light)"
+ CommonCode: J16
+ Description: A unit of relative density for liquids lighter than water.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Balling
+ ConversionFactor: ''
+ Symbol: "°Balling"
+ CommonCode: J17
+ Description: A unit of density as a measure of sugar content, especially of beer
+ wort. Named after Karl Balling.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Brix
+ ConversionFactor: ''
+ Symbol: "°Bx"
+ CommonCode: J18
+ Description: A unit of proportion used in measuring the dissolved sugar-to-water
+ mass ratio of a liquid. Named after Adolf Brix.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit hour square foot per British thermal unit (thermochemical)
+ ConversionFactor: 0,176 228 m² x K/W
+ Symbol: "°F·h·ft²/Btuth"
+ CommonCode: J19
+ Description: ''
+ conversion:
+ factor: 0.176228
+ base_units:
+ - D19
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per kilogram
+ ConversionFactor: J/kg
+ Symbol: J/kg
+ CommonCode: J2
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - J2
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit per kelvin
+ ConversionFactor: 0,555 555 6
+ Symbol: "°F/K"
+ CommonCode: J20
+ Description: ''
+ conversion:
+ factor: 0.5555556
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit per bar
+ ConversionFactor: 0,555 555 6 x 10⁻⁵ K/Pa
+ Symbol: "°F/bar"
+ CommonCode: J21
+ Description: ''
+ conversion:
+ factor: 5.555556e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit hour square foot per British thermal unit (international
+ table)
+ ConversionFactor: 0,176 110 2 m² x K/W
+ Symbol: "°F·h·ft²/BtuIT"
+ CommonCode: J22
+ Description: ''
+ conversion:
+ factor: 0.1761102
+ base_units:
+ - D19
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit per hour
+ ConversionFactor: 1,543 210 x 10⁻⁴ K/s
+ Symbol: "°F/h"
+ CommonCode: J23
+ Description: ''
+ conversion:
+ factor: 0.000154321
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit per minute
+ ConversionFactor: 9,259 259 x 10⁻³ K/s
+ Symbol: "°F/min"
+ CommonCode: J24
+ Description: ''
+ conversion:
+ factor: 0.009259259
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Fahrenheit per second
+ ConversionFactor: 0,555 555 6 K/s
+ Symbol: "°F/s"
+ CommonCode: J25
+ Description: ''
+ conversion:
+ factor: 0.5555556
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal degree Fahrenheit
+ ConversionFactor: 1,8 1/K
+ Symbol: 1/°F
+ CommonCode: J26
+ Description: ''
+ conversion:
+ factor: 1.81
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Oechsle
+ ConversionFactor: ''
+ Symbol: "°Oechsle"
+ CommonCode: J27
+ Description: A unit of density as a measure of sugar content of must, the unfermented
+ liqueur from which wine is made. Named after Ferdinand Oechsle.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Rankine per hour
+ ConversionFactor: 1,543 210 x 10⁻⁴ K/s
+ Symbol: "°R/h"
+ CommonCode: J28
+ Description: ''
+ conversion:
+ factor: 0.000154321
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Rankine per minute
+ ConversionFactor: 9,259 259 x 10⁻³ K/s
+ Symbol: "°R/min"
+ CommonCode: J29
+ Description: ''
+ conversion:
+ factor: 0.009259259
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: degree Rankine per second
+ ConversionFactor: 0,555 555 6 K/s
+ Symbol: "°R/s"
+ CommonCode: J30
+ Description: ''
+ conversion:
+ factor: 0.5555556
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Twaddell
+ ConversionFactor: ''
+ Symbol: "°Tw"
+ CommonCode: J31
+ Description: A unit of density for liquids that are heavier than water. 1 degree
+ Twaddle represents a difference in specific gravity of 0.005.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: micropoise
+ ConversionFactor: 10⁻⁶ Pa x s
+ Symbol: µP
+ CommonCode: J32
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microgram per kilogram
+ ConversionFactor: 10⁻⁹
+ Symbol: µg/kg
+ CommonCode: J33
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+- Status: ''
+ LevelAndCategory: '2'
+ Name: microgram per cubic metre kelvin
+ ConversionFactor: 10⁻⁹ (kg/m³)/K
+ Symbol: "(µg/m³)/K"
+ CommonCode: J34
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: microgram per cubic metre bar
+ ConversionFactor: 10⁻¹⁴ (kg/m³)/Pa
+ Symbol: "(µg/m³)/bar"
+ CommonCode: J35
+ Description: ''
+ conversion:
+ factor: 1.0e-14
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microlitre per litre
+ ConversionFactor: 10⁻⁶
+ Symbol: µl/l
+ CommonCode: J36
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: baud
+ ConversionFactor: ''
+ Symbol: Bd
+ CommonCode: J38
+ Description: A unit of signal transmission speed equal to one signalling event per
+ second.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (mean)
+ ConversionFactor: 1,055 87 x 10³ J
+ Symbol: Btu
+ CommonCode: J39
+ Description: ''
+ conversion:
+ factor: 1055.87
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) foot per hour square foot degree
+ Fahrenheit
+ ConversionFactor: 1,730 735 W/(m x K)
+ Symbol: BtuIT·ft/(h·ft²·°F)
+ CommonCode: J40
+ Description: ''
+ conversion:
+ factor: 1.730735
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) inch per hour square foot degree
+ Fahrenheit
+ ConversionFactor: 0,144 227 9 W/(m x K)
+ Symbol: BtuIT·in/(h·ft²·°F)
+ CommonCode: J41
+ Description: ''
+ conversion:
+ factor: 0.1442279
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) inch per second square foot degree
+ Fahrenheit
+ ConversionFactor: 5,192 204 x 10² W/(m x K)
+ Symbol: BtuIT·in/(s·ft²·°F)
+ CommonCode: J42
+ Description: ''
+ conversion:
+ factor: 519.2204
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per pound degree Fahrenheit
+ ConversionFactor: 4,186 8 x 10³ J/(kg x K)
+ Symbol: BtuIT/(lb·°F)
+ CommonCode: J43
+ Description: ''
+ conversion:
+ factor: 4186.8
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per minute
+ ConversionFactor: 17,584 266 W
+ Symbol: BtuIT/min
+ CommonCode: J44
+ Description: ''
+ conversion:
+ factor: 17.584266
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per second
+ ConversionFactor: 1,055 056 x 10³ W
+ Symbol: BtuIT/s
+ CommonCode: J45
+ Description: ''
+ conversion:
+ factor: 1055.056
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) foot per hour square foot degree Fahrenheit
+ ConversionFactor: 1,729 577 W/(m x K)
+ Symbol: Btuth·ft/(h·ft²·°F)
+ CommonCode: J46
+ Description: ''
+ conversion:
+ factor: 1.729577
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per hour
+ ConversionFactor: 0,292 875 1 W
+ Symbol: Btuth/h
+ CommonCode: J47
+ Description: ''
+ conversion:
+ factor: 0.2928751
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) inch per hour square foot degree Fahrenheit
+ ConversionFactor: 0,144 131 4 W/(m x K)
+ Symbol: Btuth·in/(h·ft²·°F)
+ CommonCode: J48
+ Description: ''
+ conversion:
+ factor: 0.1441314
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) inch per second square foot degree Fahrenheit
+ ConversionFactor: 5,188 732 x 10² W/(m x K)
+ Symbol: Btuth·in/(s·ft²·°F)
+ CommonCode: J49
+ Description: ''
+ conversion:
+ factor: 518.8732
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per pound degree Fahrenheit
+ ConversionFactor: 4,184 x 10³ J/(kg x K)
+ Symbol: Btuth/(lb·°F)
+ CommonCode: J50
+ Description: ''
+ conversion:
+ factor: 4184.0
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per minute
+ ConversionFactor: 17,572 50 W
+ Symbol: Btuth/min
+ CommonCode: J51
+ Description: ''
+ conversion:
+ factor: 17.5725
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per second
+ ConversionFactor: 1,054 350 x 10³ W
+ Symbol: Btuth/s
+ CommonCode: J52
+ Description: ''
+ conversion:
+ factor: 1054.35
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: coulomb square metre per kilogram
+ ConversionFactor: C x m²/kg
+ Symbol: C·m²/kg
+ CommonCode: J53
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - J53
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: megabaud
+ ConversionFactor: 10⁶ Bd
+ Symbol: MBd
+ CommonCode: J54
+ Description: A unit of signal transmission speed equal to 10⁶ (1000000) signaling
+ events per second.
+ conversion:
+ factor: 1000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: watt second
+ ConversionFactor: W x s
+ Symbol: W·s
+ CommonCode: J55
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - J55
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bar per bar
+ ConversionFactor: '1'
+ Symbol: bar/bar
+ CommonCode: J56
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (UK petroleum)
+ ConversionFactor: 0,159 113 15 m³
+ Symbol: bbl (UK liq.)
+ CommonCode: J57
+ Description: ''
+ conversion:
+ factor: 0.15911315
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (UK petroleum) per minute
+ ConversionFactor: 2,651 886 m³/s
+ Symbol: bbl (UK liq.)/min
+ CommonCode: J58
+ Description: ''
+ conversion:
+ factor: 2.651886
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (UK petroleum) per day
+ ConversionFactor: 1,841 587 4 x 10⁻⁶ m³/s
+ Symbol: bbl (UK liq.)/d
+ CommonCode: J59
+ Description: ''
+ conversion:
+ factor: 1.8415874e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (UK petroleum) per hour
+ ConversionFactor: 4,419 810 x 10⁻⁵ m³/s
+ Symbol: bbl (UK liq.)/h
+ CommonCode: J60
+ Description: ''
+ conversion:
+ factor: 4.41981e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (UK petroleum) per second
+ ConversionFactor: 0,159 113 15 m³/s
+ Symbol: bbl (UK liq.)/s
+ CommonCode: J61
+ Description: ''
+ conversion:
+ factor: 0.15911315
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (US petroleum) per hour
+ ConversionFactor: 4,416 314 x 10⁻⁵ m³/s
+ Symbol: bbl (US)/h
+ CommonCode: J62
+ Description: ''
+ conversion:
+ factor: 4.416314e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: barrel (US petroleum) per second
+ ConversionFactor: 0,158 987 3 m³/s
+ Symbol: bbl (US)/s
+ CommonCode: J63
+ Description: ''
+ conversion:
+ factor: 0.1589873
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (UK) per day
+ ConversionFactor: 4,209 343 x 10⁻⁷ m³/s
+ Symbol: bu (UK)/d
+ CommonCode: J64
+ Description: ''
+ conversion:
+ factor: 4.209343e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (UK) per hour
+ ConversionFactor: 1,010 242 x 10⁻⁵ m³/s
+ Symbol: bu (UK)/h
+ CommonCode: J65
+ Description: ''
+ conversion:
+ factor: 1.010242e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (UK) per minute
+ ConversionFactor: 6,061 453 x 10⁻⁴ m³/s
+ Symbol: bu (UK)/min
+ CommonCode: J66
+ Description: ''
+ conversion:
+ factor: 0.0006061453
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (UK) per second
+ ConversionFactor: 3,636 872 x 10⁻² m³/s
+ Symbol: bu (UK)/s
+ CommonCode: J67
+ Description: ''
+ conversion:
+ factor: 0.03636872
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (US dry) per day
+ ConversionFactor: 4,078 596 x 10⁻⁷ m³/s
+ Symbol: bu (US dry)/d
+ CommonCode: J68
+ Description: ''
+ conversion:
+ factor: 4.078596e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (US dry) per hour
+ ConversionFactor: 9,788 631 x 10⁻⁶ m³/s
+ Symbol: bu (US dry)/h
+ CommonCode: J69
+ Description: ''
+ conversion:
+ factor: 9.788631e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (US dry) per minute
+ ConversionFactor: 5,873 178 x 10⁻⁴ m³/s
+ Symbol: bu (US dry)/min
+ CommonCode: J70
+ Description: ''
+ conversion:
+ factor: 0.0005873178
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: bushel (US dry) per second
+ ConversionFactor: 3,523 907 x 10⁻² m³/s
+ Symbol: bu (US dry)/s
+ CommonCode: J71
+ Description: ''
+ conversion:
+ factor: 0.03523907
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1S
+ Name: centinewton metre
+ ConversionFactor: 10⁻² N x m
+ Symbol: cN·m
+ CommonCode: J72
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centipoise per kelvin
+ ConversionFactor: 10⁻³ Pa x s/K
+ Symbol: cP/K
+ CommonCode: J73
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centipoise per bar
+ ConversionFactor: 10⁻⁸ s
+ Symbol: cP/bar
+ CommonCode: J74
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '2'
+ Name: calorie (mean)
+ ConversionFactor: 4,190 02 J
+ Symbol: cal
+ CommonCode: J75
+ Description: ''
+ conversion:
+ factor: 4.19002
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: calorie (international table) per gram degree Celsius
+ ConversionFactor: 4,186 8 x 10³ J/(kg x K)
+ Symbol: calIT/(g·°C)
+ CommonCode: J76
+ Description: ''
+ conversion:
+ factor: 4186.8
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per centimetre second degree Celsius
+ ConversionFactor: 4,184 x 10² W/(m x K)
+ Symbol: calth/(cm·s·°C)
+ CommonCode: J78
+ Description: ''
+ conversion:
+ factor: 418.4
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per gram degree Celsius
+ ConversionFactor: 4,184 x 10³ J/(kg x K)
+ Symbol: calth/(g·°C)
+ CommonCode: J79
+ Description: ''
+ conversion:
+ factor: 4184.0
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per minute
+ ConversionFactor: 6,973 333 x 10⁻² W
+ Symbol: calth/min
+ CommonCode: J81
+ Description: ''
+ conversion:
+ factor: 0.06973333
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: calorie (thermochemical) per second
+ ConversionFactor: 4,184 W
+ Symbol: calth/s
+ CommonCode: J82
+ Description: ''
+ conversion:
+ factor: 4.184
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: clo
+ ConversionFactor: 0,155 m² x K/W
+ Symbol: clo
+ CommonCode: J83
+ Description: ''
+ conversion:
+ factor: 0.155
+ base_units:
+ - D19
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centimetre per second kelvin
+ ConversionFactor: 10⁻² (m/s)/K
+ Symbol: "(cm/s)/K"
+ CommonCode: J84
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units:
+ - L12
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centimetre per second bar
+ ConversionFactor: 10⁻⁷ (m/s)/Pa
+ Symbol: "(cm/s)/bar"
+ CommonCode: J85
+ Description: ''
+ conversion:
+ factor: 1.0e-07
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic centimetre per cubic metre
+ ConversionFactor: 10⁻⁶
+ Symbol: cm³/m³
+ CommonCode: J87
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+- Status: D
+ LevelAndCategory: '2'
+ Name: centimetre of mercury
+ ConversionFactor: 1,333 224 x 10³ Pa
+ Symbol: cm Hg
+ CommonCode: J89
+ Description: ''
+ conversion:
+ factor: 1333.224
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decimetre per day
+ ConversionFactor: 1,157 41 x 10⁻⁸ m³/s
+ Symbol: dm³/d
+ CommonCode: J90
+ Description: ''
+ conversion:
+ factor: 1.15741e-08
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decimetre per cubic metre
+ ConversionFactor: 10⁻³
+ Symbol: dm³/m³
+ CommonCode: J91
+ Description: ''
+ conversion:
+ factor: 0.001
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decimetre per minute
+ ConversionFactor: 1,666 67 x 10⁻⁵ m³/s
+ Symbol: dm³/min
+ CommonCode: J92
+ Description: ''
+ conversion:
+ factor: 1.66667e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic decimetre per second
+ ConversionFactor: 10⁻³ m³/s
+ Symbol: dm³/s
+ CommonCode: J93
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - MQS
+- Status: D
+ LevelAndCategory: '2'
+ Name: dyne centimetre
+ ConversionFactor: 10⁻⁷ N x m
+ Symbol: dyn·cm
+ CommonCode: J94
+ Description: ''
+ conversion:
+ factor: 1.0e-07
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (UK fluid) per day
+ ConversionFactor: 3,288 549 x 10⁻¹⁰ m³/s
+ Symbol: fl oz (UK)/d
+ CommonCode: J95
+ Description: ''
+ conversion:
+ factor: 3.288549e-10
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (UK fluid) per hour
+ ConversionFactor: 7,892 517 x 10⁻⁹ m³/s
+ Symbol: fl oz (UK)/h
+ CommonCode: J96
+ Description: ''
+ conversion:
+ factor: 7.892517e-09
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (UK fluid) per minute
+ ConversionFactor: 4,735 51 x 10⁻⁷ m³/s
+ Symbol: fl oz (UK)/min
+ CommonCode: J97
+ Description: ''
+ conversion:
+ factor: 4.73551e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (UK fluid) per second
+ ConversionFactor: 2,841 306 x 10⁻⁵ m³/s
+ Symbol: fl oz (UK)/s
+ CommonCode: J98
+ Description: ''
+ conversion:
+ factor: 2.841306e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (US fluid) per day
+ ConversionFactor: 3,422 862 x 10⁻¹⁰ m³/s
+ Symbol: fl oz (US)/d
+ CommonCode: J99
+ Description: ''
+ conversion:
+ factor: 3.422862e-10
+ base_units:
+ - MQS
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: jumbo
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: JB
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule per kelvin
+ ConversionFactor: J/K
+ Symbol: J/K
+ CommonCode: JE
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - JE
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: jug
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: JG
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megajoule per kilogram
+ ConversionFactor: 10⁶ J/kg
+ Symbol: MJ/kg
+ CommonCode: JK
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - J2
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megajoule per cubic metre
+ ConversionFactor: 10⁶ J/m³
+ Symbol: MJ/m³
+ CommonCode: JM
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - B8
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pipeline joint
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: JNT
+ Description: A count of the number of pipeline joints.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: joint
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: JO
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: joule
+ ConversionFactor: J
+ Symbol: J
+ CommonCode: JOU
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: hundred metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: JPS
+ Description: A unit of count defining the number of 100 metre lengths.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: jar
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: JR
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: number of jewels
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: JWL
+ Description: 'A unit of count defining the number of jewels (jewel: precious stone).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: kilowatt demand
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: K1
+ Description: A unit of measure defining the power load measured at predetermined
+ intervals.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (US fluid) per hour
+ ConversionFactor: 8,214 869 x 10⁻⁹ m³/s
+ Symbol: fl oz (US)/h
+ CommonCode: K10
+ Description: ''
+ conversion:
+ factor: 8.214869e-09
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (US fluid) per minute
+ ConversionFactor: 4,928 922 x 10⁻⁷ m³/s
+ Symbol: fl oz (US)/min
+ CommonCode: K11
+ Description: ''
+ conversion:
+ factor: 4.928922e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (US fluid) per second
+ ConversionFactor: 2,957 353 x 10⁻⁵ m³/s
+ Symbol: fl oz (US)/s
+ CommonCode: K12
+ Description: ''
+ conversion:
+ factor: 2.957353e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per degree Fahrenheit
+ ConversionFactor: 0,548 64 m/K
+ Symbol: ft/°F
+ CommonCode: K13
+ Description: ''
+ conversion:
+ factor: 0.54864
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per hour
+ ConversionFactor: 8,466 667 x 10⁻⁵m/s
+ Symbol: ft/h
+ CommonCode: K14
+ Description: ''
+ conversion:
+ factor: 8.466667e-05
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot pound-force per hour
+ ConversionFactor: 3,766 161 x 10⁻⁴ W
+ Symbol: ft·lbf/h
+ CommonCode: K15
+ Description: ''
+ conversion:
+ factor: 0.0003766161
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot pound-force per minute
+ ConversionFactor: 2,259 697 x 10⁻² W
+ Symbol: ft·lbf/min
+ CommonCode: K16
+ Description: ''
+ conversion:
+ factor: 0.02259697
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per psi
+ ConversionFactor: 4,420 750 x 10⁻⁵ m/Pa
+ Symbol: ft/psi
+ CommonCode: K17
+ Description: ''
+ conversion:
+ factor: 4.42075e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per second degree Fahrenheit
+ ConversionFactor: 0,548 64 (m/s)/K
+ Symbol: "(ft/s)/°F"
+ CommonCode: K18
+ Description: ''
+ conversion:
+ factor: 0.54864
+ base_units:
+ - L12
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot per second psi
+ ConversionFactor: 4,420 750 x 10⁻⁵ (m/s)/Pa
+ Symbol: "(ft/s)/psi"
+ CommonCode: K19
+ Description: ''
+ conversion:
+ factor: 4.42075e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: kilovolt ampere reactive demand
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: K2
+ Description: A unit of measure defining the reactive power demand equal to one kilovolt
+ ampere of reactive power.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal cubic foot
+ ConversionFactor: 35,314 66 m⁻³
+ Symbol: 1/ft³
+ CommonCode: K20
+ Description: ''
+ conversion:
+ factor: 35.31466
+ base_units:
+ - C86
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic foot per degree Fahrenheit
+ ConversionFactor: 5,097 033 x 10⁻² m³/K
+ Symbol: ft³/°F
+ CommonCode: K21
+ Description: ''
+ conversion:
+ factor: 0.05097033
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic foot per day
+ ConversionFactor: 3,277 413 x 10⁻⁷ m³/s
+ Symbol: ft³/d
+ CommonCode: K22
+ Description: ''
+ conversion:
+ factor: 3.277413e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic foot per psi
+ ConversionFactor: 4,107 012 x 10⁻⁶ m³/Pa
+ Symbol: ft³/psi
+ CommonCode: K23
+ Description: ''
+ conversion:
+ factor: 4.107012e-06
+ base_units: []
+- Status: D
+ LevelAndCategory: '2'
+ Name: foot of water
+ ConversionFactor: 2,989 067 x 10³ Pa
+ Symbol: ft H₂O
+ CommonCode: K24
+ Description: ''
+ conversion:
+ factor: 2989.067
+ base_units:
+ - C55
+ - PAL
+- Status: D
+ LevelAndCategory: '2'
+ Name: foot of mercury
+ ConversionFactor: 4,063 666 x 10⁴ Pa
+ Symbol: ft Hg
+ CommonCode: K25
+ Description: ''
+ conversion:
+ factor: 40636.66
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gallon (UK) per day
+ ConversionFactor: 5,261 678 x 10⁻⁸ m³/s
+ Symbol: gal (UK)/d
+ CommonCode: K26
+ Description: ''
+ conversion:
+ factor: 5.261678e-08
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gallon (UK) per hour
+ ConversionFactor: 1,262 803 x 10⁻⁶ m³/s
+ Symbol: gal (UK)/h
+ CommonCode: K27
+ Description: ''
+ conversion:
+ factor: 1.262803e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gallon (UK) per second
+ ConversionFactor: 4,546 09 x 10⁻³ m³/s
+ Symbol: gal (UK)/s
+ CommonCode: K28
+ Description: ''
+ conversion:
+ factor: 0.00454609
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: kilovolt ampere reactive hour
+ ConversionFactor: ''
+ Symbol: kvar·h
+ CommonCode: K3
+ Description: A unit of measure defining the accumulated reactive energy equal to
+ one kilovolt ampere of reactive power per hour.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gallon (US liquid) per second
+ ConversionFactor: 3,785 412 x 10⁻³ m³/s
+ Symbol: gal (US liq.)/s
+ CommonCode: K30
+ Description: ''
+ conversion:
+ factor: 0.003785412
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gram-force per square centimetre
+ ConversionFactor: 98,066 5 Pa
+ Symbol: gf/cm²
+ CommonCode: K31
+ Description: ''
+ conversion:
+ factor: 98.0665
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (UK) per day
+ ConversionFactor: 1,644 274 x 10⁻⁵ m³/s
+ Symbol: gi (UK)/d
+ CommonCode: K32
+ Description: ''
+ conversion:
+ factor: 1.644274e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (UK) per hour
+ ConversionFactor: 3,946 258 x 10⁻⁸ m³/s
+ Symbol: gi (UK)/h
+ CommonCode: K33
+ Description: ''
+ conversion:
+ factor: 3.946258e-08
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (UK) per minute
+ ConversionFactor: 0,023 677 55 m³/s
+ Symbol: gi (UK)/min
+ CommonCode: K34
+ Description: ''
+ conversion:
+ factor: 0.02367755
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (UK) per second
+ ConversionFactor: 1,420 653 x 10⁻⁴ m³/s
+ Symbol: gi (UK)/s
+ CommonCode: K35
+ Description: ''
+ conversion:
+ factor: 0.0001420653
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (US) per day
+ ConversionFactor: 1,369 145 x 10⁻⁹ m³/s
+ Symbol: gi (US)/d
+ CommonCode: K36
+ Description: ''
+ conversion:
+ factor: 1.369145e-09
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (US) per hour
+ ConversionFactor: 3,285 947 x 10⁻⁸ m³/s
+ Symbol: gi (US)/h
+ CommonCode: K37
+ Description: ''
+ conversion:
+ factor: 3.285947e-08
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (US) per minute
+ ConversionFactor: 1,971 568 x 10⁻⁶ m³/s
+ Symbol: gi (US)/min
+ CommonCode: K38
+ Description: ''
+ conversion:
+ factor: 1.971568e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gill (US) per second
+ ConversionFactor: 1,182 941 x 10⁻⁴ m³/s
+ Symbol: gi (US)/s
+ CommonCode: K39
+ Description: ''
+ conversion:
+ factor: 0.0001182941
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: standard acceleration of free fall
+ ConversionFactor: 9,806 65 m/s²
+ Symbol: gn
+ CommonCode: K40
+ Description: ''
+ conversion:
+ factor: 9.80665
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: '2'
+ Name: grain per gallon (US)
+ ConversionFactor: 1,711 806 x 10⁻² kg/m³
+ Symbol: gr/gal (US)
+ CommonCode: K41
+ Description: ''
+ conversion:
+ factor: 0.01711806
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: horsepower (boiler)
+ ConversionFactor: 9,809 50 x 10³ W
+ Symbol: boiler hp
+ CommonCode: K42
+ Description: ''
+ conversion:
+ factor: 9809.5
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: horsepower (electric)
+ ConversionFactor: 746 W
+ Symbol: electric hp
+ CommonCode: K43
+ Description: ''
+ conversion:
+ factor: 746.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per degree Fahrenheit
+ ConversionFactor: 4,572 x 10⁻² m/K
+ Symbol: in/°F
+ CommonCode: K45
+ Description: ''
+ conversion:
+ factor: 0.04572
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per psi
+ ConversionFactor: 3,683 959 x 10⁻⁶ m/Pa
+ Symbol: in/psi
+ CommonCode: K46
+ Description: ''
+ conversion:
+ factor: 3.683959e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per second degree Fahrenheit
+ ConversionFactor: 4,572 x 10⁻² (m/s)/K
+ Symbol: "(in/s)/°F"
+ CommonCode: K47
+ Description: ''
+ conversion:
+ factor: 0.04572
+ base_units:
+ - L12
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per second psi
+ ConversionFactor: 3,683 959 x 10⁻⁶ (m/s)/Pa
+ Symbol: "(in/s)/psi"
+ CommonCode: K48
+ Description: ''
+ conversion:
+ factor: 3.683959e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal cubic inch
+ ConversionFactor: 6,102 375 9 x 10⁴ m⁻³
+ Symbol: 1/in³
+ CommonCode: K49
+ Description: ''
+ conversion:
+ factor: 61023.759
+ base_units:
+ - C86
+- Status: D
+ LevelAndCategory: 1S
+ Name: kilovolt ampere (reactive)
+ ConversionFactor: 10³ V x A
+ Symbol: kvar
+ CommonCode: K5
+ Description: Use kilovar (common code KVR)
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D44
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kilobaud
+ ConversionFactor: 10³ Bd
+ Symbol: kBd
+ CommonCode: K50
+ Description: A unit of signal transmission speed equal to 10³ (1000) signaling events
+ per second.
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (mean)
+ ConversionFactor: 4,190 02 x 10³ J
+ Symbol: kcal
+ CommonCode: K51
+ Description: ''
+ conversion:
+ factor: 4190.02
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (international table) per hour metre degree Celsius
+ ConversionFactor: 1,163 J/(m x s x K)
+ Symbol: kcal/(m·h·°C)
+ CommonCode: K52
+ Description: ''
+ conversion:
+ factor: 1.163
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (thermochemical)
+ ConversionFactor: 4,184 x 10³ J
+ Symbol: kcalth
+ CommonCode: K53
+ Description: ''
+ conversion:
+ factor: 4184.0
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (thermochemical) per minute
+ ConversionFactor: 69,733 33 W
+ Symbol: kcalth/min
+ CommonCode: K54
+ Description: ''
+ conversion:
+ factor: 69.73333
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (thermochemical) per second
+ ConversionFactor: 4,184 x 10³ W
+ Symbol: kcalth/s
+ CommonCode: K55
+ Description: ''
+ conversion:
+ factor: 4184.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilomole per hour
+ ConversionFactor: 2,777 78 x 10⁻¹ mol/s
+ Symbol: kmol/h
+ CommonCode: K58
+ Description: ''
+ conversion:
+ factor: 0.277778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilomole per cubic metre kelvin
+ ConversionFactor: 10³ (mol/m³)/K
+ Symbol: "(kmol/m³)/K"
+ CommonCode: K59
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - L28
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilolitre
+ ConversionFactor: m³
+ Symbol: kl
+ CommonCode: K6
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilomole per cubic metre bar
+ ConversionFactor: 10⁻² (mol/m³)/Pa
+ Symbol: "(kmol/m³)/bar"
+ CommonCode: K60
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilomole per minute
+ ConversionFactor: 16,666 7 mol/s
+ Symbol: kmol/min
+ CommonCode: K61
+ Description: ''
+ conversion:
+ factor: 16.6667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: litre per litre
+ ConversionFactor: '1'
+ Symbol: l/l
+ CommonCode: K62
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal litre
+ ConversionFactor: 10³ m⁻³
+ Symbol: 1/l
+ CommonCode: K63
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C86
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per degree Fahrenheit
+ ConversionFactor: 0,816 466 3 kg/K
+ Symbol: lb/°F
+ CommonCode: K64
+ Description: ''
+ conversion:
+ factor: 0.8164663
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) square foot
+ ConversionFactor: 4,214 011 x 10⁻² kg x m²
+ Symbol: lb·ft²
+ CommonCode: K65
+ Description: ''
+ conversion:
+ factor: 0.04214011
+ base_units:
+ - B32
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per day
+ ConversionFactor: 5,249 912 x 10⁻⁶ kg/s
+ Symbol: lb/d
+ CommonCode: K66
+ Description: ''
+ conversion:
+ factor: 5.249912e-06
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per foot hour
+ ConversionFactor: 4,133 789 x 10⁻⁴ Pa x s
+ Symbol: lb/(ft·h)
+ CommonCode: K67
+ Description: ''
+ conversion:
+ factor: 0.0004133789
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per foot second
+ ConversionFactor: 1,488 164 Pa x s
+ Symbol: lb/(ft·s)
+ CommonCode: K68
+ Description: ''
+ conversion:
+ factor: 1.488164
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per cubic foot degree Fahrenheit
+ ConversionFactor: 28,833 23 (kg/m³)/K
+ Symbol: "(lb/ft³)/°F"
+ CommonCode: K69
+ Description: ''
+ conversion:
+ factor: 28.83323
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per cubic foot psi
+ ConversionFactor: 2,323 282 x 10⁻³
+ Symbol: "(lb/ft³)/psi"
+ CommonCode: K70
+ Description: ''
+ conversion:
+ factor: 0.002323282
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per gallon (UK)
+ ConversionFactor: 99,776 37 kg/m³
+ Symbol: lb/gal (UK)
+ CommonCode: K71
+ Description: ''
+ conversion:
+ factor: 99.77637
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per hour degree Fahrenheit
+ ConversionFactor: 2,267 962 x 10⁻⁴ (kg/s)/K
+ Symbol: "(lb/h)/°F"
+ CommonCode: K73
+ Description: ''
+ conversion:
+ factor: 0.0002267962
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per hour psi
+ ConversionFactor: 1,827 445 x 10⁻⁸ (kg/s)/Pa
+ Symbol: "(lb/h)/psi"
+ CommonCode: K74
+ Description: ''
+ conversion:
+ factor: 1.827445e-08
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per cubic inch degree Fahrenheit
+ ConversionFactor: 4,982 384 x 10⁴ (kg/m³)/K
+ Symbol: "(lb/in³)/°F"
+ CommonCode: K75
+ Description: ''
+ conversion:
+ factor: 49823.84
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per cubic inch psi
+ ConversionFactor: 4,014 632 (kg/m³)/Pa
+ Symbol: "(lb/in³)/psi"
+ CommonCode: K76
+ Description: ''
+ conversion:
+ factor: 4.014632
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per psi
+ ConversionFactor: 6,578 802 x 10⁻⁵ kg/Pa
+ Symbol: lb/psi
+ CommonCode: K77
+ Description: ''
+ conversion:
+ factor: 6.578802e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per minute
+ ConversionFactor: 7,559 873 x 10⁻³ kg/s
+ Symbol: lb/min
+ CommonCode: K78
+ Description: ''
+ conversion:
+ factor: 0.007559873
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per minute degree Fahrenheit
+ ConversionFactor: 1,360 777 x 10⁻² (kg/s)/K
+ Symbol: lb/(min·°F)
+ CommonCode: K79
+ Description: ''
+ conversion:
+ factor: 0.01360777
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per minute psi
+ ConversionFactor: 1,096 467 x 10⁻⁶ (kg/s)/Pa
+ Symbol: "(lb/min)/psi"
+ CommonCode: K80
+ Description: ''
+ conversion:
+ factor: 1.096467e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per second
+ ConversionFactor: 0,453 592 4 kg/s
+ Symbol: lb/s
+ CommonCode: K81
+ Description: ''
+ conversion:
+ factor: 0.4535924
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per second degree Fahrenheit
+ ConversionFactor: 0,816 466 3 (kg/s)/K
+ Symbol: "(lb/s)/°F"
+ CommonCode: K82
+ Description: ''
+ conversion:
+ factor: 0.8164663
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound (avoirdupois) per second psi
+ ConversionFactor: 6,578 802 x 10⁻⁵ (kg/s)/Pa
+ Symbol: "(lb/s)/psi"
+ CommonCode: K83
+ Description: ''
+ conversion:
+ factor: 6.578802e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per cubic yard
+ ConversionFactor: 0,593 276 4 kg/m³
+ Symbol: lb/yd³
+ CommonCode: K84
+ Description: ''
+ conversion:
+ factor: 0.5932764
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force per square foot
+ ConversionFactor: 47,880 26 Pa
+ Symbol: lbf/ft²
+ CommonCode: K85
+ Description: ''
+ conversion:
+ factor: 47.88026
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force per square inch degree Fahrenheit
+ ConversionFactor: 1,241 056 x 10⁴ Pa/K
+ Symbol: psi/°F
+ CommonCode: K86
+ Description: ''
+ conversion:
+ factor: 12410.56
+ base_units:
+ - C64
+- Status: ''
+ LevelAndCategory: '2'
+ Name: psi cubic inch per second
+ ConversionFactor: 0,112 985 Pa x m³/s
+ Symbol: psi·in³/s
+ CommonCode: K87
+ Description: ''
+ conversion:
+ factor: 0.112985
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: psi litre per second
+ ConversionFactor: 6,894 757 Pa x m³/s
+ Symbol: psi·l/s
+ CommonCode: K88
+ Description: ''
+ conversion:
+ factor: 6.894757
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: psi cubic metre per second
+ ConversionFactor: 6,894 757 x 10³ Pa x m³/s
+ Symbol: psi·m³/s
+ CommonCode: K89
+ Description: ''
+ conversion:
+ factor: 6894.757
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: psi cubic yard per second
+ ConversionFactor: 5,271 420 x 10³ Pa x m³/s
+ Symbol: psi·yd³/s
+ CommonCode: K90
+ Description: ''
+ conversion:
+ factor: 5271.42
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force second per square foot
+ ConversionFactor: 47,880 26 Pa x s
+ Symbol: lbf·s/ft²
+ CommonCode: K91
+ Description: ''
+ conversion:
+ factor: 47.88026
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force second per square inch
+ ConversionFactor: 6,894 757 x 10³ Pa x s
+ Symbol: lbf·s/in²
+ CommonCode: K92
+ Description: ''
+ conversion:
+ factor: 6894.757
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal psi
+ ConversionFactor: 1,450 377 x 10⁻⁴ Pa⁻¹
+ Symbol: 1/psi
+ CommonCode: K93
+ Description: ''
+ conversion:
+ factor: 0.0001450377
+ base_units:
+ - C96
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (UK liquid) per day
+ ConversionFactor: 1,315 420 x 10⁻⁸ m³/s
+ Symbol: qt (UK liq.)/d
+ CommonCode: K94
+ Description: ''
+ conversion:
+ factor: 1.31542e-08
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (UK liquid) per hour
+ ConversionFactor: 3,157 008 x 10⁻⁷ m³/s
+ Symbol: qt (UK liq.)/h
+ CommonCode: K95
+ Description: ''
+ conversion:
+ factor: 3.157008e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (UK liquid) per minute
+ ConversionFactor: 1,894 205 x 10⁻⁵ m³/s
+ Symbol: qt (UK liq.)/min
+ CommonCode: K96
+ Description: ''
+ conversion:
+ factor: 1.894205e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (UK liquid) per second
+ ConversionFactor: 1,136 523 x 10⁻³ m³/s
+ Symbol: qt (UK liq.)/s
+ CommonCode: K97
+ Description: ''
+ conversion:
+ factor: 0.001136523
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (US liquid) per day
+ ConversionFactor: 1,095 316 x 10⁻⁸ m³/s
+ Symbol: qt (US liq.)/d
+ CommonCode: K98
+ Description: ''
+ conversion:
+ factor: 1.095316e-08
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (US liquid) per hour
+ ConversionFactor: 2,628 758 x 10⁻⁷ m³/s
+ Symbol: qt (US liq.)/h
+ CommonCode: K99
+ Description: ''
+ conversion:
+ factor: 2.628758e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: cake
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KA
+ Description: 'A unit of count defining the number of cakes (cake: object shaped
+ into a flat, compact mass).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: katal
+ ConversionFactor: s⁻¹ x mol
+ Symbol: kat
+ CommonCode: KAT
+ Description: A unit of catalytic activity defining the catalytic activity of enzymes
+ and other catalysts.
+ conversion:
+ factor: 1.0
+ base_units:
+ - KAT
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: kilocharacter
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KB
+ Description: A unit of information equal to 10³ (1000) characters.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilobar
+ ConversionFactor: 10⁸ Pa
+ Symbol: kbar
+ CommonCode: KBA
+ Description: ''
+ conversion:
+ factor: 100000000.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of choline chloride
+ ConversionFactor: ''
+ Symbol: kg C₅ H₁₄ClNO
+ CommonCode: KCC
+ Description: A unit of mass equal to one thousand grams of choline chloride.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: kilogram decimal
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KD
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram drained net weight
+ ConversionFactor: ''
+ Symbol: kg/net eda
+ CommonCode: KDW
+ Description: A unit of mass defining the net number of kilograms of a product, disregarding
+ the liquid content of the product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kelvin
+ ConversionFactor: K
+ Symbol: K
+ CommonCode: KEL
+ Description: 'Refer ISO 80000-5 (Quantities and units — Part 5: Thermodynamics)'
+ conversion:
+ factor: 1.0
+ base_units:
+ - CEL
+ - KEL
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: kilopacket
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KF
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: keg
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KG
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram
+ ConversionFactor: kg
+ Symbol: kg
+ CommonCode: KGM
+ Description: A unit of mass equal to one thousand grams.
+ conversion:
+ factor: 1.0
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram per second
+ ConversionFactor: kg/s
+ Symbol: kg/s
+ CommonCode: KGS
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of hydrogen peroxide
+ ConversionFactor: ''
+ Symbol: kg H₂O₂
+ CommonCode: KHY
+ Description: A unit of mass equal to one thousand grams of hydrogen peroxide.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilohertz
+ ConversionFactor: 10³ Hz
+ Symbol: kHz
+ CommonCode: KHZ
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - HTZ
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram per millimetre width
+ ConversionFactor: 10³ kg/m
+ Symbol: ''
+ CommonCode: KI
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram, including container
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KIC
+ Description: A unit of mass defining the number of kilograms of a product, including
+ its container.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram, including inner packaging
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KIP
+ Description: A unit of mass defining the number of kilograms of a product, including
+ its inner packaging materials.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kilosegment
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KJ
+ Description: A unit of information equal to 10³ (1000) segments.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilojoule
+ ConversionFactor: 10³ J
+ Symbol: kJ
+ CommonCode: KJO
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram per metre
+ ConversionFactor: kg/m
+ Symbol: kg/m
+ CommonCode: KL
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: lactic dry material percentage
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KLK
+ Description: A unit of proportion defining the percentage of dry lactic material
+ in a product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilolux
+ ConversionFactor: 10³ cd x sr / m²
+ Symbol: klx
+ CommonCode: KLX
+ Description: A unit of illuminance equal to one thousand lux.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - LUX
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of methylamine
+ ConversionFactor: ''
+ Symbol: kg met.am.
+ CommonCode: KMA
+ Description: A unit of mass equal to one thousand grams of methylamine.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilometre per hour
+ ConversionFactor: 0,277 778 m/s
+ Symbol: km/h
+ CommonCode: KMH
+ Description: ''
+ conversion:
+ factor: 0.277778
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: 1S
+ Name: square kilometre
+ ConversionFactor: 10⁶ m²
+ Symbol: km²
+ CommonCode: KMK
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '1'
+ Name: kilogram per cubic metre
+ ConversionFactor: kg/m³
+ Symbol: kg/m³
+ CommonCode: KMQ
+ Description: A unit of weight expressed in kilograms of a substance that fills a
+ volume of one cubic metre.
+ conversion:
+ factor: 1.0
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilometre
+ ConversionFactor: 10³ m
+ Symbol: km
+ CommonCode: KMT
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of nitrogen
+ ConversionFactor: ''
+ Symbol: kg N
+ CommonCode: KNI
+ Description: A unit of mass equal to one thousand grams of nitrogen.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilonewton per square metre
+ ConversionFactor: 103pascal
+ Symbol: kN/m2
+ CommonCode: KNM
+ Description: Pressure expressed in kN/m2.
+ conversion:
+ factor: 103.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram named substance
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KNS
+ Description: A unit of mass equal to one kilogram of a named substance.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: knot
+ ConversionFactor: 0,514 444 m/s
+ Symbol: kn
+ CommonCode: KNT
+ Description: ''
+ conversion:
+ factor: 0.514444
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: milliequivalence caustic potash per gram of product
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KO
+ Description: A unit of count defining the number of milligrams of potassium hydroxide
+ per gram of product as a measure of the concentration of potassium hydroxide in
+ the product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilopascal
+ ConversionFactor: 10³ Pa
+ Symbol: kPa
+ CommonCode: KPA
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of potassium hydroxide (caustic potash)
+ ConversionFactor: ''
+ Symbol: kg KOH
+ CommonCode: KPH
+ Description: A unit of mass equal to one thousand grams of potassium hydroxide (caustic
+ potash).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of potassium oxide
+ ConversionFactor: ''
+ Symbol: kg K₂O
+ CommonCode: KPO
+ Description: A unit of mass equal to one thousand grams of potassium oxide.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of phosphorus pentoxide (phosphoric anhydride)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KPP
+ Description: A unit of mass equal to one thousand grams of phosphorus pentoxide
+ phosphoric anhydride.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kiloroentgen
+ ConversionFactor: 2,58 x 10⁻¹ C/kg
+ Symbol: kR
+ CommonCode: KR
+ Description: ''
+ conversion:
+ factor: 0.258
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand pound per square inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KS
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of substance 90 % dry
+ ConversionFactor: ''
+ Symbol: kg 90 % sdt
+ CommonCode: KSD
+ Description: A unit of mass equal to one thousand grams of a named substance that
+ is 90% dry.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of sodium hydroxide (caustic soda)
+ ConversionFactor: ''
+ Symbol: kg NaOH
+ CommonCode: KSH
+ Description: A unit of mass equal to one thousand grams of sodium hydroxide (caustic
+ soda).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: kit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KT
+ Description: 'A unit of count defining the number of kits (kit: tub, barrel or pail).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: 1S
+ Name: kilometre
+ ConversionFactor: 10³ m
+ Symbol: km
+ CommonCode: KTM
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilotonne
+ ConversionFactor: 10⁶ kg
+ Symbol: kt
+ CommonCode: KTN
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of uranium
+ ConversionFactor: ''
+ Symbol: kg U
+ CommonCode: KUR
+ Description: A unit of mass equal to one thousand grams of uranium.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilovolt - ampere
+ ConversionFactor: 10³ V x A
+ Symbol: kV·A
+ CommonCode: KVA
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D44
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilovar
+ ConversionFactor: 10³ V x A
+ Symbol: kvar
+ CommonCode: KVR
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D44
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilovolt
+ ConversionFactor: 10³ V
+ Symbol: kV
+ CommonCode: KVT
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per millimetre
+ ConversionFactor: 10³ kg/m
+ Symbol: kg/mm
+ CommonCode: KW
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilowatt hour
+ ConversionFactor: 3,6 x 10⁶ J
+ Symbol: kW·h
+ CommonCode: KWH
+ Description: ''
+ conversion:
+ factor: 3600000.0
+ base_units:
+ - JOU
+- Status: "+"
+ LevelAndCategory: '2'
+ Name: kilowatt year
+ ConversionFactor: ''
+ Symbol: kW/year
+ CommonCode: KWY
+ Description: killowatt year
+ conversion:
+ factor: 1.0
+- Status: "¦"
+ LevelAndCategory: '2'
+ Name: Kilowatt hour per normalized cubic metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KWN
+ Description: Kilowatt hour per normalized cubic metre (temperature 0°C and pressure
+ 1013.25 millibars ).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram of tungsten trioxide
+ ConversionFactor: ''
+ Symbol: kg WO₃
+ CommonCode: KWO
+ Description: A unit of mass equal to one thousand grams of tungsten trioxide.
+ conversion:
+ factor: 1.0
+- Status: "¦"
+ LevelAndCategory: '2'
+ Name: Kilowatt hour per standard cubic metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: KWS
+ Description: Kilowatt hour per standard cubic metre (temperature 15°C and pressure
+ 1013.25 millibars).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilowatt
+ ConversionFactor: 10³ W
+ Symbol: kW
+ CommonCode: KWT
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millilitre per kilogram
+ ConversionFactor: 10⁻⁶ m³/kg
+ Symbol: ml/kg
+ CommonCode: KX
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - A39
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (US liquid) per minute
+ ConversionFactor: 1,577 255 x 10⁻⁵ m³/s
+ Symbol: qt (US liq.)/min
+ CommonCode: L10
+ Description: ''
+ conversion:
+ factor: 1.577255e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (US liquid) per second
+ ConversionFactor: 9,463 529 x 10⁻⁴ m³/s
+ Symbol: qt (US liq.)/s
+ CommonCode: L11
+ Description: ''
+ conversion:
+ factor: 0.0009463529
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: metre per second kelvin
+ ConversionFactor: "(m/s)/K"
+ Symbol: "(m/s)/K"
+ CommonCode: L12
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - L12
+- Status: ''
+ LevelAndCategory: '2'
+ Name: metre per second bar
+ ConversionFactor: 10⁻⁵ (m/s)/Pa
+ Symbol: "(m/s)/bar"
+ CommonCode: L13
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square metre hour degree Celsius per kilocalorie (international table)
+ ConversionFactor: 0,859 845 2 m² x s x K/J
+ Symbol: m²·h·°C/kcal
+ CommonCode: L14
+ Description: ''
+ conversion:
+ factor: 0.8598452
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: millipascal second per kelvin
+ ConversionFactor: 10⁻³ Pa x s/K
+ Symbol: mPa·s/K
+ CommonCode: L15
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: millipascal second per bar
+ ConversionFactor: 10⁻⁸ s
+ Symbol: mPa·s/bar
+ CommonCode: L16
+ Description: ''
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '2'
+ Name: milligram per cubic metre kelvin
+ ConversionFactor: 10⁻⁶ (kg/m³)/K
+ Symbol: "(mg/m³)/K"
+ CommonCode: L17
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: milligram per cubic metre bar
+ ConversionFactor: 10⁻¹¹ (kg/m³)/Pa
+ Symbol: "(mg/m³)/bar"
+ CommonCode: L18
+ Description: ''
+ conversion:
+ factor: 1.0e-11
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millilitre per litre
+ ConversionFactor: 10⁻³
+ Symbol: ml/l
+ CommonCode: L19
+ Description: ''
+ conversion:
+ factor: 0.001
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per minute
+ ConversionFactor: 1,666 67 x 10⁻⁵ m³/s
+ Symbol: l/min
+ CommonCode: L2
+ Description: ''
+ conversion:
+ factor: 1.66667e-05
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: 1S
+ Name: reciprocal cubic millimetre
+ ConversionFactor: 10⁹ m⁻³
+ Symbol: 1/mm³
+ CommonCode: L20
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units:
+ - C86
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic millimetre per cubic metre
+ ConversionFactor: 10⁹
+ Symbol: mm³/m³
+ CommonCode: L21
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: mole per hour
+ ConversionFactor: 2,777 78 x 10⁻⁴ mol/s
+ Symbol: mol/h
+ CommonCode: L23
+ Description: ''
+ conversion:
+ factor: 0.000277778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mole per kilogram kelvin
+ ConversionFactor: "(mol/kg)/K"
+ Symbol: "(mol/kg)/K"
+ CommonCode: L24
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - L24
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mole per kilogram bar
+ ConversionFactor: 10⁻⁵ (mol/kg)/Pa
+ Symbol: "(mol/kg)/bar"
+ CommonCode: L25
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mole per litre kelvin
+ ConversionFactor: 10³ (mol/m³)/K
+ Symbol: "(mol/l)/K"
+ CommonCode: L26
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - L28
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mole per litre bar
+ ConversionFactor: 10⁻² (mol/m³)/Pa
+ Symbol: "(mol/l)/bar"
+ CommonCode: L27
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mole per cubic metre kelvin
+ ConversionFactor: "(mol/m³)/K"
+ Symbol: "(mol/m³)/K"
+ CommonCode: L28
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - L28
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mole per cubic metre bar
+ ConversionFactor: 10⁻⁵ (mol/m³)/Pa
+ Symbol: "(mol/m³)/bar"
+ CommonCode: L29
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: mole per minute
+ ConversionFactor: 1,666 67 x 10⁻² mol/s
+ Symbol: mol/min
+ CommonCode: L30
+ Description: ''
+ conversion:
+ factor: 0.0166667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: milliroentgen aequivalent men
+ ConversionFactor: 10⁻⁵ Sv
+ Symbol: mrem
+ CommonCode: L31
+ Description: ''
+ conversion:
+ factor: 1.0e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanogram per kilogram
+ ConversionFactor: 10⁻¹²
+ Symbol: ng/kg
+ CommonCode: L32
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per day
+ ConversionFactor: 3,281 194 x 10⁻⁷kg/s
+ Symbol: oz/d
+ CommonCode: L33
+ Description: ''
+ conversion:
+ factor: 3.281194e-07
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per hour
+ ConversionFactor: 7,874 867 x 10⁻⁶ kg/s
+ Symbol: oz/h
+ CommonCode: L34
+ Description: ''
+ conversion:
+ factor: 7.874867e-06
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per minute
+ ConversionFactor: 4,724 92 x 10⁻⁴ kg/s
+ Symbol: oz/min
+ CommonCode: L35
+ Description: ''
+ conversion:
+ factor: 0.000472492
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per second
+ ConversionFactor: 2,834 952 x 10⁻² kg/s
+ Symbol: oz/s
+ CommonCode: L36
+ Description: ''
+ conversion:
+ factor: 0.02834952
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per gallon (UK)
+ ConversionFactor: 6,236 023 kg/m³
+ Symbol: oz/gal (UK)
+ CommonCode: L37
+ Description: ''
+ conversion:
+ factor: 6.236023
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per gallon (US)
+ ConversionFactor: 7,489 152 kg/m³
+ Symbol: oz/gal (US)
+ CommonCode: L38
+ Description: ''
+ conversion:
+ factor: 7.489152
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per cubic inch
+ ConversionFactor: 1,729 994 x 10³ kg/m³
+ Symbol: oz/in³
+ CommonCode: L39
+ Description: ''
+ conversion:
+ factor: 1729.994
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois)-force
+ ConversionFactor: 0,278 013 9 N
+ Symbol: ozf
+ CommonCode: L40
+ Description: ''
+ conversion:
+ factor: 0.2780139
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois)-force inch
+ ConversionFactor: 7,061 552 x 10⁻³ N x m
+ Symbol: ozf·in
+ CommonCode: L41
+ Description: ''
+ conversion:
+ factor: 0.007061552
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: picosiemens per metre
+ ConversionFactor: 10⁻¹² S/m
+ Symbol: pS/m
+ CommonCode: L42
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - D10
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (UK)
+ ConversionFactor: 9,092 181 x 10⁻³ m³
+ Symbol: pk (UK)
+ CommonCode: L43
+ Description: ''
+ conversion:
+ factor: 0.009092181
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (UK) per day
+ ConversionFactor: 1,052 336 x 10⁻⁷ m³/s
+ Symbol: pk (UK)/d
+ CommonCode: L44
+ Description: ''
+ conversion:
+ factor: 1.052336e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (UK) per hour
+ ConversionFactor: 2,525 606 x 10⁻⁶ m³/s
+ Symbol: pk (UK)/h
+ CommonCode: L45
+ Description: ''
+ conversion:
+ factor: 2.525606e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (UK) per minute
+ ConversionFactor: 1,515 363 5 x 10⁻⁴ m³/s
+ Symbol: pk (UK)/min
+ CommonCode: L46
+ Description: ''
+ conversion:
+ factor: 0.00015153635
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (UK) per second
+ ConversionFactor: 9,092 181 x 10⁻³ m³/s
+ Symbol: pk (UK)/s
+ CommonCode: L47
+ Description: ''
+ conversion:
+ factor: 0.009092181
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (US dry) per day
+ ConversionFactor: 1,019 649 x 10⁻⁷ m³/s
+ Symbol: pk (US dry)/d
+ CommonCode: L48
+ Description: ''
+ conversion:
+ factor: 1.019649e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (US dry) per hour
+ ConversionFactor: 2,447 158 x 10⁻⁶ m³/s
+ Symbol: pk (US dry)/h
+ CommonCode: L49
+ Description: ''
+ conversion:
+ factor: 2.447158e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (US dry) per minute
+ ConversionFactor: 1,468 295 x 10⁻⁴ m³/s
+ Symbol: pk (US dry)/min
+ CommonCode: L50
+ Description: ''
+ conversion:
+ factor: 0.0001468295
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: peck (US dry) per second
+ ConversionFactor: 8,809 768 x 10⁻³ m³/s
+ Symbol: pk (US dry)/s
+ CommonCode: L51
+ Description: ''
+ conversion:
+ factor: 0.008809768
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: psi per psi
+ ConversionFactor: '1.0'
+ Symbol: psi/psi
+ CommonCode: L52
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - E98
+ - F02
+ - H60
+ - L52
+ - M91
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (UK) per day
+ ConversionFactor: 6,577 098 x 10⁻⁹ m³/s
+ Symbol: pt (UK)/d
+ CommonCode: L53
+ Description: ''
+ conversion:
+ factor: 6.577098e-09
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (UK) per hour
+ ConversionFactor: 1,578 504 x 10⁻⁷ m³/s
+ Symbol: pt (UK)/h
+ CommonCode: L54
+ Description: ''
+ conversion:
+ factor: 1.578504e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (UK) per minute
+ ConversionFactor: 9,471 022 x 10⁻⁶ m³/s
+ Symbol: pt (UK)/min
+ CommonCode: L55
+ Description: ''
+ conversion:
+ factor: 9.471022e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (UK) per second
+ ConversionFactor: 5,682 613 x 10⁻⁴ m³/s
+ Symbol: pt (UK)/s
+ CommonCode: L56
+ Description: ''
+ conversion:
+ factor: 0.0005682613
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (US liquid) per day
+ ConversionFactor: 5,476 580 x 10⁻⁹ m³/s
+ Symbol: pt (US liq.)/d
+ CommonCode: L57
+ Description: ''
+ conversion:
+ factor: 5.47658e-09
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (US liquid) per hour
+ ConversionFactor: 1,314 379 x 10⁻⁷ m³/s
+ Symbol: pt (US liq.)/h
+ CommonCode: L58
+ Description: ''
+ conversion:
+ factor: 1.314379e-07
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (US liquid) per minute
+ ConversionFactor: 7,886 275 x 10⁻⁶ m³/s
+ Symbol: pt (US liq.)/min
+ CommonCode: L59
+ Description: ''
+ conversion:
+ factor: 7.886275e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (US liquid) per second
+ ConversionFactor: 4,731 765 x 10⁻⁴ m³/s
+ Symbol: pt (US liq.)/s
+ CommonCode: L60
+ Description: ''
+ conversion:
+ factor: 0.0004731765
+ base_units:
+ - MQS
+- Status: X
+ LevelAndCategory: '2'
+ Name: pint (US dry)
+ ConversionFactor: 5,506 105 x 10⁻⁴ m³
+ Symbol: pt (US dry)
+ CommonCode: L61
+ Description: Use dry pint (common code PTD)
+ conversion:
+ factor: 0.0005506105
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '2'
+ Name: quart (US dry)
+ ConversionFactor: 1,101 221 x 10⁻³ m³
+ Symbol: qt (US dry)
+ CommonCode: L62
+ Description: Use dry quart (US) (common code QTD)
+ conversion:
+ factor: 0.001101221
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: slug per day
+ ConversionFactor: 1,689 109 x 10⁻⁴ kg/s
+ Symbol: slug/d
+ CommonCode: L63
+ Description: ''
+ conversion:
+ factor: 0.0001689109
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: slug per foot second
+ ConversionFactor: 47,880 26 Pa x s
+ Symbol: slug/(ft·s)
+ CommonCode: L64
+ Description: ''
+ conversion:
+ factor: 47.88026
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2'
+ Name: slug per cubic foot
+ ConversionFactor: 5,153 788 x 10² kg/m³
+ Symbol: slug/ft³
+ CommonCode: L65
+ Description: ''
+ conversion:
+ factor: 515.3788
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: slug per hour
+ ConversionFactor: 4,053 861 x 10⁻³ kg/s
+ Symbol: slug/h
+ CommonCode: L66
+ Description: ''
+ conversion:
+ factor: 0.004053861
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: slug per minute
+ ConversionFactor: 0,243 231 7 kg/s
+ Symbol: slug/min
+ CommonCode: L67
+ Description: ''
+ conversion:
+ factor: 0.2432317
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: slug per second
+ ConversionFactor: 14,593 90 kg/s
+ Symbol: slug/s
+ CommonCode: L68
+ Description: ''
+ conversion:
+ factor: 14.5939
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per kelvin
+ ConversionFactor: 10³ kg/K
+ Symbol: t/K
+ CommonCode: L69
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per bar
+ ConversionFactor: 10⁻² kg/Pa
+ Symbol: t/bar
+ CommonCode: L70
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per day
+ ConversionFactor: 1,157 41 x 10⁻² kg/s
+ Symbol: t/d
+ CommonCode: L71
+ Description: ''
+ conversion:
+ factor: 0.0115741
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per day kelvin
+ ConversionFactor: 1,157 41 x 10⁻² (kg/s)/K
+ Symbol: "(t/d)/K"
+ CommonCode: L72
+ Description: ''
+ conversion:
+ factor: 0.0115741
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per day bar
+ ConversionFactor: 1,157 41 x 10⁻⁷ (kg/s)/Pa
+ Symbol: "(t/d)/bar"
+ CommonCode: L73
+ Description: ''
+ conversion:
+ factor: 1.15741e-07
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per hour kelvin
+ ConversionFactor: 2,777 78 x 10⁻¹ (kg/s)/K
+ Symbol: "(t/h)/K"
+ CommonCode: L74
+ Description: ''
+ conversion:
+ factor: 0.277778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per hour bar
+ ConversionFactor: 2,777 78 x 10⁻⁶ (kg/s)/Pa
+ Symbol: "(t/h)/bar"
+ CommonCode: L75
+ Description: ''
+ conversion:
+ factor: 2.77778e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per cubic metre kelvin
+ ConversionFactor: 10³ (kg/m³)/K
+ Symbol: "(t/m³)/K"
+ CommonCode: L76
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per cubic metre bar
+ ConversionFactor: 10⁻² (kg/m³)/Pa
+ Symbol: "(t/m³)/bar"
+ CommonCode: L77
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per minute
+ ConversionFactor: 16,666 7 kg/s
+ Symbol: t/min
+ CommonCode: L78
+ Description: ''
+ conversion:
+ factor: 16.6667
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per minute kelvin
+ ConversionFactor: 16,666 7 (kg/s)/K
+ Symbol: "(t/min)/K"
+ CommonCode: L79
+ Description: ''
+ conversion:
+ factor: 16.6667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per minute bar
+ ConversionFactor: 1,666 67 x 10⁻⁴ (kg/s)/Pa
+ Symbol: "(t/min)/bar"
+ CommonCode: L80
+ Description: ''
+ conversion:
+ factor: 0.000166667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per second
+ ConversionFactor: 10³ kg/s
+ Symbol: t/s
+ CommonCode: L81
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per second kelvin
+ ConversionFactor: 10³ (kg/s)/K
+ Symbol: "(t/s)/K"
+ CommonCode: L82
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per second bar
+ ConversionFactor: 10⁻² (kg/s)/Pa
+ Symbol: "(t/s)/bar"
+ CommonCode: L83
+ Description: ''
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton (UK shipping)
+ ConversionFactor: 1,189 3 m³
+ Symbol: British shipping ton
+ CommonCode: L84
+ Description: ''
+ conversion:
+ factor: 1.1893
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton long per day
+ ConversionFactor: 1,175 980 x 10⁻² kg/s
+ Symbol: ton (UK)/d
+ CommonCode: L85
+ Description: ''
+ conversion:
+ factor: 0.0117598
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton (US shipping)
+ ConversionFactor: 1,132 6 m³
+ Symbol: "(US) shipping ton"
+ CommonCode: L86
+ Description: ''
+ conversion:
+ factor: 1.1326
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton short per degree Fahrenheit
+ ConversionFactor: 1,632 932 x 10³ kg/K
+ Symbol: ton (US)/°F
+ CommonCode: L87
+ Description: ''
+ conversion:
+ factor: 1632.932
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton short per day
+ ConversionFactor: 1,049 982 x 10⁻² kg/s
+ Symbol: ton (US)/d
+ CommonCode: L88
+ Description: ''
+ conversion:
+ factor: 0.01049982
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton short per hour degree Fahrenheit
+ ConversionFactor: 0,453 592 2 kg/s x K
+ Symbol: ton (US)/(h·°F)
+ CommonCode: L89
+ Description: ''
+ conversion:
+ factor: 0.4535922
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton short per hour psi
+ ConversionFactor: 3,654 889 x 10⁻⁵ (kg/s)/Pa
+ Symbol: "(ton (US)/h)/psi"
+ CommonCode: L90
+ Description: ''
+ conversion:
+ factor: 3.654889e-05
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton short per psi
+ ConversionFactor: 0,131 576
+ Symbol: ton (US)/psi
+ CommonCode: L91
+ Description: ''
+ conversion:
+ factor: 0.131576
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton (UK long) per cubic yard
+ ConversionFactor: 1,328 939 x 10³ kg/m³
+ Symbol: ton.l/yd³ (UK)
+ CommonCode: L92
+ Description: ''
+ conversion:
+ factor: 1328.939
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton (US short) per cubic yard
+ ConversionFactor: 1,186 553 x 10³ kg/m³
+ Symbol: ton.s/yd³ (US)
+ CommonCode: L93
+ Description: ''
+ conversion:
+ factor: 1186.553
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton-force (US short)
+ ConversionFactor: 8,896 443 x 10³ N
+ Symbol: ton.sh-force
+ CommonCode: L94
+ Description: ''
+ conversion:
+ factor: 8896.443
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: common year
+ ConversionFactor: 3,153 6 x 10⁷ s
+ Symbol: y (365 days)
+ CommonCode: L95
+ Description: ''
+ conversion:
+ factor: 31536000.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '2'
+ Name: sidereal year
+ ConversionFactor: 3,155 815 x 10⁷ s
+ Symbol: y (sidereal)
+ CommonCode: L96
+ Description: ''
+ conversion:
+ factor: 31558150.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '2'
+ Name: yard per degree Fahrenheit
+ ConversionFactor: 1,645 92 m/K
+ Symbol: yd/°F
+ CommonCode: L98
+ Description: ''
+ conversion:
+ factor: 1.64592
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: yard per psi
+ ConversionFactor: 1,326 225 x 10⁻⁴ m/Pa
+ Symbol: yd/psi
+ CommonCode: L99
+ Description: ''
+ conversion:
+ factor: 0.0001326225
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per cubic inch
+ ConversionFactor: 2,767 990 x 10⁴ kg/m³
+ Symbol: lb/in³
+ CommonCode: LA
+ Description: ''
+ conversion:
+ factor: 27679.9
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: lactose excess percentage
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LAC
+ Description: A unit of proportion defining the percentage of lactose in a product
+ that exceeds a defined percentage level.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound
+ ConversionFactor: 0,453 592 37 kg
+ Symbol: lb
+ CommonCode: LBR
+ Description: ''
+ conversion:
+ factor: 0.45359237
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: troy pound (US)
+ ConversionFactor: 373,241 7 g
+ Symbol: ''
+ CommonCode: LBT
+ Description: ''
+ conversion:
+ factor: 373.2417
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: linear centimetre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LC
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: litre per day
+ ConversionFactor: 1,157 41 x 10⁻⁸ m³/s
+ Symbol: l/d
+ CommonCode: LD
+ Description: ''
+ conversion:
+ factor: 1.15741e-08
+ base_units:
+ - MQS
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: lite
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: leaf
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LEF
+ Description: A unit of count defining the number of leaves.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: linear foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LF
+ Description: A unit of count defining the number of feet (12-inch) in length of
+ a uniform width object.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: labour hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LH
+ Description: A unit of time defining the number of labour hours.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: linear inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LI
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: large spray
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LJ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: link
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LK
+ Description: A unit of distance equal to 0.01 chain.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: linear metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LM
+ Description: A unit of count defining the number of metres in length of a uniform
+ width object.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: length
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LN
+ Description: A unit of distance defining the linear extent of an item measured from
+ end to end.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: lot [unit of procurement]
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LO
+ Description: 'A unit of count defining the number of lots (lot: a collection of
+ associated items).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: liquid pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LP
+ Description: A unit of mass defining the number of pounds of a liquid substance.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: litre of pure alcohol
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LPA
+ Description: A unit of volume equal to one litre of pure alcohol.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: layer
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LR
+ Description: A unit of count defining the number of layers.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: lump sum
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LS
+ Description: A unit of count defining the number of whole or a complete monetary
+ amounts.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton (UK) or long ton (US)
+ ConversionFactor: 1,016 047 x 10³ kg
+ Symbol: ton (UK)
+ CommonCode: LTN
+ Description: 'Synonym: gross ton (2240 lb)'
+ conversion:
+ factor: 1016.047
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '1'
+ Name: litre
+ ConversionFactor: 10⁻³ m³
+ Symbol: l
+ CommonCode: LTR
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: metric ton, lubricating oil
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LUB
+ Description: A unit of mass defining the number of metric tons of lubricating oil.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: lumen
+ ConversionFactor: cd x sr
+ Symbol: lm
+ CommonCode: LUM
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - LUM
+- Status: ''
+ LevelAndCategory: '1'
+ Name: lux
+ ConversionFactor: cd x sr / m²
+ Symbol: lx
+ CommonCode: LUX
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - LUX
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: linear yard per pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LX
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: linear yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: LY
+ Description: A unit of count defining the number of 36-inch units in length of a
+ uniform width object.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.6'
+ Name: magnetic tape
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: M0
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligram per litre
+ ConversionFactor: 10⁻³ kg/m³
+ Symbol: mg/l
+ CommonCode: M1
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - GL
+ - KMQ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal cubic yard
+ ConversionFactor: 1,307 951 m⁻³
+ Symbol: 1/yd³
+ CommonCode: M10
+ Description: ''
+ conversion:
+ factor: 1.307951
+ base_units:
+ - C86
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic yard per degree Fahrenheit
+ ConversionFactor: 1,376 199 m³/K
+ Symbol: yd³/°F
+ CommonCode: M11
+ Description: ''
+ conversion:
+ factor: 1.376199
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic yard per day
+ ConversionFactor: 8,849 015 x 10⁻⁶ m³/s
+ Symbol: yd³/d
+ CommonCode: M12
+ Description: ''
+ conversion:
+ factor: 8.849015e-06
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic yard per hour
+ ConversionFactor: 2,123 764 x 10⁻⁴ m³/s
+ Symbol: yd³/h
+ CommonCode: M13
+ Description: ''
+ conversion:
+ factor: 0.0002123764
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic yard per psi
+ ConversionFactor: 1,108 893 x 10⁻⁴ m³/Pa
+ Symbol: yd³/psi
+ CommonCode: M14
+ Description: ''
+ conversion:
+ factor: 0.0001108893
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic yard per minute
+ ConversionFactor: 1,274 258 x 10⁻² m³/s
+ Symbol: yd³/min
+ CommonCode: M15
+ Description: ''
+ conversion:
+ factor: 0.01274258
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic yard per second
+ ConversionFactor: 0,764 554 9 m³/s
+ Symbol: yd³/s
+ CommonCode: M16
+ Description: ''
+ conversion:
+ factor: 0.7645549
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilohertz metre
+ ConversionFactor: 10³ Hz x m
+ Symbol: kHz·m
+ CommonCode: M17
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - H34
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gigahertz metre
+ ConversionFactor: 10⁹ Hz x m
+ Symbol: GHz·m
+ CommonCode: M18
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units:
+ - H34
+- Status: ''
+ LevelAndCategory: '3'
+ Name: Beaufort
+ ConversionFactor: ''
+ Symbol: Bft
+ CommonCode: M19
+ Description: An empirical measure for describing wind speed based mainly on observed
+ sea conditions. The Beaufort scale indicates the wind speed by numbers that typically
+ range from 0 for calm, to 12 for hurricane.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal megakelvin or megakelvin to the power minus one
+ ConversionFactor: 10⁻⁶ K⁻¹
+ Symbol: 1/MK
+ CommonCode: M20
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - C91
+ - N83
+- Status: ''
+ LevelAndCategory: '2'
+ Name: reciprocal kilovolt - ampere reciprocal hour
+ ConversionFactor: 2,777 778 x 10⁻⁷ (V x A x s)⁻¹
+ Symbol: 1/kVAh
+ CommonCode: M21
+ Description: ''
+ conversion:
+ factor: 2.777778e-07
+ base_units:
+ - M30
+- Status: ''
+ LevelAndCategory: '2'
+ Name: millilitre per square centimetre minute
+ ConversionFactor: 2,777 778 x 10⁻⁶ (m³/s)/m²
+ Symbol: "(ml/min)/cm²"
+ CommonCode: M22
+ Description: ''
+ conversion:
+ factor: 2.777778e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton per centimetre
+ ConversionFactor: 10² N/m
+ Symbol: N/cm
+ CommonCode: M23
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - 4P
+- Status: ''
+ LevelAndCategory: 1M
+ Name: ohm kilometre
+ ConversionFactor: 10³ Ω x m
+ Symbol: Ω·km
+ CommonCode: M24
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C61
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent per degree Celsius
+ ConversionFactor: 10⁻² °C⁻¹
+ Symbol: "%/°C"
+ CommonCode: M25
+ Description: A unit of proportion, equal to 0.01, in relation to a temperature of
+ one degree.
+ conversion:
+ factor: 0.01
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gigaohm per metre
+ ConversionFactor: 10⁹ Ω/m
+ Symbol: GΩ/m
+ CommonCode: M26
+ Description: ''
+ conversion:
+ factor: 1000000000.0
+ base_units:
+ - H26
+- Status: ''
+ LevelAndCategory: '2'
+ Name: megahertz metre
+ ConversionFactor: 10⁶ Hz x m
+ Symbol: MHz·m
+ CommonCode: M27
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - H34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilogram per kilogram
+ ConversionFactor: '1'
+ Symbol: kg/kg
+ CommonCode: M29
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: reciprocal volt - ampere reciprocal second
+ ConversionFactor: "(V x A x s)⁻¹"
+ Symbol: 1/(V·A·s)
+ CommonCode: M30
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - M30
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilogram per kilometre
+ ConversionFactor: 10⁻³ kg/m
+ Symbol: kg/km
+ CommonCode: M31
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pascal second per litre
+ ConversionFactor: 10³ Pa x s/m³
+ Symbol: Pa·s/l
+ CommonCode: M32
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C66
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millimole per litre
+ ConversionFactor: mol/m³
+ Symbol: mmol/l
+ CommonCode: M33
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C36
+ - M33
+- Status: ''
+ LevelAndCategory: 1S
+ Name: newton metre per square metre
+ ConversionFactor: N x m/m²
+ Symbol: N·m/m²
+ CommonCode: M34
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - M34
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millivolt - ampere
+ ConversionFactor: 10⁻³ V x A
+ Symbol: mV·A
+ CommonCode: M35
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - D44
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: 30-day month
+ ConversionFactor: 2,592 000 x 10⁶ s
+ Symbol: mo (30 days)
+ CommonCode: M36
+ Description: A unit of count defining the number of months expressed in multiples
+ of 30 days, one day equals 24 hours.
+ conversion:
+ factor: 2592000.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: actual/360
+ ConversionFactor: 3,110 400 0 x 10⁷ s
+ Symbol: y (360 days)
+ CommonCode: M37
+ Description: A unit of count defining the number of years expressed in multiples
+ of 360 days, one day equals 24 hours.
+ conversion:
+ factor: 31104000.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilometre per second squared
+ ConversionFactor: 10³ m/s²
+ Symbol: km/s²
+ CommonCode: M38
+ Description: 1000-fold of the SI base unit metre divided by the power of the SI
+ base unit second by exponent 2.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: 1M
+ Name: centimetre per second squared
+ ConversionFactor: 10⁻² m/s²
+ Symbol: cm/s²
+ CommonCode: M39
+ Description: 0,01-fold of the SI base unit metre divided by the power of the SI
+ base unit second by exponent 2.
+ conversion:
+ factor: 0.01
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: monetary value
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: M4
+ Description: A unit of measure expressed as a monetary amount.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: yard per second squared
+ ConversionFactor: 9,144 x 10⁻¹ m/s²
+ Symbol: yd/s²
+ CommonCode: M40
+ Description: Unit of the length according to the Anglo-American and Imperial system
+ of units divided by the power of the SI base unit second by exponent 2.
+ conversion:
+ factor: 0.9144
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millimetre per second squared
+ ConversionFactor: 10⁻³ m/s²
+ Symbol: mm/s²
+ CommonCode: M41
+ Description: 0,001-fold of the SI base unit metre divided by the power of the SI
+ base unit second by exponent 2.
+ conversion:
+ factor: 0.001
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mile (statute mile) per second squared
+ ConversionFactor: 1,609 344 x 10³ m/s²
+ Symbol: mi/s²
+ CommonCode: M42
+ Description: Unit of the length according to the Imperial system of units divided
+ by the power of the SI base unit second by exponent 2.
+ conversion:
+ factor: 1609.344
+ base_units:
+ - MSK
+ - P79
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mil
+ ConversionFactor: 9,817 477 x 10⁻⁴ rad
+ Symbol: mil
+ CommonCode: M43
+ Description: Unit to indicate an angle at military zone, equal to the 6400th part
+ of the full circle of the 360° or 2·p·rad.
+ conversion:
+ factor: 0.0009817477
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: '2'
+ Name: revolution
+ ConversionFactor: 6,283 185 rad
+ Symbol: rev
+ CommonCode: M44
+ Description: Unit to identify an angle of the full circle of 360° or 2·p·rad (Refer
+ ISO/TC12 SI Guide).
+ conversion:
+ factor: 6.283185
+ base_units:
+ - C81
+- Status: ''
+ LevelAndCategory: 1M
+ Name: degree [unit of angle] per second squared
+ ConversionFactor: 1,745 329 x 10⁻² rad / s
+ Symbol: "°/s²"
+ CommonCode: M45
+ Description: 360 part of a full circle divided by the power of the SI base unit
+ second and the exponent 2.
+ conversion:
+ factor: 0.01745329
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: revolution per minute
+ ConversionFactor: 0,104 719 8 rad/s
+ Symbol: r/min
+ CommonCode: M46
+ Description: Unit of the angular velocity.
+ conversion:
+ factor: 0.1047198
+ base_units:
+ - 2A
+- Status: ''
+ LevelAndCategory: '2'
+ Name: circular mil
+ ConversionFactor: 5,067 075 x 10⁻¹⁰ m²
+ Symbol: cmil
+ CommonCode: M47
+ Description: 'Unit of an area, of which the size is given by a diameter of length
+ of 1 mm (0,001 in) based on the formula: area = p·(diameter/2)².'
+ conversion:
+ factor: 5.067075e-10
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square mile (based on U.S. survey foot)
+ ConversionFactor: 2,589 998 x 10⁶ m²
+ Symbol: mi² (US survey)
+ CommonCode: M48
+ Description: Unit of the area, which is mainly common in the agriculture and forestry.
+ conversion:
+ factor: 2589998.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '2'
+ Name: chain (based on U.S. survey foot)
+ ConversionFactor: 2,011684 x 10 m
+ Symbol: 'ch (US survey) '
+ CommonCode: M49
+ Description: Unit of the length according the Anglo-American system of units.
+ conversion:
+ factor: 2.011684
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 2S
+ Name: microcurie
+ ConversionFactor: 3,7 x 10⁴ Bq
+ Symbol: µCi
+ CommonCode: M5
+ Description: ''
+ conversion:
+ factor: 37000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: furlong
+ ConversionFactor: 2,011 68 x 10² m
+ Symbol: fur
+ CommonCode: M50
+ Description: 'Unit commonly used in Great Britain at rural distances: 1 furlong
+ = 40 rods = 10 chains (UK) = 1/8 mile = 1/10 furlong = 220 yards = 660 foot.'
+ conversion:
+ factor: 201.168
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot (U.S. survey)
+ ConversionFactor: 3,048 006 x 10⁻¹ m
+ Symbol: 'ft (US survey) '
+ CommonCode: M51
+ Description: Unit commonly used in the United States for ordnance survey.,
+ conversion:
+ factor: 0.3048006
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mile (based on U.S. survey foot)
+ ConversionFactor: 1,609347 x 10³ m
+ Symbol: 'mi (US survey) '
+ CommonCode: M52
+ Description: Unit commonly used in the United States for ordnance survey.
+ conversion:
+ factor: 1609.347
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1M
+ Name: metre per pascal
+ ConversionFactor: kg⁻¹ x m² x s²
+ Symbol: m/Pa
+ CommonCode: M53
+ Description: SI base unit metre divided by the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M53
+- Status: ''
+ LevelAndCategory: 1S
+ Name: metre per radiant
+ ConversionFactor: m/rad
+ Symbol: m/rad
+ CommonCode: M55
+ Description: Unit of the translation factor for implementation from rotation to
+ linear movement.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M55
+- Status: ''
+ LevelAndCategory: '2'
+ Name: shake
+ ConversionFactor: 10⁻⁸ s
+ Symbol: shake
+ CommonCode: M56
+ Description: Unit for a very short period.
+ conversion:
+ factor: 1.0e-08
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mile per minute
+ ConversionFactor: 26,822 4 m/s
+ Symbol: mi/min
+ CommonCode: M57
+ Description: Unit of velocity from the Imperial system of units.
+ conversion:
+ factor: 26.8224
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mile per second
+ ConversionFactor: 1,609 344 x 10³ m/s
+ Symbol: mi/s
+ CommonCode: M58
+ Description: Unit of the velocity from the Imperial system of units.
+ conversion:
+ factor: 1609.344
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: 1S
+ Name: metre per second pascal
+ ConversionFactor: m² x kg⁻¹ x s
+ Symbol: "(m/s)/Pa"
+ CommonCode: M59
+ Description: SI base unit meter divided by the product of SI base unit second and
+ the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M59
+- Status: ''
+ LevelAndCategory: '2'
+ Name: metre per hour
+ ConversionFactor: 2,777 78 x 10⁻⁴ m/s
+ Symbol: m/h
+ CommonCode: M60
+ Description: SI base unit metre divided by the unit hour.
+ conversion:
+ factor: 0.000277778
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per year
+ ConversionFactor: 8,048 774 x 10⁻¹⁰ m/s
+ Symbol: in/y
+ CommonCode: M61
+ Description: Unit of the length according to the Anglo-American and Imperial system
+ of units divided by the unit common year with 365 days.
+ conversion:
+ factor: 8.048774e-10
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilometre per second
+ ConversionFactor: 10³ m/s
+ Symbol: km/s
+ CommonCode: M62
+ Description: 1000-fold of the SI base unit metre divided by the SI base unit second.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch per minute
+ ConversionFactor: 4,233 333 x 10⁻⁴ m/s
+ Symbol: in/min
+ CommonCode: M63
+ Description: Unit inch according to the Anglo-American and Imperial system of units
+ divided by the unit minute.
+ conversion:
+ factor: 0.0004233333
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: yard per second
+ ConversionFactor: 9,144 x 10⁻¹ m/s
+ Symbol: yd/s
+ CommonCode: M64
+ Description: Unit yard according to the Anglo-American and Imperial system of units
+ divided by the SI base unit second.
+ conversion:
+ factor: 0.9144
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: yard per minute
+ ConversionFactor: 1,524 x 10⁻² m/s
+ Symbol: yd/min
+ CommonCode: M65
+ Description: Unit yard according to the Anglo-American and Imperial system of units
+ divided by the unit minute.
+ conversion:
+ factor: 0.01524
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: yard per hour
+ ConversionFactor: 2,54 x 10⁻⁴ m/s
+ Symbol: yd/h
+ CommonCode: M66
+ Description: Unit yard according to the Anglo-American and Imperial system of units
+ divided by the unit hour.
+ conversion:
+ factor: 0.000254
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: acre-foot (based on U.S. survey foot)
+ ConversionFactor: 1,233 489 x 10³ m³
+ Symbol: acre-ft (US survey)
+ CommonCode: M67
+ Description: Unit of the volume, which is used in the United States to measure/gauge
+ the capacity of reservoirs.
+ conversion:
+ factor: 1233.489
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cord (128 ft3)
+ ConversionFactor: 3,624 556 m³
+ Symbol: cord
+ CommonCode: M68
+ Description: Traditional unit of the volume of stacked firewood which has been measured
+ with a cord.
+ conversion:
+ factor: 3.624556
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic mile (UK statute)
+ ConversionFactor: 4,168 182 x 10⁹ m³
+ Symbol: mi³
+ CommonCode: M69
+ Description: Unit of volume according to the Imperial system of units.
+ conversion:
+ factor: 4168182000.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: micro-inch
+ ConversionFactor: 25,4 x 10⁻⁹ m
+ Symbol: µin
+ CommonCode: M7
+ Description: ''
+ conversion:
+ factor: 2.54e-08
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton, register
+ ConversionFactor: 2,831 685 m³
+ Symbol: RT
+ CommonCode: M70
+ Description: Traditional unit of the cargo capacity.
+ conversion:
+ factor: 2.831685
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic metre per pascal
+ ConversionFactor: kg⁻¹ x m⁴ x s²
+ Symbol: m³/Pa
+ CommonCode: M71
+ Description: Power of the SI base unit meter by exponent 3 divided by the derived
+ SI base unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M71
+- Status: ''
+ LevelAndCategory: 1M
+ Name: bel
+ ConversionFactor: B
+ Symbol: B
+ CommonCode: M72
+ Description: Logarithmic relationship to base 10.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M72
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per cubic metre pascal
+ ConversionFactor: m⁻² x s²
+ Symbol: "(kg/m³)/Pa"
+ CommonCode: M73
+ Description: SI base unit kilogram divided by the product of the power of the SI
+ base unit metre with exponent 3 and the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M73
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: kilogram per pascal
+ ConversionFactor: m x s²
+ Symbol: kg/Pa
+ CommonCode: M74
+ Description: SI base unit kilogram divided by the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M74
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilopound-force
+ ConversionFactor: 4,448 222 x 10³ N
+ Symbol: kip
+ CommonCode: M75
+ Description: 1000-fold of the unit of the force pound-force (lbf) according to the
+ Anglo-American system of units with the relationship.
+ conversion:
+ factor: 4448.222
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poundal
+ ConversionFactor: 1,382 550 x 10⁻¹ N
+ Symbol: pdl
+ CommonCode: M76
+ Description: Non SI-conforming unit of the power, which corresponds to a mass of
+ a pound multiplied with the acceleration of a foot per square second.
+ conversion:
+ factor: 0.138255
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilogram metre per second squared
+ ConversionFactor: "(kg x m)/s²"
+ Symbol: kg·m/s²
+ CommonCode: M77
+ Description: Product of the SI base unit kilogram and the SI base unit metre divided
+ by the power of the SI base unit second by exponent 2.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M77
+ - NEW
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pond
+ ConversionFactor: 9,806 65 x 10⁻³ N
+ Symbol: p
+ CommonCode: M78
+ Description: 0,001-fold of the unit of the weight, defined as a mass of 1 kg which
+ finds out about a weight strength from 1 kp by the gravitational force at sea
+ level which corresponds to a strength of 9,806 65 newton.
+ conversion:
+ factor: 0.00980665
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square foot per hour
+ ConversionFactor: 2,580 64 x 10⁻⁵ m²/s
+ Symbol: ft²/h
+ CommonCode: M79
+ Description: Power of the unit foot according to the Anglo-American and Imperial
+ system of units by exponent 2 divided by the unit of time hour.
+ conversion:
+ factor: 2.58064e-05
+ base_units:
+ - S4
+- Status: ''
+ LevelAndCategory: '2'
+ Name: stokes per pascal
+ ConversionFactor: 10⁻⁴ kg⁻¹ x m³ x s
+ Symbol: St/Pa
+ CommonCode: M80
+ Description: CGS (Centimetre-Gram-Second system) unit stokes divided by the derived
+ SI unit pascal.
+ conversion:
+ factor: 0.0001
+ base_units:
+ - M82
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square centimetre per second
+ ConversionFactor: 10⁻⁴ m²/s
+ Symbol: cm²/s
+ CommonCode: M81
+ Description: 0,000 1-fold of the power of the SI base unit metre by exponent 2 divided
+ by the SI base unit second.
+ conversion:
+ factor: 0.0001
+ base_units:
+ - S4
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: square metre per second pascal
+ ConversionFactor: kg⁻¹ x m³ x s
+ Symbol: "(m²/s)/Pa"
+ CommonCode: M82
+ Description: Power of the SI base unit metre with the exponent 2 divided by the
+ SI base unit second and the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M82
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: denier
+ ConversionFactor: 1,111 111 x 10⁻⁷ kg/m
+ Symbol: den
+ CommonCode: M83
+ Description: Traditional unit for the indication of the linear mass of textile fibers
+ and yarns.
+ conversion:
+ factor: 1.111111e-07
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per yard
+ ConversionFactor: 4,960 546 x 10⁻¹ kg/m
+ Symbol: lb/yd
+ CommonCode: M84
+ Description: Unit for linear mass according to avoirdupois system of units.
+ conversion:
+ factor: 0.4960546
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton, assay
+ ConversionFactor: 2,916 667 x 10⁻² kg
+ Symbol: ''
+ CommonCode: M85
+ Description: Non SI-conforming unit of the mass used in the mineralogy to determine
+ the concentration of precious metals in ore according to the mass of the precious
+ metal in milligrams in a sample of the mass of an assay sound (number of troy
+ ounces in a short ton (1 000 lb)).
+ conversion:
+ factor: 0.02916667
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pfund
+ ConversionFactor: 0,5 kg
+ Symbol: pfd
+ CommonCode: M86
+ Description: Outdated unit of the mass used in Germany.
+ conversion:
+ factor: 0.5
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilogram per second pascal
+ ConversionFactor: m x s
+ Symbol: "(kg/s)/Pa"
+ CommonCode: M87
+ Description: SI base unit kilogram divided by the product of the SI base unit second
+ and the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M87
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per month
+ ConversionFactor: 3,802 570 537 68 x 10⁻⁴ kg/s
+ Symbol: t/mo
+ CommonCode: M88
+ Description: Unit tonne divided by the unit month.
+ conversion:
+ factor: 0.000380257053768
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2'
+ Name: tonne per year
+ ConversionFactor: 3,168 808 781 x 10⁻⁵ kg/s
+ Symbol: t/y
+ CommonCode: M89
+ Description: Unit tonne divided by the unit year with 365 days.
+ conversion:
+ factor: 3.168808781e-05
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: million Btu per 1000 cubic foot
+ ConversionFactor: ''
+ Symbol: MBTU/kft³
+ CommonCode: M9
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilopound per hour
+ ConversionFactor: 0,125 997 889 kg/s
+ Symbol: klb/h
+ CommonCode: M90
+ Description: 1000-fold of the unit of the mass avoirdupois pound according to the
+ avoirdupois unit system divided by the unit hour.
+ conversion:
+ factor: 0.125997889
+ base_units:
+ - KGS
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound per pound
+ ConversionFactor: '1.0'
+ Symbol: lb/lb
+ CommonCode: M91
+ Description: Proportion of the mass consisting of the avoirdupois pound according
+ to the avoirdupois unit system divided by the avoirdupois pound according to the
+ avoirdupois unit system.
+ conversion:
+ factor: 1.0
+ base_units:
+ - E98
+ - F02
+ - H60
+ - L52
+ - M91
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force foot
+ ConversionFactor: 1,355 818 N x m
+ Symbol: lbf·ft
+ CommonCode: M92
+ Description: Product of the unit pound-force according to the Anglo-American system
+ of units and the unit foot according to the Anglo-American and the Imperial system
+ of units.
+ conversion:
+ factor: 1.355818
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: 1M
+ Name: newton metre per radian
+ ConversionFactor: m² x kg x s⁻² x rad⁻¹
+ Symbol: N·m/rad
+ CommonCode: M93
+ Description: Product of the derived SI unit newton and the SI base unit metre divided
+ by the unit radian.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M93
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilogram metre
+ ConversionFactor: kg x m
+ Symbol: kg·m
+ CommonCode: M94
+ Description: Unit of imbalance as a product of the SI base unit kilogram and the
+ SI base unit metre.
+ conversion:
+ factor: 1.0
+ base_units:
+ - M94
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poundal foot
+ ConversionFactor: 4,214 011 x 10⁻² N x m
+ Symbol: pdl·ft
+ CommonCode: M95
+ Description: Product of the non SI-conforming unit of the force poundal and the
+ unit foot according to the Anglo-American and Imperial system of units .
+ conversion:
+ factor: 0.04214011
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poundal inch
+ ConversionFactor: 3,511 677 10⁻³ N x m
+ Symbol: pdl·in
+ CommonCode: M96
+ Description: Product of the non SI-conforming unit of the force poundal and the
+ unit inch according to the Anglo-American and Imperial system of units .
+ conversion:
+ factor: 1.0
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: dyne metre
+ ConversionFactor: 10⁻⁵ N x m
+ Symbol: dyn·m
+ CommonCode: M97
+ Description: CGS (Centimetre-Gram-Second system) unit of the rotational moment.
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - NU
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram centimetre per second
+ ConversionFactor: 10⁻² kg x m/s
+ Symbol: kg·(cm/s)
+ CommonCode: M98
+ Description: Product of the SI base unit kilogram and the 0,01-fold of the SI base
+ unit metre divided by the SI base unit second.
+ conversion:
+ factor: 0.01
+ base_units:
+ - B31
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram centimetre per second
+ ConversionFactor: 10⁻⁵ kg x m/s
+ Symbol: g·(cm/s)
+ CommonCode: M99
+ Description: Product of the 0,001-fold of the SI base unit kilogram and the 0,01-fold
+ of the SI base unit metre divided by the SI base unit second.
+ conversion:
+ factor: 1.0e-05
+ base_units:
+ - B31
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: machine per unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MA
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: megavolt ampere reactive hour
+ ConversionFactor: ''
+ Symbol: Mvar·h
+ CommonCode: MAH
+ Description: A unit of electrical reactive power defining the total amount of reactive
+ power across a power system.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megalitre
+ ConversionFactor: 10³ m³
+ Symbol: Ml
+ CommonCode: MAL
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: megametre
+ ConversionFactor: 10⁶ m
+ Symbol: Mm
+ CommonCode: MAM
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megavar
+ ConversionFactor: ''
+ Symbol: Mvar
+ CommonCode: MAR
+ Description: A unit of electrical reactive power represented by a current of one
+ thousand amperes flowing due a potential difference of one thousand volts where
+ the sine of the phase angle between them is 1.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megawatt
+ ConversionFactor: 10⁶ W
+ Symbol: MW
+ CommonCode: MAW
+ Description: A unit of power defining the rate of energy transferred or consumed
+ when a current of 1000 amperes flows due to a potential of 1000 volts at unity
+ power factor.
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: thousand standard brick equivalent
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MBE
+ Description: A unit of count defining the number of one thousand brick equivalent
+ units.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: thousand board foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MBF
+ Description: A unit of volume equal to one thousand board foot.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millibar
+ ConversionFactor: 10² Pa
+ Symbol: mbar
+ CommonCode: MBR
+ Description: ''
+ conversion:
+ factor: 100.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microgram
+ ConversionFactor: 10⁻⁹ kg
+ Symbol: µg
+ CommonCode: MC
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: 2S
+ Name: millicurie
+ ConversionFactor: 3,7 x 10⁷ Bq
+ Symbol: mCi
+ CommonCode: MCU
+ Description: ''
+ conversion:
+ factor: 37000000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: air dry metric ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MD
+ Description: A unit of count defining the number of metric tons of a product, disregarding
+ the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: milligram per square foot per side
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MF
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milligram
+ ConversionFactor: 10⁻⁶ kg
+ Symbol: mg
+ CommonCode: MGM
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megahertz
+ ConversionFactor: 10⁶ Hz
+ Symbol: MHz
+ CommonCode: MHZ
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - HTZ
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square mile (statute mile)
+ ConversionFactor: 2,589 988 km²
+ Symbol: mi²
+ CommonCode: MIK
+ Description: ''
+ conversion:
+ factor: 2.589988
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: thousand
+ ConversionFactor: 10³
+ Symbol: ''
+ CommonCode: MIL
+ Description: ''
+ conversion:
+ factor: 1000.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: minute [unit of time]
+ ConversionFactor: 60 s
+ Symbol: min
+ CommonCode: MIN
+ Description: ''
+ conversion:
+ factor: 60.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: million
+ ConversionFactor: 10⁶
+ Symbol: ''
+ CommonCode: MIO
+ Description: ''
+ conversion:
+ factor: 1000000.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: million international unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MIU
+ Description: A unit of count defining the number of international units in multiples
+ of 10⁶.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: milligram per square inch
+ ConversionFactor: ''
+ Symbol: mg/in²
+ CommonCode: MK
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: milliard
+ ConversionFactor: 10⁹
+ Symbol: ''
+ CommonCode: MLD
+ Description: 'Synonym: billion (US)'
+ conversion:
+ factor: 1000000000.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millilitre
+ ConversionFactor: 10⁻⁶ m³
+ Symbol: ml
+ CommonCode: MLT
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: square millimetre
+ ConversionFactor: 10⁻⁶ m²
+ Symbol: mm²
+ CommonCode: MMK
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic millimetre
+ ConversionFactor: 10⁻⁹ m³
+ Symbol: mm³
+ CommonCode: MMQ
+ Description: ''
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: millimetre
+ ConversionFactor: 10⁻³ m
+ Symbol: mm
+ CommonCode: MMT
+ Description: ''
+ conversion:
+ factor: 0.001
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: kilogram, dry weight
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MND
+ Description: A unit of mass defining the number of kilograms of a product, disregarding
+ the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: month
+ ConversionFactor: 2,629 800 x 10⁶ s
+ Symbol: mo
+ CommonCode: MON
+ Description: Unit of time equal to 1/12 of a year of 365,25 days.
+ conversion:
+ factor: 2629800.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megapascal
+ ConversionFactor: 10⁶ Pa
+ Symbol: MPa
+ CommonCode: MPA
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - C55
+ - PAL
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand metre
+ ConversionFactor: 10³m
+ Symbol: ''
+ CommonCode: MQ
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per hour
+ ConversionFactor: 2,777 78 x 10⁻⁴ m³/s
+ Symbol: m³/h
+ CommonCode: MQH
+ Description: ''
+ conversion:
+ factor: 0.000277778
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '1'
+ Name: cubic metre per second
+ ConversionFactor: m³/s
+ Symbol: m³/s
+ CommonCode: MQS
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - MQS
+- Status: ''
+ LevelAndCategory: '1'
+ Name: metre per second squared
+ ConversionFactor: m/s²
+ Symbol: m/s²
+ CommonCode: MSK
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - MSK
+ - P79
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: mat
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MT
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre
+ ConversionFactor: m²
+ Symbol: m²
+ CommonCode: MTK
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '1'
+ Name: cubic metre
+ ConversionFactor: m³
+ Symbol: m³
+ CommonCode: MTQ
+ Description: 'Synonym: metre cubed'
+ conversion:
+ factor: 1.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '1'
+ Name: metre
+ ConversionFactor: m
+ Symbol: m
+ CommonCode: MTR
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '1'
+ Name: metre per second
+ ConversionFactor: m/s
+ Symbol: m/s
+ CommonCode: MTS
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - MTS
+ - P87
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: number of mults
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: MV
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megavolt - ampere
+ ConversionFactor: 10⁶ V x A
+ Symbol: MV·A
+ CommonCode: MVA
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - D44
+- Status: ''
+ LevelAndCategory: 1S
+ Name: megawatt hour (1000 kW.h)
+ ConversionFactor: 3,6 x 10⁹ J
+ Symbol: MW·h
+ CommonCode: MWH
+ Description: A unit of power defining the total amount of bulk energy transferred
+ or consumed.
+ conversion:
+ factor: 3600000000.0
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: pen calorie
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: N1
+ Description: A unit of count defining the number of calories prescribed daily for
+ parenteral/enteral therapy.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound foot per second
+ ConversionFactor: 1,382 550 x 10⁻¹ kg x m/s
+ Symbol: lb·(ft/s)
+ CommonCode: N10
+ Description: Product of the avoirdupois pound according to the avoirdupois unit
+ system and the unit foot according to the Anglo-American and Imperial system of
+ units divided by the SI base unit second.
+ conversion:
+ factor: 0.138255
+ base_units:
+ - B31
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound inch per second
+ ConversionFactor: 1,152 125 x 10⁻² kg x m/s
+ Symbol: lb·(in/s)
+ CommonCode: N11
+ Description: Product of the avoirdupois pound according to the avoirdupois unit
+ system and the unit inch according to the Anglo-American and Imperial system of
+ units divided by the SI base unit second.
+ conversion:
+ factor: 0.01152125
+ base_units:
+ - B31
+- Status: ''
+ LevelAndCategory: '2'
+ Name: Pferdestaerke
+ ConversionFactor: 7,354 988 x 10² W
+ Symbol: PS
+ CommonCode: N12
+ Description: 'Obsolete unit of the power relating to DIN 1301-3:1979: 1 PS = 735,498
+ 75 W.'
+ conversion:
+ factor: 735.4988
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centimetre of mercury (0 ºC)
+ ConversionFactor: 1,333 22 x 10³ Pa
+ Symbol: cmHg (0 ºC)
+ CommonCode: N13
+ Description: Non SI-conforming unit of pressure, at which a value of 1 cmHg meets
+ the static pressure, which is generated by a mercury at a temperature of 0 °C
+ with a height of 1 centimetre .
+ conversion:
+ factor: 1333.22
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: centimetre of water (4 ºC)
+ ConversionFactor: 9,806 38 x 10 Pa
+ Symbol: cmH₂O (4 °C)
+ CommonCode: N14
+ Description: Non SI-conforming unit of pressure, at which a value of 1 cmH2O meets
+ the static pressure, which is generated by a head of water at a temperature of
+ 4 °C with a height of 1 centimetre .
+ conversion:
+ factor: 9.80638
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot of water (39.2 ºF)
+ ConversionFactor: 2,988 98 x 10³ Pa
+ Symbol: ftH₂O (39,2 ºF)
+ CommonCode: N15
+ Description: Non SI-conforming unit of pressure according to the Anglo-American
+ and Imperial system for units, whereas the value of 1 ftH2O is equivalent to the
+ static pressure, which is generated by a head of water at a temperature 39,2°F
+ with a height of 1 foot .
+ conversion:
+ factor: 2988.98
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch of mercury (32 ºF)
+ ConversionFactor: 3,386 38 x 10³ Pa
+ Symbol: inHG (32 ºF)
+ CommonCode: N16
+ Description: Non SI-conforming unit of pressure according to the Anglo-American
+ and Imperial system for units, whereas the value of 1 inHg meets the static pressure,
+ which is generated by a mercury at a temperature of 32°F with a height of 1 inch.
+ conversion:
+ factor: 3386.38
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch of mercury (60 ºF)
+ ConversionFactor: 3,376 85 x 10³ Pa
+ Symbol: inHg (60 ºF)
+ CommonCode: N17
+ Description: Non SI-conforming unit of pressure according to the Anglo-American
+ and Imperial system for units, whereas the value of 1 inHg meets the static pressure,
+ which is generated by a mercury at a temperature of 60°F with a height of 1 inch.
+ conversion:
+ factor: 3376.85
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch of water (39.2 ºF)
+ ConversionFactor: 2,490 82 × 10² Pa
+ Symbol: inH₂O (39,2 ºF)
+ CommonCode: N18
+ Description: Non SI-conforming unit of pressure according to the Anglo-American
+ and Imperial system for units, whereas the value of 1 inH2O meets the static pressure,
+ which is generated by a head of water at a temperature of 39,2°F with a height
+ of 1 inch .
+ conversion:
+ factor: 2.49082
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: inch of water (60 ºF)
+ ConversionFactor: 2,488 4 × 10² Pa
+ Symbol: inH₂O (60 ºF)
+ CommonCode: N19
+ Description: Non SI-conforming unit of pressure according to the Anglo-American
+ and Imperial system for units, whereas the value of 1 inH2O meets the static pressure,
+ which is generated by a head of water at a temperature of 60°F with a height of
+ 1 inch .
+ conversion:
+ factor: 2.4884
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: number of lines
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: N2
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kip per square inch
+ ConversionFactor: 6,894 757 x 10⁶ Pa
+ Symbol: ksi
+ CommonCode: N20
+ Description: Non SI-conforming unit of the pressure according to the Anglo-American
+ system of units as the 1000-fold of the unit of the force pound-force divided
+ by the power of the unit inch by exponent 2.
+ conversion:
+ factor: 6894757.0
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poundal per square foot
+ ConversionFactor: 1,488 164 Pa
+ Symbol: pdl/ft²
+ CommonCode: N21
+ Description: 'Non SI-conforming unit of pressure by the Imperial system of units
+ according to NIST: 1 pdl/ft² = 1,488 164 Pa.'
+ conversion:
+ factor: 1.488164
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois) per square inch
+ ConversionFactor: 4,394 185 x 10 kg/m²
+ Symbol: oz/in²
+ CommonCode: N22
+ Description: Unit of the surface specific mass (avoirdupois ounce according to the
+ avoirdupois system of units according to the surface square inch according to
+ the Anglo-American and Imperial system of units).
+ conversion:
+ factor: 4.394185
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '2'
+ Name: conventional metre of water
+ ConversionFactor: 9,806 65 x 10³ Pa
+ Symbol: mH₂O
+ CommonCode: N23
+ Description: Not SI-conforming unit of pressure, whereas a value of 1 mH2O is equivalent
+ to the static pressure, which is produced by one metre high water column .
+ conversion:
+ factor: 9806.65
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '2'
+ Name: gram per square millimetre
+ ConversionFactor: 10³ kg/m²
+ Symbol: g/mm²
+ CommonCode: N24
+ Description: 0,001-fold of the SI base unit kilogram divided by the 0.000 001-fold
+ of the power of the SI base unit meter by exponent 2.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per square yard
+ ConversionFactor: 5,424 919 x 10⁻¹ kg/m²
+ Symbol: lb/yd²
+ CommonCode: N25
+ Description: Unit for areal-related mass as a unit pound according to the avoirdupois
+ unit system divided by the power of the unit yard according to the Anglo-American
+ and Imperial system of units with exponent 2.
+ conversion:
+ factor: 0.5424919
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poundal per square inch
+ ConversionFactor: 2,142 957 × 10² Pa
+ Symbol: pdl/in²
+ CommonCode: N26
+ Description: Non SI-conforming unit of the pressure according to the Imperial system
+ of units (poundal by square inch).
+ conversion:
+ factor: 2.142957
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot to the fourth power
+ ConversionFactor: 8,630 975 x 10⁻³ m⁴
+ Symbol: ft⁴
+ CommonCode: N27
+ Description: 'Power of the unit foot according to the Anglo-American and Imperial
+ system of units by exponent 4 according to NIST: 1 ft4 = 8,630 975 m4.'
+ conversion:
+ factor: 0.008630975
+ base_units:
+ - B83
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic decimetre per kilogram
+ ConversionFactor: 10⁻³ m³ x kg⁻¹
+ Symbol: dm³/kg
+ CommonCode: N28
+ Description: 0,001 fold of the power of the SI base unit meter by exponent 3 divided
+ by the SI based unit kilogram.
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: cubic foot per pound
+ ConversionFactor: 6,242 796 x 10⁻² m³/kg
+ Symbol: ft³/lb
+ CommonCode: N29
+ Description: Power of the unit foot according to the Anglo-American and Imperial
+ system of units by exponent 3 divided by the unit avoirdupois pound according
+ to the avoirdupois unit system.
+ conversion:
+ factor: 0.06242796
+ base_units:
+ - A39
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: print point
+ ConversionFactor: 0,013 8 in (approx)
+ Symbol: ''
+ CommonCode: N3
+ Description: ''
+ conversion:
+ factor: 0.0138
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: cubic inch per pound
+ ConversionFactor: 3,612 728 x 10⁻⁵ m³/kg
+ Symbol: in³/lb
+ CommonCode: N30
+ Description: Power of the unit inch according to the Anglo-American and Imperial
+ system of units by exponent 3 divided by the avoirdupois pound according to the
+ avoirdupois unit system .
+ conversion:
+ factor: 3.612728e-05
+ base_units:
+ - A39
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilonewton per metre
+ ConversionFactor: 10³ N/m
+ Symbol: kN/m
+ CommonCode: N31
+ Description: 1000-fold of the derived SI unit newton divided by the SI base unit
+ metre.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - 4P
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: poundal per inch
+ ConversionFactor: 5,443 110 N/m
+ Symbol: pdl/in
+ CommonCode: N32
+ Description: Non SI-conforming unit of the surface tension according to the Imperial
+ unit system as quotient poundal by inch.
+ conversion:
+ factor: 5.44311
+ base_units:
+ - 4P
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound-force per yard
+ ConversionFactor: 4,864 635 N/m
+ Symbol: lbf/yd
+ CommonCode: N33
+ Description: Unit of force per unit length based on the Anglo-American system of
+ units.
+ conversion:
+ factor: 4.864635
+ base_units:
+ - 4P
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: poundal second per square foot
+ ConversionFactor: 1,488 164 Pa x s
+ Symbol: "(pdl/ft²)·s"
+ CommonCode: N34
+ Description: Non SI-conforming unit of viscosity.
+ conversion:
+ factor: 1.488164
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2'
+ Name: poise per pascal
+ ConversionFactor: 0,1 s
+ Symbol: P/Pa
+ CommonCode: N35
+ Description: CGS (Centimetre-Gram-Second system) unit poise divided by the derived
+ SI unit pascal.
+ conversion:
+ factor: 0.1
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: 1S
+ Name: newton second per square metre
+ ConversionFactor: Pa x s
+ Symbol: "(N/m²)·s"
+ CommonCode: N36
+ Description: Unit of the dynamic viscosity as a product of unit of the pressure
+ (newton by square metre) multiplied with the SI base unit second.
+ conversion:
+ factor: 1.0
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: kilogram per metre second
+ ConversionFactor: Pa x s
+ Symbol: kg/(m·s)
+ CommonCode: N37
+ Description: Unit of the dynamic viscosity as a quotient SI base unit kilogram divided
+ by the SI base unit metre and by the SI base unit second.
+ conversion:
+ factor: 1.0
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: kilogram per metre minute
+ ConversionFactor: 1,666 67 × 10⁻² Pa x s
+ Symbol: kg/(m·min)
+ CommonCode: N38
+ Description: Unit of the dynamic viscosity as a quotient SI base unit kilogram divided
+ by the SI base unit metre and by the unit minute.
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per metre day
+ ConversionFactor: 1,157 41 × 10⁻⁵ Pa x s
+ Symbol: kg/(m·d)
+ CommonCode: N39
+ Description: Unit of the dynamic viscosity as a quotient SI base unit kilogram divided
+ by the SI base unit metre and by the unit day.
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilogram per metre hour
+ ConversionFactor: 2,777 78 × 10⁻⁴ Pa x s
+ Symbol: kg/(m·h)
+ CommonCode: N40
+ Description: Unit of the dynamic viscosity as a quotient SI base unit kilogram divided
+ by the SI base unit metre and by the unit hour.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gram per centimetre second
+ ConversionFactor: 0,1 Pa x s
+ Symbol: g/(cm·s)
+ CommonCode: N41
+ Description: Unit of the dynamic viscosity as a quotient of the 0,001-fold of the
+ SI base unit kilogram divided by the 0,01-fold of the SI base unit metre and SI
+ base unit second.
+ conversion:
+ factor: 0.1
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: poundal second per square inch
+ ConversionFactor: 2,142 957 x 10² Pa x s
+ Symbol: "(pdl/in²)·s"
+ CommonCode: N42
+ Description: Non SI-conforming unit of dynamic viscosity according to the Imperial
+ system of units as product unit of the pressure (poundal by square inch) multiplied
+ by the SI base unit second.
+ conversion:
+ factor: 214.2957
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound per foot minute
+ ConversionFactor: 2,480 273 x 10⁻² Pa x s
+ Symbol: lb/(ft·min)
+ CommonCode: N43
+ Description: Unit of the dynamic viscosity according to the Anglo-American unit
+ system.
+ conversion:
+ factor: 0.02480273
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound per foot day
+ ConversionFactor: 1,722 412 x 10⁻⁵ Pa x s
+ Symbol: lb/(ft·d)
+ CommonCode: N44
+ Description: Unit of the dynamic viscosity according to the Anglo-American unit
+ system.
+ conversion:
+ factor: 1.722412e-05
+ base_units:
+ - C65
+ - N36
+ - N37
+- Status: ''
+ LevelAndCategory: 1S
+ Name: cubic metre per second pascal
+ ConversionFactor: kg⁻¹ x m⁴ x s
+ Symbol: "(m³/s)/Pa"
+ CommonCode: N45
+ Description: Power of the SI base unit meter by exponent 3 divided by the product
+ of the SI base unit second and the derived SI base unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - N45
+- Status: ''
+ LevelAndCategory: '2'
+ Name: foot poundal
+ ConversionFactor: 4,214 011 x 10⁻² J
+ Symbol: ft·pdl
+ CommonCode: N46
+ Description: Unit of the work (force-path).
+ conversion:
+ factor: 0.04214011
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: inch poundal
+ ConversionFactor: 3,511 677 x 10⁻³ J
+ Symbol: in·pdl
+ CommonCode: N47
+ Description: Unit of work (force multiplied by path) according to the Imperial system
+ of units as a product unit inch multiplied by poundal.
+ conversion:
+ factor: 0.003511677
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: watt per square centimetre
+ ConversionFactor: 10⁴ W/m²
+ Symbol: W/cm²
+ CommonCode: N48
+ Description: Derived SI unit watt divided by the power of the 0,01-fold the SI base
+ unit metre by exponent 2.
+ conversion:
+ factor: 10000.0
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: watt per square inch
+ ConversionFactor: 1,550 003 x 10³ W/m²
+ Symbol: W/in²
+ CommonCode: N49
+ Description: Derived SI unit watt divided by the power of the unit inch according
+ to the Anglo-American and Imperial system of units by exponent 2.
+ conversion:
+ factor: 1550.003
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (international table) per square foot hour
+ ConversionFactor: 3,154 591 W/m²
+ Symbol: BtuIT/(ft²·h)
+ CommonCode: N50
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 3.154591
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (thermochemical) per square foot hour
+ ConversionFactor: 3,152 481 W/m²
+ Symbol: Btuth/(ft²·h)
+ CommonCode: N51
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 3.152481
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (thermochemical) per square foot minute
+ ConversionFactor: 1,891 489 x 10² W/m²
+ Symbol: 'Btuth/(ft²·min) '
+ CommonCode: N52
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 189.1489
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (international table) per square foot second
+ ConversionFactor: 1,135 653 x 10⁴ W/m²
+ Symbol: BtuIT/(ft²·s)
+ CommonCode: N53
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 11356.53
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (thermochemical) per square foot second
+ ConversionFactor: 1,134 893 x 10⁴ W/m²
+ Symbol: Btuth/(ft²·s)
+ CommonCode: N54
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 11348.93
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (international table) per square inch second
+ ConversionFactor: 1,634 246 x 10⁶ W/m²
+ Symbol: BtuIT/(in²·s)
+ CommonCode: N55
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 1634246.0
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: calorie (thermochemical) per square centimetre minute
+ ConversionFactor: 6,973 333 x 10² W/m²
+ Symbol: calth/(cm²·min)
+ CommonCode: N56
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 697.3333
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: calorie (thermochemical) per square centimetre second
+ ConversionFactor: 4,184 x 10⁴ W/m²
+ Symbol: calth/(cm²·s)
+ CommonCode: N57
+ Description: Unit of the surface heat flux according to the Imperial system of units.
+ conversion:
+ factor: 41840.0
+ base_units:
+ - D54
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (international table) per cubic foot
+ ConversionFactor: 3,725 895 x10⁴ J/m³
+ Symbol: BtuIT/ft³
+ CommonCode: N58
+ Description: Unit of the energy density according to the Imperial system of units.
+ conversion:
+ factor: 3.725895
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (thermochemical) per cubic foot
+ ConversionFactor: 3,723 403 x10⁴ J/m³
+ Symbol: Btuth/ft³
+ CommonCode: N59
+ Description: Unit of the energy density according to the Imperial system of units.
+ conversion:
+ factor: 3.723403
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (international table) per degree Fahrenheit
+ ConversionFactor: 1,899 101 x 10³ J/K
+ Symbol: BtuIT/ºF
+ CommonCode: N60
+ Description: Unit of the heat capacity according to the Imperial system of units.
+ conversion:
+ factor: 1899.101
+ base_units:
+ - JE
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (thermochemical) per degree Fahrenheit
+ ConversionFactor: 1,897 830 x 10³ J/K
+ Symbol: Btuth/ºF
+ CommonCode: N61
+ Description: Unit of the heat capacity according to the Imperial system of units.
+ conversion:
+ factor: 1897.83
+ base_units:
+ - JE
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (international table) per degree Rankine
+ ConversionFactor: 1,899 101 x 10³ J/K
+ Symbol: BtuIT/ºR
+ CommonCode: N62
+ Description: Unit of the heat capacity according to the Imperial system of units.
+ conversion:
+ factor: 1899.101
+ base_units:
+ - JE
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (thermochemical) per degree Rankine
+ ConversionFactor: 1,897 830 x 10³ J/K
+ Symbol: Btuth/ºR
+ CommonCode: N63
+ Description: Unit of the heat capacity according to the Imperial system of units.
+ conversion:
+ factor: 1897.83
+ base_units:
+ - JE
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per pound degree Rankine
+ ConversionFactor: 4,184 x 10³ J/(kg x K)
+ Symbol: "(Btuth/°R)/lb"
+ CommonCode: N64
+ Description: Unit of the heat capacity (British thermal unit according to the international
+ table according to the Rankine degree) according to the Imperial system of units
+ divided by the unit avoirdupois pound according to the avoirdupois system of units.
+ conversion:
+ factor: 4184.0
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '2'
+ Name: kilocalorie (international table) per gram kelvin
+ ConversionFactor: 4,186 8 x 10⁶ J/(kg x K)
+ Symbol: "(kcalIT/K)/g"
+ CommonCode: N65
+ Description: Unit of the mass-related heat capacity as quotient 1000-fold of the
+ calorie (international table) divided by the product of the 0,001-fold of the
+ SI base units kilogram and kelvin.
+ conversion:
+ factor: 4186800.0
+ base_units:
+ - B11
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (39 ºF)
+ ConversionFactor: 1,059 67 x 10³ J
+ Symbol: 'Btu (39 ºF) '
+ CommonCode: N66
+ Description: Unit of heat energy according to the Imperial system of units in a
+ reference temperature of 39 °F.
+ conversion:
+ factor: 1059.67
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (59 ºF)
+ ConversionFactor: 1,054 80 x 10³ J
+ Symbol: Btu (59 ºF)
+ CommonCode: N67
+ Description: Unit of heat energy according to the Imperial system of units in a
+ reference temperature of 59 °F.
+ conversion:
+ factor: 1054.8
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (60 ºF)
+ ConversionFactor: 1,054 68 x 10³ J
+ Symbol: 'Btu (60 ºF) '
+ CommonCode: N68
+ Description: Unit of head energy according to the Imperial system of units at a
+ reference temperature of 60 °F.
+ conversion:
+ factor: 1054.68
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '2'
+ Name: calorie (20 ºC)
+ ConversionFactor: 4,181 90
+ Symbol: cal₂₀
+ CommonCode: N69
+ Description: Unit for quantity of heat, which is to be required for 1 g air free
+ water at a constant pressure from 101,325 kPa, to warm up the pressure of standard
+ atmosphere at sea level, from 19,5 °C on 20,5 °C.
+ conversion:
+ factor: 4.1819
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quad (1015 BtuIT)
+ ConversionFactor: 1,055 056 × 10¹⁸ J
+ Symbol: quad
+ CommonCode: N70
+ Description: Unit of heat energy according to the imperial system of units.
+ conversion:
+ factor: 1.055056
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: therm (EC)
+ ConversionFactor: 1,055 06 × 10⁸ J
+ Symbol: thm (EC)
+ CommonCode: N71
+ Description: 'Unit of heat energy in commercial use, within the EU defined: 1 thm
+ (EC) = 100 000 BtuIT.'
+ conversion:
+ factor: 1.05506
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: therm (U.S.)
+ ConversionFactor: 1,054 804 × 10⁸ J
+ Symbol: thm (US)
+ CommonCode: N72
+ Description: Unit of heat energy in commercial use.
+ conversion:
+ factor: 1.054804
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per pound
+ ConversionFactor: 2,324 444 x 10³ J/kg
+ Symbol: Btuth/lb
+ CommonCode: N73
+ Description: Unit of the heat energy according to the Imperial system of units divided
+ the unit avoirdupois pound according to the avoirdupois system of units.
+ conversion:
+ factor: 2324.444
+ base_units:
+ - J2
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per hour square foot degree Fahrenheit
+ ConversionFactor: 5,678 263 W/(m² x K)
+ Symbol: BtuIT/(h·ft²·ºF)
+ CommonCode: N74
+ Description: Unit of the heat transition coefficient according to the Imperial system
+ of units.
+ conversion:
+ factor: 5.678263
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per hour square foot degree Fahrenheit
+ ConversionFactor: 5,674 466 W/(m² x K)
+ Symbol: Btuth/(h·ft²·ºF)
+ CommonCode: N75
+ Description: Unit of the heat transition coefficient according to the imperial system
+ of units.
+ conversion:
+ factor: 5.674466
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (international table) per second square foot degree Fahrenheit
+ ConversionFactor: 2,044 175 x 10⁴ W/(m² x K)
+ Symbol: BtuIT/(s·ft²·ºF)
+ CommonCode: N76
+ Description: Unit of the heat transition coefficient according to the imperial system
+ of units.
+ conversion:
+ factor: 20441.75
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: '2'
+ Name: British thermal unit (thermochemical) per second square foot degree Fahrenheit
+ ConversionFactor: 2,042 808 x 10⁴ W/(m² x K)
+ Symbol: 'Btuth/(s·ft²·ºF) '
+ CommonCode: N77
+ Description: Unit of the heat transition coefficient according to the imperial system
+ of units.
+ conversion:
+ factor: 20428.08
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilowatt per square metre kelvin
+ ConversionFactor: 10³ W/(m² x K)
+ Symbol: kW/(m²·K)
+ CommonCode: N78
+ Description: 1000-fold of the derived SI unit watt divided by the product of the
+ power of the SI base unit metre by exponent 2 and the SI base unit kelvin.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D55
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kelvin per pascal
+ ConversionFactor: kg⁻¹ x m x s² x K
+ Symbol: K/Pa
+ CommonCode: N79
+ Description: SI base unit kelvin divided by the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - N79
+- Status: ''
+ LevelAndCategory: 1M
+ Name: watt per metre degree Celsius
+ ConversionFactor: W/(m x K)
+ Symbol: W/(m·°C)
+ CommonCode: N80
+ Description: Derived SI unit watt divided by the product of the SI base unit metre
+ and the unit for temperature degree Celsius.
+ conversion:
+ factor: 1.0
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilowatt per metre kelvin
+ ConversionFactor: 10³ W/(m x K)
+ Symbol: kW/(m·K)
+ CommonCode: N81
+ Description: 1000-fold of the derived SI unit watt divided by the product of the
+ SI base unit metre and the SI base unit kelvin.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilowatt per metre degree Celsius
+ ConversionFactor: 10³ W/(m x K)
+ Symbol: kW/(m·°C)
+ CommonCode: N82
+ Description: 1000-fold of the derived SI unit watt divided by the product of the
+ SI base unit metre and the unit for temperature degree Celsius.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D53
+ - N80
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: metre per degree Celcius metre
+ ConversionFactor: K⁻¹
+ Symbol: m/(°C·m)
+ CommonCode: N83
+ Description: SI base unit metre divided by the product of the unit degree Celsius
+ and the SI base unit metre.
+ conversion:
+ factor: 1.0
+ base_units:
+ - C91
+ - N83
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: degree Fahrenheit hour per British thermal unit (international table)
+ ConversionFactor: 1,895 634 K/W
+ Symbol: ºF/(BtuIT/h)
+ CommonCode: N84
+ Description: Non SI-conforming unit of the thermal resistance according to the Imperial
+ system of units.
+ conversion:
+ factor: 1.895634
+ base_units:
+ - B21
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: degree Fahrenheit hour per British thermal unit (thermochemical)
+ ConversionFactor: 1,896 903 K/W
+ Symbol: ºF/(Btuth/h)
+ CommonCode: N85
+ Description: Non SI-conforming unit of the thermal resistance according to the Imperial
+ system of units.
+ conversion:
+ factor: 1.896903
+ base_units:
+ - B21
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: degree Fahrenheit second per British thermal unit (international table)
+ ConversionFactor: 5,265 651 x 10⁻⁴ K/W
+ Symbol: ºF/(BtuIT/s)
+ CommonCode: N86
+ Description: Non SI-conforming unit of the thermal resistance according to the Imperial
+ system of units.
+ conversion:
+ factor: 0.0005265651
+ base_units:
+ - B21
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: degree Fahrenheit second per British thermal unit (thermochemical)
+ ConversionFactor: 5,269 175 x 10⁻⁴ K/W
+ Symbol: ºF/(Btuth/s)
+ CommonCode: N87
+ Description: Non SI-conforming unit of the thermal resistance according to the Imperial
+ system of units.
+ conversion:
+ factor: 0.0005269175
+ base_units:
+ - B21
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: degree Fahrenheit hour square foot per British thermal unit (international
+ table) inch
+ ConversionFactor: 6,933 472 K x m/W
+ Symbol: ºF·h·ft²/(BtuIT·in)
+ CommonCode: N88
+ Description: Unit of specific thermal resistance according to the Imperial system
+ of units.
+ conversion:
+ factor: 6.933472
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: degree Fahrenheit hour square foot per British thermal unit (thermochemical)
+ inch
+ ConversionFactor: 6,938 112 K x m/W
+ Symbol: ºF·h·ft²/(Btuth·in)
+ CommonCode: N89
+ Description: Unit of specific thermal resistance according to the Imperial system
+ of units.
+ conversion:
+ factor: 6.938112
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilofarad
+ ConversionFactor: 10³ F
+ Symbol: kF
+ CommonCode: N90
+ Description: 1000-fold of the derived SI unit farad.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - FAR
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: reciprocal joule
+ ConversionFactor: 1/J
+ Symbol: 1/J
+ CommonCode: N91
+ Description: Reciprocal of the derived SI unit joule.
+ conversion:
+ factor: 1.0
+ base_units:
+ - N91
+- Status: ''
+ LevelAndCategory: 1M
+ Name: picosiemens
+ ConversionFactor: 10⁻¹² S
+ Symbol: pS
+ CommonCode: N92
+ Description: 0,000 000 000 001-fold of the derived SI unit siemens.
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - NQ
+- Status: ''
+ LevelAndCategory: 1M
+ Name: ampere per pascal
+ ConversionFactor: kg⁻¹ x m x s² x A
+ Symbol: A/Pa
+ CommonCode: N93
+ Description: SI base unit ampere divided by the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - N93
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: franklin
+ ConversionFactor: 3,335 641 x 10⁻¹⁰ C
+ Symbol: Fr
+ CommonCode: N94
+ Description: CGS (Centimetre-Gram-Second system) unit of the electrical charge,
+ where the charge amounts to exactly 1 Fr where the force of 1 dyn on an equal
+ load is performed at a distance of 1 cm.
+ conversion:
+ factor: 3.335641e-10
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: 1M
+ Name: ampere minute
+ ConversionFactor: 60 C
+ Symbol: A·min
+ CommonCode: N95
+ Description: A unit of electric charge defining the amount of charge accumulated
+ by a steady flow of one ampere for one minute..
+ conversion:
+ factor: 60.0
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: biot
+ ConversionFactor: 10¹ A
+ Symbol: Bi
+ CommonCode: N96
+ Description: CGS (Centimetre-Gram-Second system) unit of the electric power which
+ is defined by a force of 2 dyn per cm between two parallel conductors of infinite
+ length with negligible cross-section in the distance of 1 cm.
+ conversion:
+ factor: 10.0
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: gilbert
+ ConversionFactor: 7,957 747 x 10⁻¹ A
+ Symbol: Gi
+ CommonCode: N97
+ Description: CGS (Centimetre-Gram-Second system) unit of the magnetomotive force,
+ which is defined by the work to increase the magnetic potential of a positive
+ common pol with 1 erg.
+ conversion:
+ factor: 0.7957747
+ base_units:
+ - AMP
+- Status: ''
+ LevelAndCategory: 1M
+ Name: volt per pascal
+ ConversionFactor: m³ x s⁻¹ x A⁻¹
+ Symbol: V/Pa
+ CommonCode: N98
+ Description: Derived SI unit volt divided by the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - N98
+- Status: ''
+ LevelAndCategory: 1M
+ Name: picovolt
+ ConversionFactor: 10⁻¹² V
+ Symbol: pV
+ CommonCode: N99
+ Description: 0,000 000 000 001-fold of the derived SI unit volt.
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: 1S
+ Name: milligram per kilogram
+ ConversionFactor: 10⁻⁶ 1
+ Symbol: mg/kg
+ CommonCode: NA
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: number of articles
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NAR
+ Description: 'A unit of count defining the number of articles (article: item).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: barge
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NB
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: number of bobbins
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NBB
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: car
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NC
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: number of cells
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NCL
+ Description: 'A unit of count defining the number of cells (cell: an enclosed or
+ circumscribed space, cavity, or volume).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: net barrel
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ND
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: net litre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: newton
+ ConversionFactor: "(kg x m)/s²"
+ Symbol: N
+ CommonCode: NEW
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - M77
+ - NEW
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: message
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NF
+ Description: A unit of count defining the number of messages.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: net gallon (us)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NG
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: message hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NH
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: net imperial gallon
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NI
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: nil
+ ConversionFactor: ''
+ Symbol: "()"
+ CommonCode: NIL
+ Description: A unit of count defining the number of instances of nothing.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: number of international units
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NIU
+ Description: A unit of count defining the number of international units.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: number of screens
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NJ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: load
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NL
+ Description: 'A unit of volume defining the number of loads (load: a quantity of
+ items carried or processed at one time).'
+ conversion:
+ factor: 1.0
+- Status: "¦"
+ LevelAndCategory: '2'
+ Name: Normalised cubic metre
+ ConversionFactor: m3
+ Symbol: ''
+ CommonCode: NM3
+ Description: Normalised cubic metre (temperature 0°C and pressure 1013.25 millibars
+ )
+ conversion:
+ factor: 1.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '1'
+ Name: nautical mile
+ ConversionFactor: 1 852 m
+ Symbol: n mile
+ CommonCode: NMI
+ Description: ''
+ conversion:
+ factor: 1852.0
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: number of packs
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NMP
+ Description: 'A unit of count defining the number of packs (pack: a collection of
+ objects packaged together).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: train
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NN
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: number of parcels
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NPL
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.7'
+ Name: number of pairs
+ ConversionFactor: use pair
+ Symbol: ''
+ CommonCode: NPR
+ Description: 'A unit of count defining the number of pairs (pair: item described
+ by two''s).'
+ conversion:
+ factor: 1.0
+ base_units:
+ - NPR
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: number of parts
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NPT
+ Description: 'A unit of count defining the number of parts (part: component of a
+ larger entity).'
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: mho
+ ConversionFactor: S
+ Symbol: ''
+ CommonCode: NQ
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - NQ
+- Status: D
+ LevelAndCategory: '2'
+ Name: micromho
+ ConversionFactor: 10⁻⁶ S
+ Symbol: ''
+ CommonCode: NR
+ Description: ''
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - NQ
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: number of rolls
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NRL
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: net ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NT
+ Description: A unit of mass equal to 2000 pounds, see ton (US). Refer International
+ Convention on tonnage measurement of Ships.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.4'
+ Name: net register ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NTT
+ Description: A unit of mass equal to the total cubic footage after deductions, where
+ 1 register ton is equal to 100 cubic feet. Refer International Convention on tonnage
+ measurement of Ships.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: newton metre
+ ConversionFactor: N x m
+ Symbol: N·m
+ CommonCode: NU
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - NU
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: vehicle
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NV
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: part per thousand
+ ConversionFactor: 1 x 10⁻³
+ Symbol: "‰"
+ CommonCode: NX
+ Description: 'A unit of proportion equal to 10⁻³. ,Synonym: per mille'
+ conversion:
+ factor: 0.001
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: pound per air dry metric ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: NY
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: panel
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: OA
+ Description: 'A unit of count defining the number of panels (panel: a distinct,
+ usually rectangular, section of a surface).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: ozone depletion equivalent
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ODE
+ Description: A unit of mass defining the ozone depletion potential in kilograms
+ of a product relative to the calculated depletion for the reference substance,
+ Trichlorofluoromethane (CFC-11).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: ODS Grams
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ODG
+ Description: A unit of measure calculated by multiplying the mass of the substance
+ in grams and the ozone-depleting potential for the substance.,
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: ODS Kilograms
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ODK
+ Description: A unit of measure calculated by multiplying the mass of the substance
+ in kilograms and the ozone-depleting potential for the substance.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: ODS Milligrams
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ODM
+ Description: A unit of measure calculated by multiplying the mass of the substance
+ in milligrams and the ozone-depleting potential for the substance.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: ohm
+ ConversionFactor: Ω
+ Symbol: Ω
+ CommonCode: OHM
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - OHM
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce per square yard
+ ConversionFactor: 3,390 575 x 10⁻² kg/m²
+ Symbol: oz/yd²
+ CommonCode: 'ON'
+ Description: ''
+ conversion:
+ factor: 0.03390575
+ base_units:
+ - '28'
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ounce (avoirdupois)
+ ConversionFactor: 2,834 952 x 10⁻² kg
+ Symbol: oz
+ CommonCode: ONZ
+ Description: ''
+ conversion:
+ factor: 0.02834952
+ base_units:
+ - KGM
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: two pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: OP
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: oscillations per minute
+ ConversionFactor: 1.667 x 10-2 /s
+ Symbol: o/min
+ CommonCode: OPM
+ Description: The number of oscillations per minute.
+ conversion:
+ factor: 1.0
+ base_units:
+ - BPM
+ - OPM
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: overtime hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: OT
+ Description: A unit of time defining the number of overtime hours.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.1'
+ Name: ounce av
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: OZ
+ Description: A unit of measure equal to 1/16 of a pound or about 28.3495 grams (av
+ = avoirdupois). Use ounce (common code ONZ).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: fluid ounce (US)
+ ConversionFactor: 2,957 353 x 10⁻⁵ m³
+ Symbol: fl oz (US)
+ CommonCode: OZA
+ Description: ''
+ conversion:
+ factor: 2.957353e-05
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: fluid ounce (UK)
+ ConversionFactor: 2,841 306 x 10⁻⁵ m³
+ Symbol: fl oz (UK)
+ CommonCode: OZI
+ Description: ''
+ conversion:
+ factor: 2.841306e-05
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: page - electronic
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P0
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent
+ ConversionFactor: 1 x 10⁻²
+ Symbol: "% or pct"
+ CommonCode: P1
+ Description: A unit of proportion equal to 0.01.
+ conversion:
+ factor: 0.01
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: coulomb per metre
+ ConversionFactor: m⁻¹ x s x A
+ Symbol: C/m
+ CommonCode: P10
+ Description: Derived SI unit coulomb divided by the SI base unit metre.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P10
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kiloweber
+ ConversionFactor: 10³ Wb
+ Symbol: kWb
+ CommonCode: P11
+ Description: 1000 fold of the derived SI unit weber.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - WEB
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: gamma
+ ConversionFactor: 10⁻⁹ T
+ Symbol: γ
+ CommonCode: P12
+ Description: Unit of magnetic flow density.
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilotesla
+ ConversionFactor: 10³ T
+ Symbol: kT
+ CommonCode: P13
+ Description: 1000-fold of the derived SI unit tesla.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D33
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: joule per second
+ ConversionFactor: W
+ Symbol: J/s
+ CommonCode: P14
+ Description: Quotient of the derived SI unit joule divided by the SI base unit second.
+ conversion:
+ factor: 1.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1M
+ Name: joule per minute
+ ConversionFactor: 1,666 67 × 10⁻² W
+ Symbol: J/min
+ CommonCode: P15
+ Description: Quotient from the derived SI unit joule divided by the unit minute.
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: joule per hour
+ ConversionFactor: 2,777 78 × 10⁻⁴ W
+ Symbol: J/h
+ CommonCode: P16
+ Description: Quotient from the derived SI unit joule divided by the unit hour.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: joule per day
+ ConversionFactor: 1,157 41 × 10⁻⁵ W
+ Symbol: J/d
+ CommonCode: P17
+ Description: Quotient from the derived SI unit joule divided by the unit day.
+ conversion:
+ factor: 1.15741
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilojoule per second
+ ConversionFactor: 10³ W
+ Symbol: kJ/s
+ CommonCode: P18
+ Description: Quotient from the 1000-fold of the derived SI unit joule divided by
+ the SI base unit second.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilojoule per minute
+ ConversionFactor: 1,666 67 × 10 W
+ Symbol: kJ/min
+ CommonCode: P19
+ Description: Quotient from the 1000-fold of the derived SI unit joule divided by
+ the unit minute.
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per foot
+ ConversionFactor: 1,488 164 kg/m
+ Symbol: lb/ft
+ CommonCode: P2
+ Description: ''
+ conversion:
+ factor: 1.488164
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilojoule per hour
+ ConversionFactor: 2,777 78 x 10⁻¹ W
+ Symbol: kJ/h
+ CommonCode: P20
+ Description: Quotient from the 1000-fold of the derived SI unit joule divided by
+ the unit hour.
+ conversion:
+ factor: 0.277778
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilojoule per day
+ ConversionFactor: 1,157 41 x 10⁻² W
+ Symbol: kJ/d
+ CommonCode: P21
+ Description: Quotient from the 1000-fold of the derived SI unit joule divided by
+ the unit day.
+ conversion:
+ factor: 0.0115741
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: ''
+ LevelAndCategory: 1M
+ Name: nanoohm
+ ConversionFactor: 10⁻⁹ Ω
+ Symbol: nΩ
+ CommonCode: P22
+ Description: 0,000 000 001-fold of the derived SI unit ohm.
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: ohm circular-mil per foot
+ ConversionFactor: 1,662 426 x 10⁻⁹ Ω x m
+ Symbol: 'Ω·cmil/ft '
+ CommonCode: P23
+ Description: Unit of resistivity.
+ conversion:
+ factor: 1.662426e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilohenry
+ ConversionFactor: 10³ H
+ Symbol: kH
+ CommonCode: P24
+ Description: 1000-fold of the derived SI unit henry.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - '81'
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: lumen per square foot
+ ConversionFactor: 1,076 391 x 10¹ cd x sr / m²
+ Symbol: lm/ft²
+ CommonCode: P25
+ Description: Derived SI unit lumen divided by the power of the unit foot according
+ to the Anglo-American and Imperial system of units by exponent 2.
+ conversion:
+ factor: 10.76391
+ base_units:
+ - LUX
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: phot
+ ConversionFactor: 10⁴ cd x sr / m²
+ Symbol: ph
+ CommonCode: P26
+ Description: CGS (Centimetre-Gram-Second system) unit of luminance, defined as lumen
+ by square centimetre.
+ conversion:
+ factor: 10000.0
+ base_units:
+ - LUX
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: footcandle
+ ConversionFactor: 1,076 391 x 10¹ cd x sr / m²
+ Symbol: ftc
+ CommonCode: P27
+ Description: Non SI conform traditional unit, defined as density of light which
+ impinges on a surface which has a distance of one foot from a light source, which
+ shines with an intensity of an international candle.
+ conversion:
+ factor: 10.76391
+ base_units:
+ - LUX
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: candela per square inch
+ ConversionFactor: 1,550 003 x 10³ cd/m²
+ Symbol: cd/in²
+ CommonCode: P28
+ Description: SI base unit candela divided by the power of unit inch according to
+ the Anglo-American and Imperial system of units by exponent 2.
+ conversion:
+ factor: 1550.003
+ base_units:
+ - A24
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: footlambert
+ ConversionFactor: 3,426 259 cd/m²
+ Symbol: ftL
+ CommonCode: P29
+ Description: Unit of the luminance according to the Anglo-American system of units,
+ defined as emitted or reflected luminance of a lm/ft².
+ conversion:
+ factor: 3.426259
+ base_units:
+ - A24
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: three pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P3
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: lambert
+ ConversionFactor: 3,183 099 x 10³ cd/m²
+ Symbol: Lb
+ CommonCode: P30
+ Description: CGS (Centimetre-Gram-Second system) unit of luminance, defined as the
+ emitted or reflected luminance by one lumen per square centimetre.
+ conversion:
+ factor: 3183.099
+ base_units:
+ - A24
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: stilb
+ ConversionFactor: 10⁴ cd/m²
+ Symbol: sb
+ CommonCode: P31
+ Description: CGS (Centimetre-Gram-Second system) unit of luminance, defined as emitted
+ or reflected luminance by one lumen per square centimetre.
+ conversion:
+ factor: 10000.0
+ base_units:
+ - A24
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: candela per square foot
+ ConversionFactor: 1,076 391 x 10 cd/m²
+ Symbol: cd/ft²
+ CommonCode: P32
+ Description: Base unit SI candela divided by the power of the unit foot according
+ to the Anglo-American and Imperial system of units by exponent 2.
+ conversion:
+ factor: 1.076391
+ base_units:
+ - A24
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilocandela
+ ConversionFactor: 10³ cd
+ Symbol: kcd
+ CommonCode: P33
+ Description: 1000-fold of the SI base unit candela.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - CDL
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millicandela
+ ConversionFactor: 10⁻³ cd
+ Symbol: mcd
+ CommonCode: P34
+ Description: 0,001-fold of the SI base unit candela.
+ conversion:
+ factor: 0.001
+ base_units:
+ - CDL
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: Hefner-Kerze
+ ConversionFactor: 0,903 cd
+ Symbol: HK
+ CommonCode: P35
+ Description: 'Obsolete, non-legal unit of the power in Germany relating to DIN 1301-3:1979:
+ 1 HK = 0,903 cd.'
+ conversion:
+ factor: 0.903
+ base_units:
+ - CDL
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: international candle
+ ConversionFactor: 1,019 cd
+ Symbol: IK
+ CommonCode: P36
+ Description: 'Obsolete, non-legal unit of the power in Germany relating to DIN 1301-3:1979:
+ 1 HK = 1,019 cd.'
+ conversion:
+ factor: 1.019
+ base_units:
+ - CDL
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (international table) per square foot
+ ConversionFactor: 1,135 653 x 10⁴ J/m²
+ Symbol: BtuIT/ft²
+ CommonCode: P37
+ Description: Unit of the areal-related energy transmission according to the Imperial
+ system of units.
+ conversion:
+ factor: 11356.53
+ base_units:
+ - B13
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: British thermal unit (thermochemical) per square foot
+ ConversionFactor: 1,134 893 x 10⁴ J/m²
+ Symbol: Btuth/ft²
+ CommonCode: P38
+ Description: Unit of the areal-related energy transmission according to the Imperial
+ system of units.
+ conversion:
+ factor: 11348.93
+ base_units:
+ - B13
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: calorie (thermochemical) per square centimetre
+ ConversionFactor: 4,184 x 10⁴ J/m²
+ Symbol: calth/cm²
+ CommonCode: P39
+ Description: Unit of the areal-related energy transmission according to the Imperial
+ system of units.
+ conversion:
+ factor: 41840.0
+ base_units:
+ - B13
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: four pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P4
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: langley
+ ConversionFactor: 4,184 x 10⁴ J/m²
+ Symbol: Ly
+ CommonCode: P40
+ Description: CGS (Centimetre-Gram-Second system) unit of the areal-related energy
+ transmission (as a measure of the incident quantity of heat of solar radiation
+ on the earth's surface).
+ conversion:
+ factor: 41840.0
+ base_units:
+ - B13
+- Status: ''
+ LevelAndCategory: '2'
+ Name: decade (logarithmic)
+ ConversionFactor: dec
+ Symbol: dec
+ CommonCode: P41
+ Description: 1 Dec := log2 10 ˜ 3,32 according to the logarithm for frequency range
+ between f1 and f2, when f2/f1 = 10.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P41
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: pascal squared second
+ ConversionFactor: m⁻² x kg² x s⁻³
+ Symbol: Pa²·s
+ CommonCode: P42
+ Description: Unit of the set as a product of the power of derived SI unit pascal
+ with exponent 2 and the SI base unit second.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P42
+- Status: ''
+ LevelAndCategory: 1M
+ Name: bel per metre
+ ConversionFactor: B/m
+ Symbol: B/m
+ CommonCode: P43
+ Description: Unit bel divided by the SI base unit metre.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P43
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound mole
+ ConversionFactor: 453,592 4 mol
+ Symbol: lbmol
+ CommonCode: P44
+ Description: Non SI-conforming unit of quantity of a substance relating that one
+ pound mole of a chemical composition corresponds to the same number of pounds
+ as the molecular weight of one molecule of this composition in atomic mass units.
+ conversion:
+ factor: 453.5924
+ base_units:
+ - C34
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound mole per second
+ ConversionFactor: 4,535 924 x 10² mol/s
+ Symbol: lbmol/s
+ CommonCode: P45
+ Description: Non SI-conforming unit of the power of the amount of substance non-SI
+ compliant unit of the molar flux relating that a pound mole of a chemical composition
+ the same number of pound corresponds like the molecular weight of a molecule of
+ this composition in atomic mass units.
+ conversion:
+ factor: 453.5924
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound mole per minute
+ ConversionFactor: 7,559 873 mol/s
+ Symbol: lbmol/h
+ CommonCode: P46
+ Description: Non SI-conforming unit of the power of the amount of substance non-SI
+ compliant unit of the molar flux relating that a pound mole of a chemical composition
+ the same number of pound corresponds like the molecular weight of a molecule of
+ this composition in atomic mass units.
+ conversion:
+ factor: 7.559873
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilomole per kilogram
+ ConversionFactor: 10³ mol/kg
+ Symbol: kmol/kg
+ CommonCode: P47
+ Description: 1000-fold of the SI base unit mol divided by the SI base unit kilogram.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C19
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: pound mole per pound
+ ConversionFactor: 10³ mol/kg
+ Symbol: lbmol/lb
+ CommonCode: P48
+ Description: Non SI-conforming unit of the material molar flux divided by the avoirdupois
+ pound for mass according to the avoirdupois unit system.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - C19
+- Status: ''
+ LevelAndCategory: 1S
+ Name: newton square metre per ampere
+ ConversionFactor: m³ x kg x s⁻² x A⁻¹
+ Symbol: N·m²/A
+ CommonCode: P49
+ Description: Product of the derived SI unit newton and the power of SI base unit
+ metre with exponent 2 divided by the SI base unit ampere.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P49
+ - P50
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: five pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P5
+ Description: 'A unit of count defining the number of five-packs (five-pack: set
+ of five items packaged together).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: weber metre
+ ConversionFactor: m³ x kg x s⁻² x A⁻¹
+ Symbol: Wb·m
+ CommonCode: P50
+ Description: Product of the derived SI unit weber and SI base unit metre.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P49
+ - P50
+- Status: ''
+ LevelAndCategory: 1S
+ Name: mol per kilogram pascal
+ ConversionFactor: m x kg⁻² x s² x mol
+ Symbol: "(mol/kg)/Pa"
+ CommonCode: P51
+ Description: SI base unit mol divided by the product of the SI base unit kilogram
+ and the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P51
+- Status: ''
+ LevelAndCategory: 1S
+ Name: mol per cubic metre pascal
+ ConversionFactor: m⁻² x kg⁻¹ x s² x mol
+ Symbol: "(mol/m³)/Pa"
+ CommonCode: P52
+ Description: SI base unit mol divided by the product of the power from the SI base
+ unit metre with exponent 3 and the derived SI unit pascal.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P52
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: unit pole
+ ConversionFactor: 1,256 637 x 10⁻⁷ Wb
+ Symbol: 'unit pole '
+ CommonCode: P53
+ Description: CGS (Centimetre-Gram-Second system) unit for magnetic flux of a magnetic
+ pole (according to the interaction of identical poles of 1 dyn at a distance of
+ a cm).
+ conversion:
+ factor: 1.256637e-07
+ base_units:
+ - WEB
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligray per second
+ ConversionFactor: 10⁻³ Gy/s
+ Symbol: mGy/s
+ CommonCode: P54
+ Description: 0,001-fold of the derived SI unit gray divided by the SI base unit
+ second.
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microgray per second
+ ConversionFactor: 10⁻⁶ Gy/s
+ Symbol: µGy/s
+ CommonCode: P55
+ Description: 0,000 001-fold of the derived SI unit gray divided by the SI base unit
+ second.
+ conversion:
+ factor: 1.0e-06
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: nanogray per second
+ ConversionFactor: 10⁻⁹ Gy/s
+ Symbol: nGy/s
+ CommonCode: P56
+ Description: 0,000 000 001-fold of the derived SI unit gray divided by the SI base
+ unit second.
+ conversion:
+ factor: 1.0e-09
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gray per minute
+ ConversionFactor: 1,666 67 × 10⁻² Gy/s
+ Symbol: Gy/min
+ CommonCode: P57
+ Description: SI derived unit gray divided by the unit minute.
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligray per minute
+ ConversionFactor: 1,666 67 × 10⁻⁵ Gy/s
+ Symbol: mGy/min
+ CommonCode: P58
+ Description: 0,001-fold of the derived SI unit gray divided by the unit minute.
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microgray per minute
+ ConversionFactor: 1,666 67 × 10⁻⁸ Gy/s
+ Symbol: µGy/min
+ CommonCode: P59
+ Description: 0,000 001-fold of the derived SI unit gray divided by the unit minute.
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: six pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P6
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: nanogray per minute
+ ConversionFactor: 1,666 67 × 10⁻¹¹ Gy/s
+ Symbol: nGy/min
+ CommonCode: P60
+ Description: 0,000 000 001-fold of the derived SI unit gray divided by the unit
+ minute.
+ conversion:
+ factor: 1.66667
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: gray per hour
+ ConversionFactor: 2,777 78 × 10⁻⁴ Gy/s
+ Symbol: Gy/h
+ CommonCode: P61
+ Description: SI derived unit gray divided by the unit hour.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: milligray per hour
+ ConversionFactor: 2,777 78 × 10⁻⁷ Gy/s
+ Symbol: mGy/h
+ CommonCode: P62
+ Description: 0,001-fold of the derived SI unit gray divided by the unit hour.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: microgray per hour
+ ConversionFactor: 2,777 78 × 10⁻¹⁰ Gy/s
+ Symbol: µGy/h
+ CommonCode: P63
+ Description: 0,000 001-fold of the derived SI unit gray divided by the unit hour.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: nanogray per hour
+ ConversionFactor: 2,777 78 × 10⁻¹³ Gy/s
+ Symbol: nGy/h
+ CommonCode: P64
+ Description: 0,000 000 001-fold of the derived SI unit gray divided by the unit
+ hour.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: sievert per second
+ ConversionFactor: Sv/s
+ Symbol: Sv/s
+ CommonCode: P65
+ Description: Derived SI unit sievert divided by the SI base unit second.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P65
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: millisievert per second
+ ConversionFactor: 10⁻³ Sv/s
+ Symbol: mSv/s
+ CommonCode: P66
+ Description: 0,001-fold of the derived SI unit sievert divided by the SI base unit
+ second.
+ conversion:
+ factor: 0.001
+ base_units:
+ - P65
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: microsievert per second
+ ConversionFactor: 10⁻⁶ Sv/s
+ Symbol: µSv/s
+ CommonCode: P67
+ Description: 0,000 001-fold of the derived SI unit sievert divided by the SI base
+ unit second.
+ conversion:
+ factor: 1.0e-06
+ base_units:
+ - P65
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: nanosievert per second
+ ConversionFactor: 10⁻⁹ Sv/s
+ Symbol: nSv/s
+ CommonCode: P68
+ Description: 0,000 000 001-fold of the derived SI unit sievert divided by the SI
+ base unit second.
+ conversion:
+ factor: 1.0e-09
+ base_units:
+ - P65
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: rem per second
+ ConversionFactor: 10⁻² Sv/s
+ Symbol: rem/s
+ CommonCode: P69
+ Description: 'Unit for the equivalent tin rate relating to DIN 1301-3:1979: 1 rem/s
+ = 0,01 J/(kg·s) = 1 Sv/s.'
+ conversion:
+ factor: 0.01
+ base_units:
+ - P65
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: seven pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P7
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: sievert per hour
+ ConversionFactor: 2,777 78 × 10⁻⁴ Sv/s
+ Symbol: Sv/h
+ CommonCode: P70
+ Description: Derived SI unit sievert divided by the unit hour.
+ conversion:
+ factor: 2.77778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: millisievert per hour
+ ConversionFactor: 0,277 777 778 × 10⁻⁷ Sv/s
+ Symbol: mSv/h
+ CommonCode: P71
+ Description: 0,001-fold of the derived SI unit sievert divided by the unit hour.
+ conversion:
+ factor: 0.277777778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: microsievert per hour
+ ConversionFactor: 0,277 777 778 × 10⁻¹⁰ Sv/s
+ Symbol: µSv/h
+ CommonCode: P72
+ Description: 0,000 001-fold of the derived SI unit sievert divided by the unit hour.
+ conversion:
+ factor: 0.277777778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: nanosievert per hour
+ ConversionFactor: 0,277 777 778 × 10⁻¹³ Sv/s
+ Symbol: nSv/h
+ CommonCode: P73
+ Description: 0,000 000 001-fold of the derived SI unit sievert divided by the unit
+ hour.
+ conversion:
+ factor: 0.277777778
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: sievert per minute
+ ConversionFactor: 0,016 666 Sv/s
+ Symbol: Sv/min
+ CommonCode: P74
+ Description: Derived SI unit sievert divided by the unit minute.
+ conversion:
+ factor: 0.016666
+ base_units:
+ - P65
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: millisievert per minute
+ ConversionFactor: 1,666 666 667 × 10⁻⁵ Sv/s
+ Symbol: mSv/min
+ CommonCode: P75
+ Description: 0,001-fold of the derived SI unit sievert divided by the unit minute.
+ conversion:
+ factor: 1.666666667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: microsievert per minute
+ ConversionFactor: 1,666 666 667 × 10⁻⁸ Sv/s
+ Symbol: µSv/min
+ CommonCode: P76
+ Description: 0,000 001-fold of the derived SI unit sievert divided by the unit minute.
+ conversion:
+ factor: 1.666666667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: nanosievert per minute
+ ConversionFactor: 1,666 666 667 × 10⁻¹¹ Sv/s
+ Symbol: nSv/min
+ CommonCode: P77
+ Description: 0,000 000 001-fold of the derived SI unit sievert divided by the unit
+ minute.
+ conversion:
+ factor: 1.666666667
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: reciprocal square inch
+ ConversionFactor: 1,550 003 x 10³ m⁻²
+ Symbol: 1/in²
+ CommonCode: P78
+ Description: Complement of the power of the unit inch according to the Anglo-American
+ and Imperial system of units by exponent 2.
+ conversion:
+ factor: 1550.003
+ base_units:
+ - C93
+- Status: ''
+ LevelAndCategory: '1.0'
+ Name: pascal square metre per kilogram
+ ConversionFactor: m/s²
+ Symbol: Pa/(kg/m²)
+ CommonCode: P79
+ Description: Unit of the burst index as derived unit for pressure pascal related
+ to the substance, represented as a quotient from the SI base unit kilogram divided
+ by the power of the SI base unit metre by exponent 2.
+ conversion:
+ factor: 1.0
+ base_units:
+ - MSK
+ - P79
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: eight pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P8
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: millipascal per metre
+ ConversionFactor: 10⁻³ kg/(m² x s²)
+ Symbol: mPa/m
+ CommonCode: P80
+ Description: 0,001-fold of the derived SI unit pascal divided by the SI base unit
+ metre.
+ conversion:
+ factor: 0.001
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kilopascal per metre
+ ConversionFactor: 10³ kg/(m² x s²)
+ Symbol: kPa/m
+ CommonCode: P81
+ Description: 1000-fold of the derived SI unit pascal divided by the SI base unit
+ metre.
+ conversion:
+ factor: 1000.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: hectopascal per metre
+ ConversionFactor: 10² kg/(m² x s²)
+ Symbol: hPa/m
+ CommonCode: P82
+ Description: 100-fold of the derived SI unit pascal divided by the SI base unit
+ metre.
+ conversion:
+ factor: 100.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: standard atmosphere per metre
+ ConversionFactor: 1,013 25 x 10⁵ kg/(m² x s²)
+ Symbol: Atm/m
+ CommonCode: P83
+ Description: Outdated unit of the pressure divided by the SI base unit metre.
+ conversion:
+ factor: 101325.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: technical atmosphere per metre
+ ConversionFactor: 9,806 65 x 10⁴ kg/(m² x s²)
+ Symbol: at/m
+ CommonCode: P84
+ Description: Obsolete and non-legal unit of the pressure which is generated by a
+ 10 metre water column divided by the SI base unit metre.
+ conversion:
+ factor: 98066.5
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: torr per metre
+ ConversionFactor: 1,333 224 x 10² kg/(m² x s²)
+ Symbol: Torr/m
+ CommonCode: P85
+ Description: CGS (Centimetre-Gram-Second system) unit of the pressure divided by
+ the SI base unit metre.
+ conversion:
+ factor: 133.3224
+ base_units: []
+- Status: ''
+ LevelAndCategory: '2.0'
+ Name: psi per inch
+ ConversionFactor: 2,714 471 x 10⁵ kg/(m² x s²)
+ Symbol: psi/in
+ CommonCode: P86
+ Description: Compound unit for pressure (pound-force according to the Anglo-American
+ unit system divided by the power of the unit inch according to the Anglo-American
+ and Imperial system of units with the exponent 2) divided by the unit inch according
+ to the Anglo-American and Imperial system of units .
+ conversion:
+ factor: 271447.1
+ base_units: []
+- Status: ''
+ LevelAndCategory: 1M
+ Name: cubic metre per second square metre
+ ConversionFactor: m/s
+ Symbol: "(m³/s)/m²"
+ CommonCode: P87
+ Description: Unit of volume flow cubic meters by second related to the transmission
+ surface in square metres.
+ conversion:
+ factor: 1.0
+ base_units:
+ - MTS
+ - P87
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: rhe
+ ConversionFactor: 10 m x kg⁻¹ x s
+ Symbol: rhe
+ CommonCode: P88
+ Description: Non SI-conforming unit of fluidity of dynamic viscosity.
+ conversion:
+ factor: 10.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pound-force foot per inch
+ ConversionFactor: 53,378 66 m x kg / s²
+ Symbol: lbf·ft/in
+ CommonCode: P89
+ Description: Unit for length-related rotational moment according to the Anglo-American
+ and Imperial system of units.
+ conversion:
+ factor: 53.37866
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.2'
+ Name: nine pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: P9
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pound-force inch per inch
+ ConversionFactor: 4,448 222 m x kg / s²
+ Symbol: lbf·in/in
+ CommonCode: P90
+ Description: Unit for length-related rotational moment according to the Anglo-American
+ and Imperial system of units.
+ conversion:
+ factor: 4.448222
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: perm (0 ºC)
+ ConversionFactor: 5,721 35 x 10⁻¹¹ kg/(m² x Pa x s)
+ Symbol: 'perm (0 ºC) '
+ CommonCode: P91
+ Description: Traditional unit for the ability of a material to allow the transition
+ of the steam, defined at a temperature of 0 °C as steam transmittance, where the
+ mass of one grain steam penetrates an area of one foot squared at a pressure from
+ one inch mercury per hour.
+ conversion:
+ factor: 5.72135e-11
+ base_units:
+ - Q28
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: perm (23 ºC)
+ ConversionFactor: 5,745 25 x 10⁻¹¹ kg/(m² x Pa x s)
+ Symbol: 'perm (23 ºC) '
+ CommonCode: P92
+ Description: Traditional unit for the ability of a material to allow the transition
+ of the steam, defined at a temperature of 23 °C as steam transmittance at which
+ the mass of one grain of steam penetrates an area of one square foot at a pressure
+ of one inch mercury per hour.
+ conversion:
+ factor: 5.74525e-11
+ base_units:
+ - Q28
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: byte per second
+ ConversionFactor: byte/s
+ Symbol: byte/s
+ CommonCode: P93
+ Description: Unit byte divided by the SI base unit second.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P93
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: kilobyte per second
+ ConversionFactor: 10³ byte/s
+ Symbol: kbyte/s
+ CommonCode: P94
+ Description: 1000-fold of the unit byte divided by the SI base unit second.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - P93
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: megabyte per second
+ ConversionFactor: 10⁶ byte/s
+ Symbol: Mbyte/s
+ CommonCode: P95
+ Description: 1 000 000-fold of the unit byte divided by the SI base unit second.
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - P93
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: reciprocal volt
+ ConversionFactor: m⁻² x kg⁻¹ x s³ x A
+ Symbol: 1/V
+ CommonCode: P96
+ Description: Reciprocal of the derived SI unit volt.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P96
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: reciprocal radian
+ ConversionFactor: 1/rad
+ Symbol: 1/rad
+ CommonCode: P97
+ Description: Reciprocal of the unit radian.
+ conversion:
+ factor: 1.0
+ base_units:
+ - P97
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pascal to the power sum of stoichiometric numbers
+ ConversionFactor: ''
+ Symbol: PaΣνB
+ CommonCode: P98
+ Description: Unit of the equilibrium constant on the basis of the pressure(ISO 80000-9:2009,
+ 9-35.a).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: mole per cubiv metre to the power sum of stoichiometric numbers
+ ConversionFactor: ''
+ Symbol: "(mol/m³)∑νB"
+ CommonCode: P99
+ Description: Unit of the equilibrium constant on the basis of the concentration
+ (ISO 80000-9:2009, 9-36.a).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: packet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PA
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: pascal
+ ConversionFactor: Pa
+ Symbol: Pa
+ CommonCode: PAL
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - C55
+ - PAL
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: pair inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PB
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: pad
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PD
+ Description: 'A unit of count defining the number of pads (pad: block of paper sheets
+ fastened together at one end).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: pound equivalent
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: pallet (lift)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PF
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: proof litre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PFL
+ Description: A unit of volume equal to one litre of proof spirits, or the alcohol
+ equivalent thereof. Used for measuring the strength of distilled alcoholic liquors,
+ expressed as a percentage of the alcohol content of a standard mixture at a specific
+ temperature.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: plate
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PG
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: proof gallon
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PGL
+ Description: A unit of volume equal to one gallon of proof spirits, or the alcohol
+ equivalent thereof. Used for measuring the strength of distilled alcoholic liquors,
+ expressed as a percentage of the alcohol content of a standard mixture at a specific
+ temperature.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pitch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PI
+ Description: A unit of count defining the number of characters that fit in a horizontal
+ inch.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PK
+ Description: 'Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).,Synonym: package'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: pail
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PL
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: degree Plato
+ ConversionFactor: ''
+ Symbol: "°P"
+ CommonCode: PLA
+ Description: A unit of proportion defining the sugar content of a product, especially
+ in relation to beer.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: pound percentage
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PM
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: pound net
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PN
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound per inch of length
+ ConversionFactor: 1,785 797 x 10¹ kg/m
+ Symbol: lb/in
+ CommonCode: PO
+ Description: ''
+ conversion:
+ factor: 17.85797
+ base_units:
+ - KL
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: page per inch
+ ConversionFactor: ''
+ Symbol: ppi
+ CommonCode: PQ
+ Description: A unit of quantity defining the degree of thickness of a bound publication,
+ expressed as the number of pages per inch of thickness.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: pair
+ ConversionFactor: '2'
+ Symbol: ''
+ CommonCode: PR
+ Description: 'A unit of count defining the number of pairs (pair: item described
+ by two''s).'
+ conversion:
+ factor: 2.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pound-force per square inch
+ ConversionFactor: 6,894 757 x 10³ Pa
+ Symbol: lbf/in²
+ CommonCode: PS
+ Description: ''
+ conversion:
+ factor: 6894.757
+ base_units:
+ - C55
+ - PAL
+- Status: D
+ LevelAndCategory: '2'
+ Name: pint (US)
+ ConversionFactor: 4, 731 76 x 10⁻⁴ m³
+ Symbol: pt (US)
+ CommonCode: PT
+ Description: Use liquid pint (common code PTL)
+ conversion:
+ factor: 0.000473176
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: dry pint (US)
+ ConversionFactor: 5,506 105 x 10⁻⁴ m³
+ Symbol: dry pt (US)
+ CommonCode: PTD
+ Description: ''
+ conversion:
+ factor: 0.0005506105
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pint (UK)
+ ConversionFactor: 5, 682 61 x 10⁻⁴ m³
+ Symbol: pt (UK)
+ CommonCode: PTI
+ Description: ''
+ conversion:
+ factor: 0.000568261
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: liquid pint (US)
+ ConversionFactor: 4, 731 765 x 10⁻⁴ m³
+ Symbol: liq pt (US)
+ CommonCode: PTL
+ Description: ''
+ conversion:
+ factor: 0.0004731765
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: portion
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PTN
+ Description: A quantity of allowance of food allotted to, or enough for, one person.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: tray / tray pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PU
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: half pint (US)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PV
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: pound per inch of width
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PW
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: peck dry (US)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PY
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: peck dry (UK)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: PZ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: joule per tesla
+ ConversionFactor: m² x A
+ Symbol: J/T
+ CommonCode: Q10
+ Description: Unit of the magnetic dipole moment of the molecule as derived SI unit
+ joule divided by the derived SI unit tesla.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q10
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: erlang
+ ConversionFactor: 1 E
+ Symbol: E
+ CommonCode: Q11
+ Description: Unit of the market value according to the feature of a single feature
+ as a statistical measurement of the existing utilization.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q11
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: octet
+ ConversionFactor: 8 bit
+ Symbol: o
+ CommonCode: Q12
+ Description: 'Synonym for byte: 1 octet = 8 bit = 1 byte.'
+ conversion:
+ factor: 8.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: octet per second
+ ConversionFactor: 8 bit/s
+ Symbol: o/s
+ CommonCode: Q13
+ Description: Unit octet divided by the SI base unit second.
+ conversion:
+ factor: 8.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: shannon
+ ConversionFactor: ''
+ Symbol: Sh
+ CommonCode: Q14
+ Description: Logarithmic unit for information equal to the content of decision of
+ a sentence of two mutually exclusive events, expressed as a logarithm to base
+ 2.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: hartley
+ ConversionFactor: ''
+ Symbol: Hart
+ CommonCode: Q15
+ Description: Logarithmic unit for information equal to the content of decision of
+ a sentence of ten mutually exclusive events, expressed as a logarithm to base
+ 10.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: natural unit of information
+ ConversionFactor: nat
+ Symbol: nat
+ CommonCode: Q16
+ Description: Logarithmic unit for information equal to the content of decision of
+ a sentence of ,718 281 828 459 mutually exclusive events, expressed as a logarithm
+ to base Euler value e.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q16
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: shannon per second
+ ConversionFactor: Sh/s
+ Symbol: Sh/s
+ CommonCode: Q17
+ Description: Time related logarithmic unit for information equal to the content
+ of decision of a sentence of two mutually exclusive events, expressed as a logarithm
+ to base 2.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q17
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: hartley per second
+ ConversionFactor: Hart/s
+ Symbol: Hart/s
+ CommonCode: Q18
+ Description: Time related logarithmic unit for information equal to the content
+ of decision of a sentence of ten mutually exclusive events, expressed as a logarithm
+ to base 10.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q18
+- Status: ''
+ LevelAndCategory: '3.6'
+ Name: natural unit of information per second
+ ConversionFactor: nat/s
+ Symbol: nat/s
+ CommonCode: Q19
+ Description: Time related logarithmic unit for information equal to the content
+ of decision of a sentence of 2,718 281 828 459 mutually exclusive events, expressed
+ as a logarithm to base of the Euler value e.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q19
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: second per kilogramm
+ ConversionFactor: kg⁻¹ x s
+ Symbol: s/kg
+ CommonCode: Q20
+ Description: Unit of the Einstein transition probability for spontaneous or inducing
+ emissions and absorption according to ISO 80000-7:2008, expressed as SI base unit
+ second divided by the SI base unit kilogram.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q20
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: watt square metre
+ ConversionFactor: m⁴ x kg x s⁻³
+ Symbol: W·m²
+ CommonCode: Q21
+ Description: Unit of the first radiation constants c1 = 2·p·h·c0², the value of
+ which is 3,741 771 18·10?¹6-fold that of the comparative value of the product
+ of the derived SI unit watt multiplied with the power of the SI base unit metre
+ with the exponent 2.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q21
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: second per radian cubic metre
+ ConversionFactor: m⁻³ x s x rad⁻¹
+ Symbol: 1/(Hz·rad·m³)
+ CommonCode: Q22
+ Description: Unit of the density of states as an expression of angular frequency
+ as complement of the product of hertz and radiant and the power of SI base unit
+ metre by exponent 3 .
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q22
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: weber to the power minus one
+ ConversionFactor: m⁻² x kg⁻¹ x s² x A
+ Symbol: 1/Wb
+ CommonCode: Q23
+ Description: Complement of the derived SI unit weber as unit of the Josephson constant,
+ which value is equal to the 384 597,891-fold of the reference value gigahertz
+ divided by volt.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q23
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: reciprocal inch
+ ConversionFactor: 39,370 08 m⁻¹
+ Symbol: 1/in
+ CommonCode: Q24
+ Description: Complement of the unit inch according to the Anglo-American and Imperial
+ system of units.
+ conversion:
+ factor: 39.37008
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: dioptre
+ ConversionFactor: m⁻¹
+ Symbol: dpt
+ CommonCode: Q25
+ Description: 'Unit used at the statement of relative refractive indexes of optical
+ systems as complement of the focal length with correspondence to: 1 dpt = 1/m.'
+ conversion:
+ factor: 1.0
+ base_units:
+ - C92
+ - Q25
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: one per one
+ ConversionFactor: 1/1
+ Symbol: 1/1
+ CommonCode: Q26
+ Description: Value of the quotient from two physical units of the same kind as a
+ numerator and denominator whereas the units are shortened mutually.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q26
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: newton metre per metre
+ ConversionFactor: m x kg x s⁻²
+ Symbol: N·m/m²
+ CommonCode: Q27
+ Description: Unit for length-related rotational moment as product of the derived
+ SI unit newton and the SI base unit metre divided by the SI base unit metre.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q27
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: kilogram per square metre pascal second
+ ConversionFactor: kg/(m² x Pa x s)
+ Symbol: kg/(m²·Pa·s)
+ CommonCode: Q28
+ Description: Unit for the ability of a material to allow the transition of steam.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q28
+- Status: ''
+ LevelAndCategory: 1S
+ Name: microgram per hectogram
+ ConversionFactor: 10⁻8
+ Symbol: µg/hg
+ CommonCode: Q29
+ Description: Microgram per hectogram.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: pH (potential of Hydrogen)
+ ConversionFactor: "-log10(mol/l)"
+ Symbol: pH
+ CommonCode: Q30
+ Description: The activity of the (solvated) hydrogen ion (a logarithmic measure
+ used to state the acidity or alkalinity of a chemical solution).
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q30
+- Status: ''
+ LevelAndCategory: 1S
+ Name: kilojoule per gram
+ ConversionFactor: 10⁶ J/kg
+ Symbol: kJ/g
+ CommonCode: Q31
+ Description: ''
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - J2
+- Status: ''
+ LevelAndCategory: 1S
+ Name: femtolitre
+ ConversionFactor: 10-18 m3
+ Symbol: fl
+ CommonCode: Q32
+ Description: ''
+ conversion:
+ factor: 1.0e-18
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: picolitre
+ ConversionFactor: 10-15 m3
+ Symbol: pl
+ CommonCode: Q33
+ Description: ''
+ conversion:
+ factor: 1.0e-15
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1S
+ Name: nanolitre
+ ConversionFactor: 10-12 m3
+ Symbol: nl
+ CommonCode: Q34
+ Description: ''
+ conversion:
+ factor: 1.0e-12
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: 1M
+ Name: megawatts per minute
+ ConversionFactor: 1.667 × 104 W/s
+ Symbol: MW/min
+ CommonCode: Q35
+ Description: A unit of power defining the total amount of bulk energy transferred
+ or consumer per minute.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q35
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: square metre per cubic metre
+ ConversionFactor: 1 m2/m3
+ Symbol: m2/m3
+ CommonCode: Q36
+ Description: A unit of the amount of surface area per unit volume of an object or
+ collection of objects.
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q36
+- Status: "¦"
+ LevelAndCategory: '2.0'
+ Name: Standard cubic metre per day
+ ConversionFactor: 1.15741 × 10-5 m3/s
+ Symbol: ''
+ CommonCode: Q37
+ Description: Standard cubic metre (temperature 15°C and pressure 1013.25 millibars
+ ) per day
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q37
+ - Q39
+- Status: "¦"
+ LevelAndCategory: '2.0'
+ Name: Standard cubic metre per hour
+ ConversionFactor: 2.77778 × 10-4 m3/s
+ Symbol: ''
+ CommonCode: Q38
+ Description: Standard cubic metre (temperature 15°C and pressure 1013.25 millibars
+ ) per hour
+ conversion:
+ factor: 2.0
+ base_units: []
+- Status: "¦"
+ LevelAndCategory: '2.0'
+ Name: Normalized cubic metre per day
+ ConversionFactor: 1.15741 × 10-5 m3/s
+ Symbol: ''
+ CommonCode: Q39
+ Description: Normalized cubic metre (temperature 0°C and pressure 1013.25 millibars
+ ) per day
+ conversion:
+ factor: 1.0
+ base_units:
+ - Q37
+ - Q39
+- Status: "¦"
+ LevelAndCategory: '2.0'
+ Name: Normalized cubic metre per hour
+ ConversionFactor: 2.77778 × 10-4 m3/s
+ Symbol: ''
+ CommonCode: Q40
+ Description: Normalized cubic metre (temperature 0°C and pressure 1013.25 millibars
+ ) per hour
+ conversion:
+ factor: 2.0
+ base_units: []
+- Status: "¦"
+ LevelAndCategory: '2.0'
+ Name: Joule per normalised cubic metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Q41
+ Description: Joule per normalised cubic metre (temperature 0°C and pressure 1013.25
+ millibars).
+ conversion:
+ factor: 1.0
+- Status: "¦"
+ LevelAndCategory: '2.0'
+ Name: Joule per standard cubic metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Q42
+ Description: Joule per standard cubic metre (temperature 15°C and pressure 1013.25
+ millibars).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: meal
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Q3
+ Description: 'A unit of count defining the number of meals (meal: an amount of food
+ to be eaten on a single occasion).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: page - facsimile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: QA
+ Description: A unit of count defining the number of facsimile pages.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: quarter (of a year)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: QAN
+ Description: A unit of time defining the number of quarters (3 months).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: page - hardcopy
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: QB
+ Description: 'A unit of count defining the number of hardcopy pages (hardcopy page:
+ a page rendered as printed or written output on paper, film, or other permanent
+ medium).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.7'
+ Name: quarter dozen
+ ConversionFactor: '3'
+ Symbol: ''
+ CommonCode: QD
+ Description: ''
+ conversion:
+ factor: 3.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: quarter hour
+ ConversionFactor: 900 s
+ Symbol: ''
+ CommonCode: QH
+ Description: ''
+ conversion:
+ factor: 900.0
+ base_units:
+ - H04
+ - SEC
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: quarter kilogram
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: QK
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: quire
+ ConversionFactor: ''
+ Symbol: qr
+ CommonCode: QR
+ Description: 'A unit of count for paper, expressed as the number of quires (quire:
+ a number of paper sheets, typically 25).'
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: quart (US)
+ ConversionFactor: 0,946 352 9 x 10⁻³ m³
+ Symbol: qt (US)
+ CommonCode: QT
+ Description: Use liquid quart (common code QTL)
+ conversion:
+ factor: 0.0009463529
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: dry quart (US)
+ ConversionFactor: 1,101 221 x 10⁻³ m³
+ Symbol: dry qt (US)
+ CommonCode: QTD
+ Description: ''
+ conversion:
+ factor: 0.001101221
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: quart (UK)
+ ConversionFactor: 1,136 522 5 x 10⁻³ m³
+ Symbol: qt (UK)
+ CommonCode: QTI
+ Description: ''
+ conversion:
+ factor: 0.0011365225
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: liquid quart (US)
+ ConversionFactor: 9,463 529 x 10⁻⁴ m³
+ Symbol: liq qt (US)
+ CommonCode: QTL
+ Description: ''
+ conversion:
+ factor: 0.0009463529
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: quarter (UK)
+ ConversionFactor: 12,700 59 kg
+ Symbol: Qr (UK)
+ CommonCode: QTR
+ Description: A traditional unit of weight equal to 1/4 hundredweight. In the United
+ Kingdom, one quarter equals 28 pounds.
+ conversion:
+ factor: 12.7
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pica
+ ConversionFactor: 4,217 518 x 10⁻³ m
+ Symbol: ''
+ CommonCode: R1
+ Description: 'A unit of count defining the number of picas. (pica: typographical
+ length equal to 12 points or 4.22 mm (approx.)).'
+ conversion:
+ factor: 0.004217518
+ base_units:
+ - MTR
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: calorie
+ ConversionFactor: 4,186 8 J
+ Symbol: cal
+ CommonCode: R4
+ Description: Use International Table (IT) calorie (common code D70)
+ conversion:
+ factor: 4.1868
+ base_units:
+ - JOU
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: thousand cubic metre
+ ConversionFactor: 10³m³
+ Symbol: ''
+ CommonCode: R9
+ Description: A unit of volume equal to one thousand cubic metres.
+ conversion:
+ factor: 1000.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: rack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RA
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: rod
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RD
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: ring
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RG
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: running or operating hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RH
+ Description: A unit of time defining the number of hours of operation.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: roll metric measure
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RK
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: reel
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RL
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: ream
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RM
+ Description: 'A unit of count for paper, expressed as the number of reams (ream:
+ a large quantity of paper sheets, typically 500).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: ream metric measure
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RN
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: roll
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RO
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: room
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ROM
+ Description: A unit of count defining the number of rooms.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: pound per ream
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RP
+ Description: 'A unit of mass for paper, expressed as pounds per ream. (ream: a large
+ quantity of paper, typically 500 sheets).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: revolutions per minute
+ ConversionFactor: 1,67 x 10⁻²/s
+ Symbol: r/min
+ CommonCode: RPM
+ Description: Refer ISO/TC12 SI Guide
+ conversion:
+ factor: 0.0167
+ base_units:
+ - RPS
+- Status: ''
+ LevelAndCategory: '1'
+ Name: revolutions per second
+ ConversionFactor: 1/s
+ Symbol: r/s
+ CommonCode: RPS
+ Description: Refer ISO/TC12 SI Guide
+ conversion:
+ factor: 1.0
+ base_units:
+ - RPS
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: reset
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RS
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: revenue ton mile
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RT
+ Description: 'A unit of information typically used for billing purposes, expressed
+ as the number of revenue tons (revenue ton: either a metric ton or a cubic metres,
+ whichever is the larger), moved over a distance of one mile.'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: run
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: RU
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square foot per second
+ ConversionFactor: 0,092 903 04 m²/s
+ Symbol: ft²/s
+ CommonCode: S3
+ Description: 'Synonym: foot squared per second'
+ conversion:
+ factor: 0.09290304
+ base_units:
+ - S4
+- Status: ''
+ LevelAndCategory: '1'
+ Name: square metre per second
+ ConversionFactor: m²/s
+ Symbol: m²/s
+ CommonCode: S4
+ Description: 'Synonym: metre squared per second (square metres/second US)'
+ conversion:
+ factor: 1.0
+ base_units:
+ - S4
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: sixty fourths of an inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: S5
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: session
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: S6
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: storage unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: S7
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: standard advertising unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: S8
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: sack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SA
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: half year (6 months)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SAN
+ Description: A unit of time defining the number of half years (6 months).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: score
+ ConversionFactor: '20'
+ Symbol: ''
+ CommonCode: SCO
+ Description: A unit of count defining the number of units in multiples of 20.
+ conversion:
+ factor: 20.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: scruple
+ ConversionFactor: 1,295 982 g
+ Symbol: ''
+ CommonCode: SCR
+ Description: ''
+ conversion:
+ factor: 1.295982
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: solid pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SD
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: section
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: second [unit of time]
+ ConversionFactor: s
+ Symbol: s
+ CommonCode: SEC
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: set
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SET
+ Description: 'A unit of count defining the number of sets (set: a number of objects
+ grouped together).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: segment
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SG
+ Description: A unit of information equal to 64000 bytes.
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '3.4'
+ Name: shipping ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SHT
+ Description: A unit of mass defining the number of tons for shipping.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: siemens
+ ConversionFactor: A/V
+ Symbol: S
+ CommonCode: SIE
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - SIE
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: split tank truck
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SK
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: slipsheet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SL
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: "¦"
+ LevelAndCategory: '2'
+ Name: Standard cubic metre
+ ConversionFactor: m3
+ Symbol: ''
+ CommonCode: SM3
+ Description: Standard cubic metre (temperature 15°C and pressure 1013.25 millibars
+ )
+ conversion:
+ factor: 1.0
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '2'
+ Name: mile (statute mile)
+ ConversionFactor: 1 609,344 m
+ Symbol: mile
+ CommonCode: SMI
+ Description: ''
+ conversion:
+ factor: 1609.344
+ base_units:
+ - MTR
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: square rod
+ ConversionFactor: 25,292 9 m²
+ Symbol: rd²
+ CommonCode: SN
+ Description: ''
+ conversion:
+ factor: 25.2929
+ base_units:
+ - MTK
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: spool
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SO
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: shelf package
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SP
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: square
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SQ
+ Description: 'A unit of count defining the number of squares (square: rectangular
+ shape).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: square, roofing
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SQR
+ Description: A unit of count defining the number of squares of roofing materials,
+ measured in multiples of 100 square feet.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: strip
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SR
+ Description: 'A unit of count defining the number of strips (strip: long narrow
+ piece of an object).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: sheet metric measure
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SS
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: short standard (7200 matches)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SST
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: sheet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ST
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: stick
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: STC
+ Description: 'A unit of count defining the number of sticks (stick: slender and
+ often cylindrical piece of a substance).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: stone (UK)
+ ConversionFactor: 6,350 293 kg
+ Symbol: st
+ CommonCode: STI
+ Description: ''
+ conversion:
+ factor: 6.350293
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: stick, cigarette
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: STK
+ Description: A unit of count defining the number of cigarettes in the smallest unit
+ for stock-taking and/or duty computation.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: standard litre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: STL
+ Description: A unit of volume defining the number of litres of a product at a temperature
+ of 15 degrees Celsius, especially in relation to hydrocarbon oils.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: ton (US) or short ton (UK/US)
+ ConversionFactor: 0,907184 7 x 10³ kg
+ Symbol: ton (US)
+ CommonCode: STN
+ Description: 'Synonym: net ton (2000 lb)'
+ conversion:
+ factor: 907.1847
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: straw
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: STW
+ Description: 'A unit of count defining the number of straws (straw: a slender tube
+ used for sucking up liquids).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: skid
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SV
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: skein
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SW
+ Description: 'A unit of count defining the number of skeins (skein: a loosely-coiled
+ bundle of yarn or thread).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: shipment
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SX
+ Description: 'A unit of count defining the number of shipments (shipment: an amount
+ of goods shipped or transported).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: syringe
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: SYR
+ Description: 'A unit of count defining the number of syringes (syringe: a small
+ device for pumping, spraying and/or injecting liquids through a small aperture).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: telecommunication line in service
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: T0
+ Description: A unit of count defining the number of lines in service.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand pound gross
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: T1
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: thousand piece
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: T3
+ Description: 'A unit of count defining the number of pieces in multiples of 1000
+ (piece: a single item, article or exemplar).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand bag
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: T4
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand casing
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: T5
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand gallon (US)
+ ConversionFactor: 3,785 412 m³
+ Symbol: ''
+ CommonCode: T6
+ Description: ''
+ conversion:
+ factor: 3.785412
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand impression
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: T7
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand linear inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: T8
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: tenth cubic foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TA
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1M
+ Name: kiloampere hour (thousand ampere hour)
+ ConversionFactor: 3,6 x 10⁶ C
+ Symbol: kA·h
+ CommonCode: TAH
+ Description: ''
+ conversion:
+ factor: 3600000.0
+ base_units:
+ - A8
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: total acid number
+ ConversionFactor: 'mg KOH/g '
+ Symbol: ''
+ CommonCode: TAN
+ Description: A unit of chemistry defining the amount of potassium hydroxide (KOH)
+ in milligrams that is needed to neutralize the acids in one gram of oil. It is
+ an important quality measurement of crude oil.
+ conversion:
+ factor: 1.0
+ base_units:
+ - TAN
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: truckload
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TC
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: therm
+ ConversionFactor: 10⁵ x 1 055,056 J
+ Symbol: ''
+ CommonCode: TD
+ Description: ''
+ conversion:
+ factor: 100000.0
+ base_units: []
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: tote
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TE
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: ten square yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TF
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: thousand square inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TI
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: metric ton, including container
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TIC
+ Description: A unit of mass defining the number of metric tons of a product, including
+ its container.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: metric ton, including inner packaging
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TIP
+ Description: A unit of mass defining the number of metric tons of a product, including
+ its inner packaging materials.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand square centimetre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TJ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: tank, rectangular
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TK
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.4'
+ Name: tonne kilometre
+ ConversionFactor: 10⁶ kg x m
+ Symbol: t·km
+ CommonCode: TKM
+ Description: 'A unit of information typically used for billing purposes, expressed
+ as the number of tonnes (metric tons) moved over a distance of one kilometre. '
+ conversion:
+ factor: 1000000.0
+ base_units:
+ - M94
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand foot (linear)
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TL
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: kilogram of imported meat, less offal
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TMS
+ Description: A unit of mass equal to one thousand grams of imported meat, disregarding
+ less valuable by-products such as the entrails.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: tin
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TN
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: 1S
+ Name: tonne (metric ton)
+ ConversionFactor: 10³ kg
+ Symbol: t
+ CommonCode: TNE
+ Description: 'Synonym: metric ton'
+ conversion:
+ factor: 1000.0
+ base_units:
+ - KGM
+- Status: ''
+ LevelAndCategory: '3.2'
+ Name: ten pack
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TP
+ Description: A unit of count defining the number of items in multiples of 10.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: teeth per inch
+ ConversionFactor: 0.0254 /m
+ Symbol: ''
+ CommonCode: TPI
+ Description: The number of teeth per inch.
+ conversion:
+ factor: 0.0
+ base_units: []
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: ten pair
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TPR
+ Description: 'A unit of count defining the number of pairs in multiples of 10 (pair:
+ item described by two''s).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TQ
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.8'
+ Name: thousand cubic metre per day
+ ConversionFactor: 1,157 41 x 10⁻² m³/s
+ Symbol: km³/d
+ CommonCode: TQD
+ Description: A unit of volume equal to one thousand cubic metres per day.
+ conversion:
+ factor: 0.0115741
+ base_units:
+ - MQS
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: ten square foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TR
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: trillion (EUR)
+ ConversionFactor: 10¹⁸
+ Symbol: ''
+ CommonCode: TRL
+ Description: ''
+ conversion:
+ factor: 1.0e+18
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand square foot
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TS
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: tonne of substance 90 % dry
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TSD
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.1'
+ Name: ton of steam per hour
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TSH
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: ten set
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TST
+ Description: 'A unit of count defining the number of sets in multiples of 10 (set:
+ a number of objects grouped together).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand linear metre
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TT
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: ten thousand sticks
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TTS
+ Description: 'A unit of count defining the number of sticks in multiples of 10000
+ (stick: slender and often cylindrical piece of a substance).'
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: tube
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TU
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand kilogram
+ ConversionFactor: 10³kg
+ Symbol: ''
+ CommonCode: TV
+ Description: ''
+ conversion:
+ factor: 1000.0
+ base_units:
+ - KGM
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: thousand sheet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TW
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: tank, cylindrical
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: TY
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: treatment
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: U1
+ Description: 'A unit of count defining the number of treatments (treatment: subjection
+ to the action of a chemical, physical or biological agent).'
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: tablet
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: U2
+ Description: 'A unit of count defining the number of tablets (tablet: a small flat
+ or compressed solid object).'
+ conversion:
+ factor: 1.0
+- Status: D
+ LevelAndCategory: '2'
+ Name: torr
+ ConversionFactor: 133,322 4 Pa
+ Symbol: Torr
+ CommonCode: UA
+ Description: ''
+ conversion:
+ factor: 133.3224
+ base_units:
+ - C55
+ - PAL
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: telecommunication line in service average
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: UB
+ Description: A unit of count defining the average number of lines in service.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: telecommunication port
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: UC
+ Description: A unit of count defining the number of network access ports.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: tenth minute
+ ConversionFactor: 6 s
+ Symbol: ''
+ CommonCode: UD
+ Description: ''
+ conversion:
+ factor: 6.0
+ base_units:
+ - H04
+ - SEC
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: tenth hour
+ ConversionFactor: 360 s
+ Symbol: ''
+ CommonCode: UE
+ Description: ''
+ conversion:
+ factor: 360.0
+ base_units:
+ - H04
+ - SEC
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: usage per telecommunication line average
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: UF
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: ten thousand yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: UH
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: million unit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: UM
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: volt - ampere per kilogram
+ ConversionFactor: V x A / kg
+ Symbol: V·A / kg
+ CommonCode: VA
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - VA
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: vial
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: VI
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: volt
+ ConversionFactor: V
+ Symbol: V
+ CommonCode: VLT
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - 2G
+ - 2H
+ - VLT
+- Status: ''
+ LevelAndCategory: '3.7'
+ Name: percent volume
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: VP
+ Description: A measure of concentration, typically expressed as the percentage volume
+ of a solute in a solution.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: bulk
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: VQ
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: visit
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: VS
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: wet kilo
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: W2
+ Description: A unit of mass defining the number of kilograms of a product, including
+ the water content of the product.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: two week
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: W4
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: watt per kilogram
+ ConversionFactor: 1 W/kg
+ Symbol: W/kg
+ CommonCode: WA
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - WA
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: wet pound
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WB
+ Description: A unit of mass defining the number of pounds of a material, including
+ the water content of the material.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: cord
+ ConversionFactor: 3,63 m³
+ Symbol: ''
+ CommonCode: WCD
+ Description: A unit of volume used for measuring lumber. One board foot equals 1/12
+ of a cubic foot.
+ conversion:
+ factor: 3.63
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: wet ton
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WE
+ Description: A unit of mass defining the number of tons of a material, including
+ the water content of the material.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: weber
+ ConversionFactor: Wb
+ Symbol: Wb
+ CommonCode: WEB
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - WEB
+- Status: ''
+ LevelAndCategory: '2'
+ Name: week
+ ConversionFactor: 6,048 x 10⁵ s
+ Symbol: wk
+ CommonCode: WEE
+ Description: ''
+ conversion:
+ factor: 604800.0
+ base_units:
+ - H04
+ - SEC
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: wine gallon
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WG
+ Description: A unit of volume equal to 231 cubic inches.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: wheel
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WH
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt hour
+ ConversionFactor: 3,6 x 10³ J
+ Symbol: W·h
+ CommonCode: WHR
+ Description: ''
+ conversion:
+ factor: 3600.0
+ base_units:
+ - JOU
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: weight per square inch
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WI
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.1'
+ Name: working month
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WM
+ Description: A unit of time defining the number of working months.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: wrap
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WR
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: standard
+ ConversionFactor: 4,672 m³
+ Symbol: std
+ CommonCode: WSD
+ Description: 'A unit of volume of finished lumber equal to 165 cubic feet.,Synonym:
+ standard cubic foot'
+ conversion:
+ factor: 4.672
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: ''
+ LevelAndCategory: '1'
+ Name: watt
+ ConversionFactor: W
+ Symbol: W
+ CommonCode: WTT
+ Description: ''
+ conversion:
+ factor: 1.0
+ base_units:
+ - D46
+ - P14
+ - WTT
+- Status: D
+ LevelAndCategory: '3.1'
+ Name: millilitre of water
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: WW
+ Description: A unit of volume equal to the number of millilitres of water.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: Gunter's chain
+ ConversionFactor: 20,116 8 m
+ Symbol: ch (UK)
+ CommonCode: X1
+ Description: A unit of distance used or formerly used by British surveyors.
+ conversion:
+ factor: 20.1168
+ base_units:
+ - MTR
+- Status: ''
+ LevelAndCategory: '2'
+ Name: square yard
+ ConversionFactor: 8,361 274 x 10⁻¹ m²
+ Symbol: yd²
+ CommonCode: YDK
+ Description: ''
+ conversion:
+ factor: 0.8361274
+ base_units:
+ - MTK
+- Status: ''
+ LevelAndCategory: '2'
+ Name: cubic yard
+ ConversionFactor: 0,764 555 m³
+ Symbol: yd³
+ CommonCode: YDQ
+ Description: ''
+ conversion:
+ factor: 0.764555
+ base_units:
+ - D40
+ - G26
+ - K6
+ - MTQ
+ - NM3
+ - SM3
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: hundred linear yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: YL
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '2'
+ Name: yard
+ ConversionFactor: 0,914 4 m
+ Symbol: yd
+ CommonCode: YRD
+ Description: ''
+ conversion:
+ factor: 0.9144
+ base_units:
+ - MTR
+- Status: X
+ LevelAndCategory: '3.8'
+ Name: ten yard
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: YT
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.4'
+ Name: lift van
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z1
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: hanging container
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z11
+ Description: A unit of count defining the number of hanging containers.
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: chest
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z2
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: cask
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z3
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.3'
+ Name: hogshead
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z4
+ Description: Use UN/ECE Recommendation 21 (refer to Note 2 in the spreadsheet introduction,
+ 1st sheet).
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: lug
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z5
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.5'
+ Name: conference point
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z6
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: X
+ LevelAndCategory: '3.9'
+ Name: newspage agate line
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: Z8
+ Description: ''
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.5'
+ Name: page
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ZP
+ Description: A unit of count defining the number of pages.
+ conversion:
+ factor: 1.0
+- Status: ''
+ LevelAndCategory: '3.9'
+ Name: mutually defined
+ ConversionFactor: ''
+ Symbol: ''
+ CommonCode: ZZ
+ Description: A unit of measure as agreed in common between two or more parties.
+ conversion:
+ factor: 1.0
diff --git a/config/units-of-measure/un-ece-20.yml b/config/units-of-measure/un-ece-20.yml
new file mode 100644
index 00000000..71695a96
--- /dev/null
+++ b/config/units-of-measure/un-ece-20.yml
@@ -0,0 +1,15383 @@
+# downloaded json from https://github.com/datasets/unece-units-of-measure and manually converted to yaml using
+# `ruby -ryaml -rjson -e 'puts YAML.dump(JSON.parse(STDIN.read))' 10 kpa
+- Code: WH
+ Description:
+ Name: Intermediate bulk container, aluminium, pressurised > 10 kpa
+- Code: WJ
+ Description:
+ Name: Intermediate bulk container, metal, pressure 10 kpa
+- Code: WK
+ Description:
+ Name: Intermediate bulk container, steel, liquid
+- Code: WL
+ Description:
+ Name: Intermediate bulk container, aluminium, liquid
+- Code: WM
+ Description:
+ Name: Intermediate bulk container, metal, liquid
+- Code: WN
+ Description:
+ Name: Intermediate bulk container, woven plastic, without coat/liner
+- Code: WP
+ Description:
+ Name: Intermediate bulk container, woven plastic, coated
+- Code: WQ
+ Description:
+ Name: Intermediate bulk container, woven plastic, with liner
+- Code: WR
+ Description:
+ Name: Intermediate bulk container, woven plastic, coated and liner
+- Code: WS
+ Description:
+ Name: Intermediate bulk container, plastic film
+- Code: WT
+ Description:
+ Name: Intermediate bulk container, textile with out coat/liner
+- Code: WU
+ Description:
+ Name: Intermediate bulk container, natural wood, with inner liner
+- Code: WV
+ Description:
+ Name: Intermediate bulk container, textile, coated
+- Code: WW
+ Description:
+ Name: Intermediate bulk container, textile, with liner
+- Code: WX
+ Description:
+ Name: Intermediate bulk container, textile, coated and liner
+- Code: WY
+ Description:
+ Name: Intermediate bulk container, plywood, with inner liner
+- Code: WZ
+ Description:
+ Name: Intermediate bulk container, reconstituted wood, with inner liner
+- Code: XA
+ Description:
+ Name: Bag, woven plastic, without inner coat/liner
+- Code: XB
+ Description:
+ Name: Bag, woven plastic, sift proof
+- Code: XC
+ Description:
+ Name: Bag, woven plastic, water resistant
+- Code: XD
+ Description:
+ Name: Bag, plastics film
+- Code: XF
+ Description:
+ Name: Bag, textile, without inner coat/liner
+- Code: XG
+ Description:
+ Name: Bag, textile, sift proof
+- Code: XH
+ Description:
+ Name: Bag, textile, water resistant
+- Code: XJ
+ Description:
+ Name: Bag, paper, multi-wall
+- Code: XK
+ Description:
+ Name: Bag, paper, multi-wall, water resistant
+- Code: YA
+ Description:
+ Name: Composite packaging, plastic receptacle in steel drum
+- Code: YB
+ Description:
+ Name: Composite packaging, plastic receptacle in steel crate box
+- Code: YC
+ Description:
+ Name: Composite packaging, plastic receptacle in aluminium drum
+- Code: YD
+ Description:
+ Name: Composite packaging, plastic receptacle in aluminium crate
+- Code: YF
+ Description:
+ Name: Composite packaging, plastic receptacle in wooden box
+- Code: YG
+ Description:
+ Name: Composite packaging, plastic receptacle in plywood drum
+- Code: YH
+ Description:
+ Name: Composite packaging, plastic receptacle in plywood box
+- Code: YJ
+ Description:
+ Name: Composite packaging, plastic receptacle in fibre drum
+- Code: YK
+ Description:
+ Name: Composite packaging, plastic receptacle in fibreboard box
+- Code: YL
+ Description:
+ Name: Composite packaging, plastic receptacle in plastic drum
+- Code: YM
+ Description:
+ Name: Composite packaging, plastic receptacle in solid plastic box
+- Code: YN
+ Description:
+ Name: Composite packaging, glass receptacle in steel drum
+- Code: YP
+ Description:
+ Name: Composite packaging, glass receptacle in steel crate box
+- Code: YQ
+ Description:
+ Name: Composite packaging, glass receptacle in aluminium drum
+- Code: YR
+ Description:
+ Name: Composite packaging, glass receptacle in aluminium crate
+- Code: YS
+ Description:
+ Name: Composite packaging, glass receptacle in wooden box
+- Code: YT
+ Description:
+ Name: Composite packaging, glass receptacle in plywood drum
+- Code: YV
+ Description:
+ Name: Composite packaging, glass receptacle in wickerwork hamper
+- Code: YW
+ Description:
+ Name: Composite packaging, glass receptacle in fibre drum
+- Code: YX
+ Description:
+ Name: Composite packaging, glass receptacle in fibreboard box
+- Code: YY
+ Description:
+ Name: Composite packaging, glass receptacle in expandable plastic pack
+- Code: YZ
+ Description:
+ Name: Composite packaging, glass receptacle in solid plastic pack
+- Code: ZA
+ Description:
+ Name: Intermediate bulk container, paper, multi-wall
+- Code: ZB
+ Description:
+ Name: Bag, large
+- Code: ZC
+ Description:
+ Name: Intermediate bulk container, paper, multi-wall, water resistant
+- Code: ZD
+ Description:
+ Name: Intermediate bulk container, rigid plastic, with structural equipment, solids
+- Code: ZF
+ Description:
+ Name: Intermediate bulk container, rigid plastic, freestanding, solids
+- Code: ZG
+ Description:
+ Name: Intermediate bulk container, rigid plastic, with structural equipment, pressurised
+- Code: ZH
+ Description:
+ Name: Intermediate bulk container, rigid plastic, freestanding, pressurised
+- Code: ZJ
+ Description:
+ Name: Intermediate bulk container, rigid plastic, with structural equipment, liquids
+- Code: ZK
+ Description:
+ Name: Intermediate bulk container, rigid plastic, freestanding, liquids
+- Code: ZL
+ Description:
+ Name: Intermediate bulk container, composite, rigid plastic, solids
+- Code: ZM
+ Description:
+ Name: Intermediate bulk container, composite, flexible plastic, solids
+- Code: ZN
+ Description:
+ Name: Intermediate bulk container, composite, rigid plastic, pressurised
+- Code: ZP
+ Description:
+ Name: Intermediate bulk container, composite, flexible plastic, pressurised
+- Code: ZQ
+ Description:
+ Name: Intermediate bulk container, composite, rigid plastic, liquids
+- Code: ZR
+ Description:
+ Name: Intermediate bulk container, composite, flexible plastic, liquids
+- Code: ZS
+ Description:
+ Name: Intermediate bulk container, composite
+- Code: ZT
+ Description:
+ Name: Intermediate bulk container, fibreboard
+- Code: ZU
+ Description:
+ Name: Intermediate bulk container, flexible
+- Code: ZV
+ Description:
+ Name: Intermediate bulk container, metal, other than steel
+- Code: ZW
+ Description:
+ Name: Intermediate bulk container, natural wood
+- Code: ZX
+ Description:
+ Name: Intermediate bulk container, plywood
+- Code: ZY
+ Description:
+ Name: Intermediate bulk container, reconstituted wood
+- Code: ZZ
+ Description:
+ Name: Mutually defined
+- Code: '13'
+ Description:
+ Name: Flexible IBC for solids, filled or discharged by gravity (liquids and solids
+ under pressure [>0.1 bar] are not permitted)
+- Code: 13H1
+ Description:
+ Name: Woven PP fabric without coating or inner liner
+- Code: 13H2
+ Description:
+ Name: Woven PP fabric, coated
+- Code: 13H3
+ Description:
+ Name: Woven PP fabric with inner liner
+- Code: 13H4
+ Description:
+ Name: Woven PP fabric, coated and with inner liner
diff --git a/db/migrate/001_create_users.rb b/db/migrate/001_create_users.rb
index ab4a560a..3bdd0952 100644
--- a/db/migrate/001_create_users.rb
+++ b/db/migrate/001_create_users.rb
@@ -4,30 +4,30 @@ 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..cffa19cc 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 @@ def self.up
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 @@ def self.up
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 @@ def self.up
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 32488794..68ed0915 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 @@ def self.up
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..0106fc32 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,76 @@ def self.up
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 @@ def self.up
# 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 dc664347..fdbf312d 100644
--- a/db/migrate/021_remove_table_article_prices.rb
+++ b/db/migrate/021_remove_table_article_prices.rb
@@ -1,71 +1,71 @@
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_attributes!(:clear_price => price.clear_price,
- :gross_price => price.gross_price,
- :tax => price.tax,
- :refund => price.refund,
- :unit_quantity => price.unit_quantity,
- :order_number => price.order_number,
- :updated_at => price.updated_on,
- :created_at => price.updated_on)
+ article.update_attributes!(clear_price: price.clear_price,
+ gross_price: price.gross_price,
+ tax: price.tax,
+ refund: price.refund,
+ unit_quantity: price.unit_quantity,
+ order_number: price.order_number,
+ updated_at: price.updated_on,
+ 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 @@ def self.up
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 4b8aa9ab..c7adfc87 100644
--- a/db/migrate/20090120184410_road_to_version_three.rb
+++ b/db/migrate/20090120184410_road_to_version_three.rb
@@ -10,10 +10,10 @@ def self.up
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 @@ def self.up
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 @@ def self.up
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
@@ -47,8 +47,8 @@ def self.up
Ordergroup.all.each do |ordergroup|
contact = ordergroup.users.first
if contact
- ordergroup.update_attributes :contact_person => contact.name,
- :contact_phone => contact.phone, :contact_address => contact.address
+ ordergroup.update_attributes contact_person: contact.name,
+ contact_phone: contact.phone, contact_address: contact.address
end
end
remove_column :users, :address
@@ -58,15 +58,18 @@ def self.up
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
@@ -79,9 +82,9 @@ def self.up
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
@@ -108,41 +111,41 @@ def self.up
# == 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
@@ -159,6 +162,5 @@ def self.up
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 @@ def self.up
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 b780f3e9..f0bea5e2 100644
--- a/db/migrate/20130622095040_move_weekly_tasks.rb
+++ b/db/migrate/20130622095040_move_weekly_tasks.rb
@@ -15,21 +15,21 @@ def up
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_attributes 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_attributes 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 @@ def self.up
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 cd173e45..2d0e3c56 100644
--- a/db/migrate/20130718183101_migrate_user_settings.rb
+++ b/db/migrate/20130718183101_migrate_user_settings.rb
@@ -16,7 +16,7 @@ def up
begin
user = type.constantize.find(id)
rescue ActiveRecord::RecordNotFound
- Rails.logger.debug "Can't find configurable object with type: #{type.inspect}, id: #{id.inspect}"
+ Rails.logger.debug { "Can't find configurable object with type: #{type.inspect}, id: #{id.inspect}" }
next
end
@@ -27,7 +27,7 @@ def up
# prepare value
value = YAML.load(old_setting.value)
- value = value.nil? ? false : value
+ value = false if value.nil?
# set the settings_attributes (thanks to settings.merge! we can set them one by one)
user.settings_attributes = {
@@ -46,8 +46,7 @@ def up
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 @@ def up
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 @@ def change
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/20171002000000_create_financial_links.rb b/db/migrate/20171002000000_create_financial_links.rb
index 5f42ec2e..c906869b 100644
--- a/db/migrate/20171002000000_create_financial_links.rb
+++ b/db/migrate/20171002000000_create_financial_links.rb
@@ -4,7 +4,9 @@ def change
t.text :note
end
- add_column :financial_transactions, :financial_link_id, :integer, index: true
- add_column :invoices, :financial_link_id, :integer, index: true
+ add_column :financial_transactions, :financial_link_id, :integer
+ add_index :financial_transactions, :financial_link_id
+ add_column :invoices, :financial_link_id, :integer
+ add_index :invoices, :financial_link_id
end
end
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 @@ def change
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 @@ def change
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 @@ def up
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..650274b4 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,7 @@ def change
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/20221026102300_alter_articles_add_versioning.rb b/db/migrate/20221026102300_alter_articles_add_versioning.rb
new file mode 100644
index 00000000..7914c8cd
--- /dev/null
+++ b/db/migrate/20221026102300_alter_articles_add_versioning.rb
@@ -0,0 +1,217 @@
+class AlterArticlesAddVersioning < ActiveRecord::Migration[5.2]
+ def up
+ rename_table :article_prices, :article_versions
+ rename_column :order_articles, :article_price_id, :article_version_id
+
+ change_table :article_versions do |t|
+ t.string :name, default: '', null: false
+ t.integer :article_category_id, default: 0, null: false
+ t.string :unit, default: ''
+ t.string :note
+ t.boolean :availability, default: true, null: false
+ t.string :manufacturer
+ t.string :origin
+ t.string :order_number
+ t.datetime :updated_at
+ end
+
+ # copy all article fields into article_versions
+ articles = select_all('SELECT article_versions.id AS article_version_id, articles.* FROM article_versions JOIN articles ON articles.id = article_versions.article_id')
+ articles.each do |article|
+ update(%(
+ UPDATE article_versions SET
+ name = #{quote article['name']},
+ article_category_id = #{quote article['article_category_id']},
+ unit = #{quote article['unit']},
+ note = #{quote article['note']},
+ availability = #{quote article['availability']},
+ manufacturer = #{quote article['manufacturer']},
+ origin = #{quote article['origin']},
+ order_number = #{quote article['order_number']},
+ updated_at = #{quote article['updated_at']}
+ WHERE id = #{quote article['article_version_id']}
+ ))
+ end
+
+ remove_index :articles, %i[name supplier_id]
+
+ # drop article columns (now superfluous as they exist in article_versions):
+ change_table :articles do |t|
+ t.remove :name
+ t.remove :article_category_id
+ t.remove :unit
+ t.remove :note
+ t.remove :availability
+ t.remove :manufacturer
+ t.remove :origin
+ t.remove :order_number
+ t.remove :updated_at
+ t.remove :price
+ t.remove :tax
+ t.remove :deposit
+ t.remove :unit_quantity
+ end
+
+ # remove order_articles' reference to articles (reference now always goes through article_versions):
+ articles = select_all(%(
+ SELECT articles.id AS article_id, article_versions.id AS article_version_id
+ FROM articles
+ JOIN article_versions ON article_versions.article_id = articles.id
+ JOIN order_articles ON order_articles.article_id = articles.id
+ WHERE order_articles.article_version_id IS NULL
+ GROUP BY article_versions.article_id
+ ORDER BY article_versions.created_at DESC
+ ))
+
+ articles.each do |article|
+ update(%(
+ UPDATE order_articles
+ SET article_version_id = #{quote article['article_version_id']}
+ WHERE order_articles.article_version_id IS NULL
+ AND order_articles.article_id = #{quote article['article_id']}
+ ))
+ end
+
+ # Remove orphaned order articles (db inconsistencies due to lack of foreign key constraints):
+ delete('DELETE FROM order_articles WHERE order_articles.article_version_id IS NULL')
+
+ # De-duplicate article version (db inconsistencies due to lack of unique key for created_at and article_id):
+ duplicate_article_versions = select_all(%{
+ SELECT article_id, created_at
+ FROM article_versions
+ GROUP BY article_id, created_at
+ HAVING COUNT(*) > 1
+ })
+
+ duplicate_article_versions.each do |duplicate_article_version|
+ article_versions = select_all(%(
+ SELECT id
+ FROM article_versions
+ WHERE article_id = #{quote duplicate_article_version['article_id']}
+ AND created_at = #{quote duplicate_article_version['created_at']}
+ ))
+
+ latest_version = article_versions.last
+ article_versions[0..-2].each do |obsolete_version|
+ update("UPDATE order_articles SET article_version_id = #{quote latest_version['id']} WHERE article_version_id = #{quote obsolete_version['id']}")
+ delete("DELETE FROM article_versions WHERE id = #{quote obsolete_version['id']}")
+ end
+ end
+
+ remove_index :order_articles, %i[order_id article_id]
+ remove_column :order_articles, :article_id
+ change_column_null :order_articles, :article_version_id, false
+ add_index :order_articles, %i[order_id article_version_id], unique: true
+ add_index :order_articles, :article_version_id
+ remove_index :article_versions, :article_id
+ add_index :article_versions, %i[article_id created_at], unique: true
+ add_index :article_versions, [:article_category_id]
+ end
+
+ def down
+ rename_table :article_versions, :article_prices
+ rename_column :order_articles, :article_version_id, :article_price_id
+
+ remove_index :order_articles, %i[order_id article_price_id]
+ remove_index :order_articles, :article_price_id
+ remove_index :article_prices, %i[article_id created_at]
+ remove_index :article_prices, [:article_category_id]
+
+ add_column :order_articles, :article_id, :integer
+ change_column_null :order_articles, :article_price_id, true
+
+ change_table :articles do |t|
+ t.string :name, default: '', null: false
+ t.integer :article_category_id, default: 0, null: false
+ t.string :unit, default: ''
+ t.string :note
+ t.boolean :availability, default: true, null: false
+ t.string :manufacturer
+ t.string :origin
+ t.string :order_number
+ t.datetime :updated_at
+ t.decimal :price, precision: 8, scale: 2
+ t.float :tax
+ t.decimal :deposit, precision: 8, scale: 2, default: '0.0'
+ t.integer :unit_quantity, null: false, default: 0
+ end
+
+ article_prices = select_all(%{
+ SELECT article_prices.*
+ FROM article_prices
+ JOIN (
+ SELECT article_id, MAX(created_at) AS max_created_at
+ FROM article_prices
+ GROUP BY article_id
+ ) AS latest_article_prices
+ ON latest_article_prices.article_id = article_prices.article_id
+ AND latest_article_prices.max_created_at = article_prices.created_at
+ })
+ article_prices.each do |article_price|
+ update(%(
+ UPDATE articles SET
+ name = #{quote article_price['name']},
+ article_category_id = #{quote article_price['article_category_id']},
+ unit = #{quote article_price['unit']},
+ note = #{quote article_price['note']},
+ availability = #{quote article_price['availability']},
+ manufacturer = #{quote article_price['manufacturer']},
+ origin = #{quote article_price['origin']},
+ order_number = #{quote article_price['order_number']},
+ updated_at = #{quote article_price['updated_at']},
+ type = #{quote article_price['type']},
+ price = #{quote article_price['price']},
+ tax = #{quote article_price['tax']},
+ deposit = #{quote article_price['deposit']},
+ unit_quantity = #{quote article_price['unit_quantity']}
+ WHERE id = #{quote article_price['article_id']}
+ ))
+ end
+
+ order_articles = select_all(%(
+ SELECT order_articles.id, article_prices.article_id
+ FROM order_articles
+ JOIN article_prices ON article_prices.id = order_articles.article_price_id
+ ))
+
+ order_articles.each do |order_article|
+ update(%(
+ UPDATE order_articles
+ SET article_id = #{quote order_article['article_id']}
+ WHERE id = #{quote order_article['id']}
+ ))
+ end
+
+ update(%{
+ UPDATE order_articles
+ SET article_price_id = NULL
+ WHERE order_id IN (SELECT id FROM orders WHERE state = #{quote 'open'})
+ })
+
+ change_table :article_prices do |t|
+ t.remove :name
+ t.remove :article_category_id
+ t.remove :unit
+ t.remove :note
+ t.remove :availability
+ t.remove :manufacturer
+ t.remove :origin
+ t.remove :order_number
+ t.remove :updated_at
+ end
+
+ change_column_default :articles, :unit_quantity, nil
+ change_column_null :order_articles, :article_id, false
+ add_index :order_articles, %i[order_id article_id], unique: true
+ add_index :article_prices, :article_id
+ add_index :articles, %i[name supplier_id]
+ end
+
+ protected
+
+ # We cannot use quote out of context (as it wouldn't relate to the current DB syntax),
+ # but using Article's db connection by default should be fine
+ def quote(value)
+ Article.connection.quote(value)
+ end
+end
diff --git a/db/migrate/20221026102301_alter_articles_add_more_unit_logic.rb b/db/migrate/20221026102301_alter_articles_add_more_unit_logic.rb
new file mode 100644
index 00000000..35740693
--- /dev/null
+++ b/db/migrate/20221026102301_alter_articles_add_more_unit_logic.rb
@@ -0,0 +1,106 @@
+class AlterArticlesAddMoreUnitLogic < ActiveRecord::Migration[5.2]
+ def up
+ change_table :article_versions do |t|
+ t.column :supplier_order_unit, :string, length: 3
+ t.column :price_unit, :string, length: 3
+ t.column :billing_unit, :string, length: 3
+ t.column :group_order_unit, :string, length: 3
+ t.column :group_order_granularity, :decimal, precision: 8, scale: 3, null: false, default: 1
+ t.column :minimum_order_quantity, :float
+ t.change :unit, :string, null: true, default: nil
+ end
+
+ create_table :article_unit_ratios do |t|
+ t.references :article_version, null: false
+
+ t.column :sort, :integer, null: false, index: true
+ t.column :quantity, :decimal, precision: 8, scale: 3, null: false
+ t.column :unit, :string, length: 3
+ end
+
+ article_versions = select_all('SELECT id, unit, unit_quantity, price FROM article_versions WHERE unit_quantity > 1 AND NOT unit IS NULL')
+ article_versions.each do |article_version|
+ insert(%{
+ INSERT INTO article_unit_ratios (article_version_id, sort, quantity, unit)
+ VALUES (
+ #{quote article_version['id']},
+ #{quote 1},
+ #{quote article_version['unit_quantity']},
+ #{quote 'XPP'}
+ )
+ })
+
+ compound_unit = "#{article_version['unit_quantity']}x#{article_version['unit']}"
+ update(%(
+ UPDATE article_versions
+ SET unit = #{quote compound_unit},
+ group_order_granularity = #{quote 1},
+ group_order_unit = #{quote 'XPP'},
+ price = #{quote article_version['price'].to_f * article_version['unit_quantity']},
+ price_unit = #{quote 'XPP'},
+ billing_unit = #{quote 'XPP'}
+ WHERE article_versions.id = #{quote article_version['id']}
+ ))
+ end
+
+ change_table :article_versions do |t|
+ t.remove :unit_quantity
+ end
+
+ change_table :order_articles do |t|
+ t.change :quantity, :decimal, precision: 8, scale: 3, null: false
+ t.change :tolerance, :decimal, precision: 8, scale: 3, null: false
+ t.change :units_to_order, :decimal, precision: 8, scale: 3, null: false
+ end
+
+ change_table :group_order_articles do |t|
+ t.change :quantity, :decimal, precision: 8, scale: 3, null: false
+ t.change :tolerance, :decimal, precision: 8, scale: 3, null: false
+ end
+
+ change_table :group_order_article_quantities do |t|
+ t.change :quantity, :decimal, precision: 8, scale: 3, null: false
+ t.change :tolerance, :decimal, precision: 8, scale: 3, null: false
+ end
+ end
+
+ def down
+ change_table :article_versions do |t|
+ t.remove :supplier_order_unit
+ t.remove :price_unit
+ t.remove :billing_unit
+ t.remove :group_order_unit
+ t.remove :group_order_granularity
+ t.remove :minimum_order_quantity
+ t.column :unit_quantity, :integer, null: false
+ t.change :unit, :string, null: true, default: ''
+ end
+
+ article_unit_ratios = select_all('SELECT article_version_id, quantity FROM article_unit_ratios WHERE sort=1')
+ article_unit_ratios.each do |article_unit_ratio|
+ update(%(
+ UPDATE article_versions
+ SET unit_quantity = #{quote article_unit_ratio['quantity']}
+ WHERE id = #{quote article_unit_ratio['article_version_id']}
+ ))
+ end
+
+ drop_table :article_unit_ratios
+
+ change_table :order_articles do |t|
+ t.change :quantity, :integer, null: false
+ t.change :tolerance, :integer, null: false
+ t.change :units_to_order, :integer, null: false
+ end
+
+ change_table :group_order_articles do |t|
+ t.change :quantity, :integer, null: false
+ t.change :tolerance, :integer, null: false
+ end
+
+ change_table :group_order_article_quantities do |t|
+ t.change :quantity, :integer, null: false
+ t.change :tolerance, :integer, null: false
+ end
+ end
+end
diff --git a/db/migrate/20230414122017_alter_suppliers_sharing_fields.rb b/db/migrate/20230414122017_alter_suppliers_sharing_fields.rb
new file mode 100644
index 00000000..5d66df18
--- /dev/null
+++ b/db/migrate/20230414122017_alter_suppliers_sharing_fields.rb
@@ -0,0 +1,19 @@
+class AlterSuppliersSharingFields < ActiveRecord::Migration[5.2]
+ def up
+ change_table :suppliers do |t|
+ t.remove :shared_supplier_id
+ t.column :supplier_remote_source, :string
+ t.column :external_uuid, :string
+ end
+
+ add_index :suppliers, :external_uuid, unique: true
+ end
+
+ def down
+ change_table :suppliers do |t|
+ t.column :shared_supplier_id, :integer
+ t.remove :supplier_remote_source
+ t.remove :external_uuid
+ end
+ end
+end
diff --git a/db/migrate/20231104141204_create_article_units.rb b/db/migrate/20231104141204_create_article_units.rb
new file mode 100644
index 00000000..93402f9f
--- /dev/null
+++ b/db/migrate/20231104141204_create_article_units.rb
@@ -0,0 +1,29 @@
+class CreateArticleUnits < ActiveRecord::Migration[5.2]
+ def up
+ create_table :article_units, id: false do |t|
+ t.string :unit, length: 3, null: false
+
+ t.timestamps
+ end
+
+ add_index :article_units, :unit, unique: true
+
+ # TODO: modify list and make it scan current article units to determine, if metric, imperial or both unit types should be added
+ # (see see https://github.com/foodcoopsat/foodsoft_hackathon/issues/35):
+ unit_codes = %w[GRM HGM KGM LTR MLT PTN STC XPP XCR XBO XBH XGR XPK XSA XPU XPT]
+ unit_codes.each do |unit_code|
+ insert(%{
+ INSERT INTO article_units (unit, created_at, updated_at)
+ VALUES (
+ #{quote unit_code},
+ NOW(),
+ NOW()
+ )
+ })
+ end
+ end
+
+ def down
+ drop_table :article_units
+ end
+end
diff --git a/db/migrate/20231210181807_add_unit_migration_completed_to_suppliers.rb b/db/migrate/20231210181807_add_unit_migration_completed_to_suppliers.rb
new file mode 100644
index 00000000..497e0077
--- /dev/null
+++ b/db/migrate/20231210181807_add_unit_migration_completed_to_suppliers.rb
@@ -0,0 +1,5 @@
+class AddUnitMigrationCompletedToSuppliers < ActiveRecord::Migration[5.2]
+ def change
+ add_column :suppliers, :unit_migration_completed, :datetime
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ce812b3f..b403900b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,538 +10,558 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_02_05_090257) do
-
- create_table "active_storage_attachments", id: :integer, 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.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|
- 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.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
- end
-
- create_table "article_categories", id: :integer, 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|
- 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.index ["article_id"], name: "index_article_prices_on_article_id"
- end
-
- create_table "articles", id: :integer, 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
- t.string "unit", default: "", null: false
- t.string "note"
- t.boolean "availability", default: true, null: false
- t.string "manufacturer"
- t.string "origin"
- t.datetime "shared_updated_on"
- 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.string "type"
- t.integer "quantity", default: 0
- t.index ["article_category_id"], name: "index_articles_on_article_category_id"
- t.index ["name", "supplier_id"], name: "index_articles_on_name_and_supplier_id"
- t.index ["supplier_id"], name: "index_articles_on_supplier_id"
- t.index ["type"], name: "index_articles_on_type"
- end
-
- create_table "assignments", id: :integer, 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|
- 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.string "import_continuation_point"
- t.integer "bank_gateway_id"
- end
-
- create_table "bank_gateways", id: :integer, 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|
- t.integer "bank_account_id", null: false
- t.string "external_id"
- t.date "date"
- t.decimal "amount", precision: 8, scale: 2, null: false
- t.string "iban"
- t.string "reference"
- t.text "text"
- t.text "receipt"
- t.binary "image", limit: 16777215
- 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|
- t.string "name"
- t.string "mime"
- t.binary "data", limit: 4294967295
- t.integer "created_by_user_id"
- t.datetime "created_at"
- t.datetime "updated_at"
- 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|
- t.text "note"
- end
-
- create_table "financial_transaction_classes", id: :integer, 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|
- t.string "name", null: false
- t.integer "financial_transaction_class_id", null: false
- t.string "name_short"
- t.integer "bank_account_id"
- t.index ["name_short"], name: "index_financial_transaction_types_on_name_short"
- end
-
- create_table "financial_transactions", id: :integer, 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.integer "financial_transaction_type_id", null: false
- t.integer "financial_link_id"
- t.integer "reverts_id"
- t.integer "group_order_id"
- t.index ["ordergroup_id"], name: "index_financial_transactions_on_ordergroup_id"
- 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|
- 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.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|
- 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.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
- t.index ["group_order_id"], name: "index_group_order_articles_on_group_order_id"
- 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|
- 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.integer "updated_by_user_id"
- t.decimal "transport", precision: 8, scale: 2
- t.index ["order_id"], name: "index_group_orders_on_order_id"
- t.index ["ordergroup_id", "order_id"], name: "index_group_orders_on_ordergroup_id_and_order_id", unique: true
- t.index ["ordergroup_id"], name: "index_group_orders_on_ordergroup_id"
- end
-
- create_table "groups", id: :integer, 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.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.string "contact_person"
- t.string "contact_phone"
- t.string "contact_address"
- t.text "stats"
- t.integer "next_weekly_tasks_number", default: 8
- t.boolean "ignore_apple_restriction", default: false
- t.date "break_start"
- t.date "break_end"
- t.boolean "role_invoices", default: false, null: false
- t.boolean "role_pickups", default: false, null: false
- t.index ["name"], name: "index_groups_on_name", unique: true
- end
-
- create_table "invites", id: :integer, force: :cascade do |t|
- t.string "token", default: "", null: false
- t.datetime "expires_at", 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|
- t.integer "supplier_id"
- t.string "number"
- t.date "date"
- t.date "paid_on"
- t.text "note"
- 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.integer "created_by_user_id"
- t.string "attachment_mime"
- t.binary "attachment_data", limit: 16777215
- 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|
- t.string "name", null: false
- t.string "url", null: false
- t.integer "workgroup_id"
- t.boolean "indirect", default: false, null: false
- t.string "authorization"
- end
-
- create_table "mail_delivery_status", id: :integer, force: :cascade do |t|
- t.datetime "created_at"
- t.string "email", null: false
- t.string "message", null: false
- t.string "attachment_mime"
- t.binary "attachment_data", limit: 4294967295
- t.index ["email"], name: "index_mail_delivery_status_on_email"
- end
-
- create_table "memberships", id: :integer, 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|
- 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.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|
- t.integer "sender_id"
- t.string "subject", null: false
- t.text "body"
- t.boolean "private", default: false
- t.datetime "created_at"
- t.integer "reply_to"
- t.integer "group_id"
- t.string "salt"
- t.binary "received_email", limit: 16777215
- end
-
- create_table "oauth_access_grants", id: :integer, 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.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|
- 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.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|
- 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.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|
- t.integer "order_id", default: 0, null: false
- t.integer "article_id", default: 0, null: false
- t.integer "quantity", default: 0, null: false
- t.integer "tolerance", default: 0, null: false
- t.integer "units_to_order", default: 0, null: false
- t.integer "lock_version", default: 0, null: false
- t.integer "article_price_id"
- t.decimal "units_billed", precision: 8, scale: 3
- t.decimal "units_received", precision: 8, scale: 3
- t.index ["order_id", "article_id"], name: "index_order_articles_on_order_id_and_article_id", unique: true
- t.index ["order_id"], name: "index_order_articles_on_order_id"
- end
-
- create_table "order_comments", id: :integer, force: :cascade do |t|
- t.integer "order_id"
- t.integer "user_id"
- t.text "text"
- t.datetime "created_at"
- t.index ["order_id"], name: "index_order_comments_on_order_id"
- end
-
- create_table "orders", id: :integer, force: :cascade do |t|
- t.integer "supplier_id"
- t.text "note"
- t.datetime "starts"
- t.datetime "ends"
- 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.integer "invoice_id"
- t.date "pickup"
- t.datetime "last_sent_mail"
- 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|
- 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.index ["page_id"], name: "index_page_versions_on_page_id"
- end
-
- create_table "pages", id: :integer, force: :cascade do |t|
- t.string "title"
- t.text "body"
- t.string "permalink"
- t.integer "lock_version", default: 0
- t.integer "updated_by"
- t.integer "redirect"
- t.integer "parent_id"
- t.datetime "created_at"
- t.datetime "updated_at"
- 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|
- t.date "next_task_date"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- end
-
- create_table "poll_choices", id: :integer, 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|
- 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.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|
- t.integer "created_by_user_id", null: false
- t.string "name", null: false
- t.text "description"
- t.datetime "starts"
- t.datetime "ends"
- t.boolean "one_vote_per_ordergroup", default: false, null: false
- t.text "required_ordergroup_custom_fields"
- t.text "required_user_custom_fields"
- t.integer "voting_method", null: false
- t.text "choices", null: false
- t.integer "final_choice"
- 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.index ["final_choice"], name: "index_polls_on_final_choice"
- end
-
- create_table "printer_job_updates", id: :integer, force: :cascade do |t|
- t.integer "printer_job_id", null: false
- t.datetime "created_at", 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|
- 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.index ["finished_at"], name: "index_printer_jobs_on_finished_at"
- end
-
- create_table "settings", id: :integer, 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.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|
- t.integer "stock_event_id"
- t.integer "order_id"
- t.integer "stock_article_id"
- t.integer "quantity", default: 0
- t.datetime "created_at"
- 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|
- t.integer "supplier_id"
- t.date "date"
- t.datetime "created_at"
- 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|
- 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|
- t.string "name", default: "", null: false
- t.string "address", default: "", null: false
- t.string "phone", default: "", null: false
- t.string "phone2"
- t.string "fax"
- t.string "email"
- t.string "url"
- t.string "contact_person"
- t.string "customer_number"
- t.string "delivery_days"
- t.string "order_howto"
- t.string "note"
- t.integer "shared_supplier_id"
- t.string "min_order_quantity"
- t.datetime "deleted_at"
- 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|
- 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.integer "required_users", default: 1
- t.integer "duration", default: 1
- t.integer "periodic_task_group_id"
- t.integer "created_by_user_id"
- t.index ["due_date"], name: "index_tasks_on_due_date"
- t.index ["name"], name: "index_tasks_on_name"
- t.index ["workgroup_id"], name: "index_tasks_on_workgroup_id"
- end
-
- create_table "users", id: :integer, force: :cascade do |t|
- t.string "nick"
- t.string "password_hash", default: "", null: false
- t.string "password_salt", default: "", null: false
- t.string "first_name", default: "", null: false
- t.string "last_name", default: "", null: false
- t.string "email", default: "", null: false
- t.string "phone"
- t.datetime "created_on", 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.string "iban"
- t.index ["email"], name: "index_users_on_email", unique: true
- t.index ["nick"], name: "index_users_on_nick", unique: true
- end
-
+ActiveRecord::Schema.define(version: 20_231_210_181_807) do
+ create_table 'active_storage_attachments', options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.index ['blob_id'], name: 'index_active_storage_attachments_on_blob_id'
+ t.index %w[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness', unique: true
+ end
+
+ create_table 'active_storage_blobs', options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.index ['key'], name: 'index_active_storage_blobs_on_key', unique: true
+ end
+
+ create_table 'article_categories', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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_unit_ratios', options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.bigint 'article_version_id', null: false
+ t.integer 'sort', null: false
+ t.decimal 'quantity', precision: 8, scale: 3, null: false
+ t.string 'unit'
+ t.index ['article_version_id'], name: 'index_article_unit_ratios_on_article_version_id'
+ t.index ['sort'], name: 'index_article_unit_ratios_on_sort'
+ end
+
+ create_table 'article_units', id: false, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'unit', null: false
+ t.datetime 'created_at', null: false
+ t.datetime 'updated_at', null: false
+ t.index ['unit'], name: 'index_article_units_on_unit', unique: true
+ end
+
+ create_table 'article_versions', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.datetime 'created_at'
+ t.string 'name', default: '', null: false
+ t.integer 'article_category_id', default: 0, null: false
+ t.string 'unit'
+ t.string 'note'
+ t.boolean 'availability', default: true, null: false
+ t.string 'manufacturer'
+ t.string 'origin'
+ t.string 'order_number'
+ t.datetime 'updated_at'
+ t.string 'supplier_order_unit'
+ t.string 'price_unit'
+ t.string 'billing_unit'
+ t.string 'group_order_unit'
+ t.decimal 'group_order_granularity', precision: 8, scale: 3, default: '1.0', null: false
+ t.float 'minimum_order_quantity'
+ t.index ['article_category_id'], name: 'index_article_versions_on_article_category_id'
+ t.index %w[article_id created_at], name: 'index_article_versions_on_article_id_and_created_at', unique: true
+ end
+
+ create_table 'articles', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'supplier_id', default: 0, null: false
+ t.datetime 'shared_updated_on'
+ t.datetime 'created_at'
+ t.datetime 'deleted_at'
+ t.string 'type'
+ t.integer 'quantity', default: 0
+ t.index ['supplier_id'], name: 'index_articles_on_supplier_id'
+ t.index ['type'], name: 'index_articles_on_type'
+ end
+
+ create_table 'assignments', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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 %w[user_id task_id], name: 'index_assignments_on_user_id_and_task_id', unique: true
+ end
+
+ create_table 'bank_accounts', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.string 'import_continuation_point'
+ t.integer 'bank_gateway_id'
+ end
+
+ create_table 'bank_gateways', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'bank_account_id', null: false
+ t.string 'external_id'
+ t.date 'date'
+ t.decimal 'amount', precision: 8, scale: 2, null: false
+ t.string 'iban'
+ t.string 'reference'
+ t.text 'text'
+ t.text 'receipt'
+ t.integer 'financial_link_id'
+ t.index ['financial_link_id'], name: 'index_bank_transactions_on_financial_link_id'
+ end
+
+ create_table 'documents', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'name'
+ t.string 'mime'
+ t.binary 'data', limit: 16_777_215
+ t.integer 'created_by_user_id'
+ t.datetime 'created_at'
+ t.datetime 'updated_at'
+ t.integer 'parent_id'
+ t.index ['parent_id'], name: 'index_documents_on_parent_id'
+ end
+
+ create_table 'financial_links', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci ROW_FORMAT=COMPACT', force: :cascade do |t|
+ t.text 'note'
+ end
+
+ create_table 'financial_transaction_classes', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'name', null: false
+ t.integer 'financial_transaction_class_id', null: false
+ t.string 'name_short'
+ t.integer 'bank_account_id'
+ t.index ['name_short'], name: 'index_financial_transaction_types_on_name_short'
+ end
+
+ create_table 'financial_transactions', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.integer 'financial_link_id'
+ t.integer 'financial_transaction_type_id', null: false
+ t.integer 'reverts_id'
+ t.integer 'group_order_id'
+ t.index ['ordergroup_id'], name: 'index_financial_transactions_on_ordergroup_id'
+ t.index ['reverts_id'], name: 'index_financial_transactions_on_reverts_id', unique: true
+ end
+
+ create_table 'group_order_article_quantities', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'group_order_article_id', default: 0, null: false
+ t.decimal 'quantity', precision: 8, scale: 3, default: '0.0', null: false
+ t.decimal 'tolerance', precision: 8, scale: 3, default: '0.0', null: false
+ t.datetime 'created_on', 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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.decimal 'quantity', precision: 8, scale: 3, default: '0.0', null: false
+ t.decimal 'tolerance', precision: 8, scale: 3, default: '0.0', null: false
+ t.datetime 'updated_on', null: false
+ t.decimal 'result', precision: 8, scale: 3
+ t.decimal 'result_computed', precision: 8, scale: 3
+ t.index %w[group_order_id order_article_id], name: 'goa_index', unique: true
+ t.index ['group_order_id'], name: 'index_group_order_articles_on_group_order_id'
+ t.index ['order_article_id'], name: 'index_group_order_articles_on_order_article_id'
+ end
+
+ create_table 'group_orders', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.integer 'updated_by_user_id'
+ t.decimal 'transport', precision: 8, scale: 2
+ t.index ['order_id'], name: 'index_group_orders_on_order_id'
+ t.index %w[ordergroup_id order_id], name: 'index_group_orders_on_ordergroup_id_and_order_id', unique: true
+ t.index ['ordergroup_id'], name: 'index_group_orders_on_ordergroup_id'
+ end
+
+ create_table 'groups', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.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.string 'contact_person'
+ t.string 'contact_phone'
+ t.string 'contact_address'
+ t.text 'stats'
+ t.integer 'next_weekly_tasks_number', default: 8
+ t.boolean 'ignore_apple_restriction', default: false
+ t.boolean 'role_invoices', default: false, null: false
+ t.date 'break_start'
+ t.date 'break_end'
+ t.boolean 'role_pickups', default: false, null: false
+ t.index ['name'], name: 'index_groups_on_name', unique: true
+ end
+
+ create_table 'invites', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'token', default: '', null: false
+ t.datetime 'expires_at', 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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'supplier_id'
+ t.string 'number'
+ t.date 'date'
+ t.date 'paid_on'
+ t.text 'note'
+ 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.integer 'created_by_user_id'
+ t.string 'attachment_mime'
+ t.binary 'attachment_data', limit: 16_777_215
+ t.integer 'financial_link_id'
+ t.index ['supplier_id'], name: 'index_invoices_on_supplier_id'
+ end
+
+ create_table 'links', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'name', null: false
+ t.string 'url', null: false
+ t.integer 'workgroup_id'
+ t.boolean 'indirect', default: false, null: false
+ t.string 'authorization'
+ end
+
+ create_table 'mail_delivery_status', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.datetime 'created_at'
+ t.string 'email', null: false
+ t.string 'message', null: false
+ t.string 'attachment_mime'
+ t.binary 'attachment_data', limit: 16_777_215
+ t.index ['email'], name: 'index_mail_delivery_status_on_email'
+ end
+
+ create_table 'memberships', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'group_id', default: 0, null: false
+ t.integer 'user_id', default: 0, null: false
+ t.index %w[user_id group_id], name: 'index_memberships_on_user_id_and_group_id', unique: true
+ end
+
+ create_table 'message_recipients', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.index ['message_id'], name: 'index_message_recipients_on_message_id'
+ t.index %w[user_id read_at], name: 'index_message_recipients_on_user_id_and_read_at'
+ end
+
+ create_table 'messages', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.integer 'reply_to'
+ t.integer 'group_id'
+ t.string 'salt'
+ t.binary 'received_email', limit: 16_777_215
+ end
+
+ create_table 'oauth_access_grants', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.string 'scopes'
+ t.index ['token'], name: 'index_oauth_access_grants_on_token', unique: true
+ end
+
+ create_table 'oauth_access_tokens', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.boolean 'confidential', default: true, null: false
+ t.index ['uid'], name: 'index_oauth_applications_on_uid', unique: true
+ end
+
+ create_table 'order_articles', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'order_id', default: 0, null: false
+ t.decimal 'quantity', precision: 8, scale: 3, default: '0.0', null: false
+ t.decimal 'tolerance', precision: 8, scale: 3, default: '0.0', null: false
+ t.decimal 'units_to_order', precision: 8, scale: 3, default: '0.0', null: false
+ t.integer 'lock_version', default: 0, null: false
+ t.integer 'article_version_id', null: false
+ t.decimal 'units_billed', precision: 8, scale: 3
+ t.decimal 'units_received', precision: 8, scale: 3
+ t.index ['article_version_id'], name: 'index_order_articles_on_article_version_id'
+ t.index %w[order_id article_version_id], name: 'index_order_articles_on_order_id_and_article_version_id', unique: true
+ t.index ['order_id'], name: 'index_order_articles_on_order_id'
+ end
+
+ create_table 'order_comments', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'order_id'
+ t.integer 'user_id'
+ t.text 'text'
+ t.datetime 'created_at'
+ t.index ['order_id'], name: 'index_order_comments_on_order_id'
+ end
+
+ create_table 'orders', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'supplier_id'
+ t.text 'note'
+ t.datetime 'starts'
+ t.datetime 'ends'
+ 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.date 'pickup'
+ t.integer 'invoice_id'
+ t.datetime 'last_sent_mail'
+ 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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.index ['page_id'], name: 'index_page_versions_on_page_id'
+ end
+
+ create_table 'pages', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'title'
+ t.text 'body'
+ t.string 'permalink'
+ t.integer 'lock_version', default: 0
+ t.integer 'updated_by'
+ t.integer 'redirect'
+ t.integer 'parent_id'
+ t.datetime 'created_at'
+ t.datetime 'updated_at'
+ t.index ['permalink'], name: 'index_pages_on_permalink'
+ t.index ['title'], name: 'index_pages_on_title'
+ end
+
+ create_table 'periodic_task_groups', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.date 'next_task_date'
+ t.datetime 'created_at', null: false
+ t.datetime 'updated_at', null: false
+ end
+
+ create_table 'poll_choices', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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 %w[poll_vote_id choice], name: 'index_poll_choices_on_poll_vote_id_and_choice', unique: true
+ end
+
+ create_table 'poll_votes', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.index %w[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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.boolean 'one_vote_per_ordergroup', default: false, null: false
+ t.text 'required_ordergroup_custom_fields'
+ t.text 'required_user_custom_fields'
+ t.integer 'voting_method', null: false
+ t.text 'choices', null: false
+ t.integer 'final_choice'
+ 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.index ['final_choice'], name: 'index_polls_on_final_choice'
+ end
+
+ create_table 'printer_job_updates', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'printer_job_id', null: false
+ t.datetime 'created_at', null: false
+ t.string 'state', null: false
+ t.text 'message'
+ t.index %w[printer_job_id created_at], name: 'index_printer_job_updates_on_printer_job_id_and_created_at'
+ end
+
+ create_table 'printer_jobs', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.index ['finished_at'], name: 'index_printer_jobs_on_finished_at'
+ end
+
+ create_table 'settings', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.index %w[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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.integer 'supplier_id'
+ t.date 'date'
+ t.datetime 'created_at'
+ 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, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'name', null: false
+ t.string 'description'
+ t.integer 'financial_transaction_class_id', null: false
+ t.integer 'bank_account_id'
+ end
+
+ create_table 'suppliers', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'name', default: '', null: false
+ t.string 'address', default: '', null: false
+ t.string 'phone', default: '', null: false
+ t.string 'phone2'
+ t.string 'fax'
+ t.string 'email'
+ t.string 'url'
+ t.string 'contact_person'
+ t.string 'customer_number'
+ t.string 'delivery_days'
+ t.string 'order_howto'
+ t.string 'note'
+ t.string 'min_order_quantity'
+ t.datetime 'deleted_at'
+ t.string 'shared_sync_method'
+ t.string 'iban'
+ t.integer 'supplier_category_id', null: false
+ t.string 'supplier_remote_source'
+ t.string 'external_uuid'
+ t.datetime 'unit_migration_completed'
+ t.index ['external_uuid'], name: 'index_suppliers_on_external_uuid', unique: true
+ t.index ['name'], name: 'index_suppliers_on_name', unique: true
+ end
+
+ create_table 'tasks', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_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.integer 'required_users', default: 1
+ t.integer 'duration', default: 1
+ t.integer 'periodic_task_group_id'
+ t.integer 'created_by_user_id'
+ t.index ['due_date'], name: 'index_tasks_on_due_date'
+ t.index ['name'], name: 'index_tasks_on_name'
+ t.index ['workgroup_id'], name: 'index_tasks_on_workgroup_id'
+ end
+
+ create_table 'users', id: :integer, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci', force: :cascade do |t|
+ t.string 'nick'
+ t.string 'password_hash', default: '', null: false
+ t.string 'password_salt', default: '', null: false
+ t.string 'first_name', default: '', null: false
+ t.string 'last_name', default: '', null: false
+ t.string 'email', default: '', null: false
+ t.string 'phone'
+ t.datetime 'created_on', 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.string 'iban'
+ t.string 'attachment_mime'
+ t.binary 'attachment_data', limit: 16_777_215
+ 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_attachments', 'active_storage_blobs', column: 'blob_id'
end
diff --git a/db/seeds/hackathon.seeds.rb b/db/seeds/hackathon.seeds.rb
new file mode 100644
index 00000000..81d88fd3
--- /dev/null
+++ b/db/seeds/hackathon.seeds.rb
@@ -0,0 +1,158 @@
+require_relative 'seed_helper'
+
+## Financial transaction classes
+
+FinancialTransactionClass.create!(id: 1, name: 'Standard')
+FinancialTransactionClass.create!(id: 2, name: 'Foodsoft')
+
+## Article units
+
+# TODO: - make this depend on locale - see https://github.com/foodcoopsat/foodsoft_hackathon/issues/35
+unit_codes = %w[GRM HGM KGM LTR MLT PTN STC XPP XCR XBO XBH XGR XPK XSA XPU XPT]
+unit_codes.each { |unit_code| ArticleUnit.create!(unit: unit_code) }
+
+## Suppliers & articles
+
+SupplierCategory.create!(id: 1, name: 'Other', financial_transaction_class_id: 1)
+
+Supplier.create!([
+ { id: 1, name: 'Hackathon', supplier_category_id: 1, address: 'Smallstreet 1, Cookilage',
+ phone: '0123456789', email: 'info@bbakery.test', min_order_quantity: '100', unit_migration_completed: Time.now }
+ ])
+
+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!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Carrots', article_category_id: 3, unit: nil, price: 3, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: 'KGM', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.001 })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Pumpkin', article_category_id: 3, unit: nil, price: 1.5, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'XPP',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1.3, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Bread', article_category_id: 5, unit: nil, price: 2.1, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'XPP', group_order_granularity: 0.5,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 700, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Bread rolls', article_category_id: 5, unit: nil, price: 1, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'XPP', group_order_unit: 'XPP', minimum_order_quantity: 5,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 350, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Muesli', article_category_id: 13, unit: nil, price: 2.5, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'XPP', billing_unit: 'XPP', group_order_unit: 'XPP',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 500, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Smoked tofu', article_category_id: 8, unit: nil, price: 2.4, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'HGM', billing_unit: 'GRM', group_order_unit: 'XPP',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 160, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Beer', article_category_id: 6, unit: nil, price: 52, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XCR', price_unit: 'XBO', billing_unit: 'XBO', group_order_unit: 'XBO',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 20, unit: 'XBO' }), ArticleUnitRatio.new({ sort: 2, quantity: 10, unit: 'LTR' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Detergent', article_category_id: 1, unit: nil, price: 20, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'LTR', billing_unit: 'LTR', group_order_unit: 'LTR', group_order_granularity: 0.001,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 2, quantity: 20, unit: 'LTR' }), ArticleUnitRatio.new({ sort: 2, quantity: 25, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Rice', article_category_id: 12, unit: nil, price: 6.75, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Potatoes', article_category_id: 3, unit: nil, price: 1.5, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: 'KGM', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'GRM' })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Wheat', article_category_id: 12, unit: nil, price: 25, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Oranges', article_category_id: 2, unit: nil, price: 36, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 12, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Lentils', article_category_id: 12, unit: nil, price: 2.7, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 500, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Oyster mushrooms', article_category_id: 3, unit: nil,
+ price: 3, tax: 7.0, deposit: '0.0', supplier_order_unit: 'KGM', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.001, minimum_order_quantity: 1.2 })
+
+Supplier.create!([
+ { id: 2, name: 'Unit migration test', supplier_category_id: 1, address: 'Smallstreet 2, Cookilage',
+ phone: '0123456789', email: 'info@bbakery.test', min_order_quantity: '100', unit_migration_completed: nil }
+ ])
+
+Article.create!({ supplier_id: 2,
+ quantity: 0 }).article_versions.create({ name: 'Goat cheese', article_category_id: 8, unit: '250g', price: 3, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: nil, price_unit: nil, billing_unit: nil, group_order_unit: nil, group_order_granularity: 1 })
+Article.create!({ supplier_id: 2,
+ quantity: 0 }).article_versions.create({ name: 'Butter', article_category_id: 8, unit: '4x250g', price: 3, tax: 7.0, deposit: '0.0', supplier_order_unit: nil, price_unit: 'XPP', billing_unit: 'XPP', group_order_unit: 'XPP', group_order_granularity: 1,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 4, unit: 'XPP' })] })
+Article.create!({ supplier_id: 2,
+ quantity: 0 }).article_versions.create({ name: 'Bread', article_category_id: 5, unit: 'pc', price: 3, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: nil, price_unit: nil, billing_unit: nil, group_order_unit: nil, group_order_granularity: 1 })
+
+## 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: 'Peters',
+ email: 'peter@peters.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: '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')
+
+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)
+
+## Orders & OrderArticles
+
+seed_order(supplier_id: 1, starts: 2.days.ago, ends: 5.days.from_now)
+
+## Finances
+
+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)
+
+FoodsoftConfig[:minimum_balance] = '-10000'
diff --git a/db/seeds/hackathon_de.seeds.rb b/db/seeds/hackathon_de.seeds.rb
new file mode 100644
index 00000000..3e645e47
--- /dev/null
+++ b/db/seeds/hackathon_de.seeds.rb
@@ -0,0 +1,158 @@
+require_relative 'seed_helper'
+
+## Financial transaction classes
+
+FinancialTransactionClass.create!(id: 1, name: 'Standard')
+FinancialTransactionClass.create!(id: 2, name: 'Foodsoft')
+
+## Article units
+
+# TODO: - make this depend on locale - see https://github.com/foodcoopsat/foodsoft_hackathon/issues/35
+unit_codes = %w[GRM HGM KGM LTR MLT PTN STC XPP XCR XBO XBH XGR XPK XSA XPU XPT]
+unit_codes.each { |unit_code| ArticleUnit.create!(unit: unit_code) }
+
+## Suppliers & articles
+
+SupplierCategory.create!(id: 1, name: 'Other', financial_transaction_class_id: 1)
+
+Supplier.create!([
+ { id: 1, name: 'Hackathon', supplier_category_id: 1, address: 'Smallstreet 1, Cookilage',
+ phone: '0123456789', email: 'info@bbakery.test', min_order_quantity: '100', unit_migration_completed: Time.now }
+ ])
+
+ArticleCategory.create!(id: 1, name: 'Sonstiges')
+ArticleCategory.create!(id: 2, name: 'Obst')
+ArticleCategory.create!(id: 3, name: 'Gemüse')
+ArticleCategory.create!(id: 4, name: 'Kartoffel & Zwiebel')
+ArticleCategory.create!(id: 5, name: 'Backwaren')
+ArticleCategory.create!(id: 6, name: 'Getränke')
+ArticleCategory.create!(id: 7, name: 'Kräuter & Gewürze')
+ArticleCategory.create!(id: 8, name: 'Mich & Milchprodukte')
+ArticleCategory.create!(id: 9, name: 'Fisch & Meeresfrüchte')
+ArticleCategory.create!(id: 10, name: 'Fleisch')
+ArticleCategory.create!(id: 11, name: 'Öle & Fett')
+ArticleCategory.create!(id: 12, name: 'Getreide & Bohnen')
+ArticleCategory.create!(id: 13, name: 'Nüsse & Samen')
+ArticleCategory.create!(id: 14, name: 'Zucker & Süßigkeiten')
+
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Karotten', article_category_id: 3, unit: nil, price: 3, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: 'KGM', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.001 })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Kürbis', article_category_id: 3, unit: nil, price: 1.5, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'XPP',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1.3, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Brot', article_category_id: 5, unit: nil, price: 2.1, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'XPP', group_order_granularity: 0.5,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 700, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Semmeln', article_category_id: 5, unit: nil, price: 1, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'XPP', group_order_unit: 'XPP', minimum_order_quantity: 5,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 350, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Müsli', article_category_id: 13, unit: nil, price: 2.5, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'XPP', billing_unit: 'XPP', group_order_unit: 'XPP',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 500, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Räuchertofu', article_category_id: 8, unit: nil, price: 2.4, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'HGM', billing_unit: 'GRM', group_order_unit: 'XPP',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 160, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Bier', article_category_id: 6, unit: nil, price: 52, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XCR', price_unit: 'XBO', billing_unit: 'XBO', group_order_unit: 'XBO',
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 20, unit: 'XBO' }), ArticleUnitRatio.new({ sort: 2, quantity: 10, unit: 'LTR' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Waschmittel', article_category_id: 1, unit: nil, price: 20, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'LTR', billing_unit: 'LTR', group_order_unit: 'LTR', group_order_granularity: 0.001,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 2, quantity: 20, unit: 'LTR' }), ArticleUnitRatio.new({ sort: 2, quantity: 25, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Reis', article_category_id: 12, unit: nil, price: 6.75, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Kartoffeln', article_category_id: 3, unit: nil, price: 1.5, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: 'KGM', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'GRM' })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Weizen', article_category_id: 12, unit: nil, price: 25, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Orangen', article_category_id: 2, unit: nil, price: 36, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 12, unit: 'KGM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Linsen', article_category_id: 12, unit: nil, price: 2.7, tax: 7.0, deposit: '0.0', supplier_order_unit: 'XPP', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.05,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 500, unit: 'GRM' })] })
+Article.create!({ supplier_id: 1,
+ quantity: 0 }).article_versions.create({ name: 'Austernpilze', article_category_id: 3, unit: nil,
+ price: 3, tax: 7.0, deposit: '0.0', supplier_order_unit: 'KGM', price_unit: 'KGM', billing_unit: 'KGM', group_order_unit: 'KGM', group_order_granularity: 0.001, minimum_order_quantity: 1.2 })
+
+Supplier.create!([
+ { id: 2, name: 'Einheiten-Migrations-Test', supplier_category_id: 1,
+ address: 'Smallstreet 2, Cookilage', phone: '0123456789', email: 'info@bbakery.test', min_order_quantity: '100', unit_migration_completed: nil }
+ ])
+
+Article.create!({ supplier_id: 2,
+ quantity: 0 }).article_versions.create({ name: 'Ziegenkäse', article_category_id: 8, unit: '250g', price: 3, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: nil, price_unit: nil, billing_unit: nil, group_order_unit: nil, group_order_granularity: 1 })
+Article.create!({ supplier_id: 2,
+ quantity: 0 }).article_versions.create({ name: 'Butter', article_category_id: 8, unit: '4x250g', price: 3, tax: 7.0, deposit: '0.0', supplier_order_unit: nil, price_unit: 'XPP', billing_unit: 'XPP', group_order_unit: 'XPP', group_order_granularity: 1,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 4, unit: 'XPP' })] })
+Article.create!({ supplier_id: 2,
+ quantity: 0 }).article_versions.create({ name: 'Brot', article_category_id: 5, unit: 'Stk', price: 3, tax: 7.0,
+ deposit: '0.0', supplier_order_unit: nil, price_unit: nil, billing_unit: nil, group_order_unit: nil, group_order_granularity: 1 })
+
+## 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: 'Peters',
+ email: 'peter@peters.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: '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(1).settings['profile'] = { language: :de }
+
+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)
+
+## Orders & OrderArticles
+
+seed_order(supplier_id: 1, starts: 2.days.ago, ends: 5.days.from_now)
+
+## Finances
+
+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)
+
+FoodsoftConfig[:minimum_balance] = '-10000'
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..f46fc863 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)
+ unit_quantity = goa.order_article.article_version.unit_quantity
+ goa.update_quantities rand([4, (2 * unit_quantity) + 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 40c338ac..71dd6c9b 100644
--- a/db/seeds/small.en.seeds.rb
+++ b/db/seeds/small.en.seeds.rb
@@ -1,173 +1,299 @@
-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")
-
-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")
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 12, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 22, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 20, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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 => "Peters", :email => "peter@peters.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 => "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')
-
-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)
+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: 'Peters',
+ email: 'peter@peters.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: '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')
+
+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)
## Orders & OrderArticles
@@ -181,10 +307,15 @@
## 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..9912eaf1 100644
--- a/db/seeds/small.nl.seeds.rb
+++ b/db/seeds/small.nl.seeds.rb
@@ -1,173 +1,299 @@
-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")
-
-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")
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 12, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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,
+ article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 8, unit: 'XPP' })])
+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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 22, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 20, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 25, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 10, unit: 'XPP' })],
+ 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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, article_unit_ratios: [ArticleUnitRatio.new({ sort: 1, quantity: 1, unit: 'XPP' })], 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')
-
-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)
+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)
+
+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 +307,15 @@
## 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_shared/migrate/20221204112447_create_articles_and_suppliers.rb b/db_shared/migrate/20221204112447_create_articles_and_suppliers.rb
new file mode 100644
index 00000000..f39909cd
--- /dev/null
+++ b/db_shared/migrate/20221204112447_create_articles_and_suppliers.rb
@@ -0,0 +1,50 @@
+class CreateArticlesAndSuppliers < ActiveRecord::Migration[5.2]
+ def change
+ create_table 'articles', force: :cascade do |t|
+ t.string 'name', null: false
+ t.integer 'supplier_id', null: false
+ t.string 'number'
+ t.string 'note'
+ t.string 'manufacturer'
+ t.string 'origin'
+ t.string 'unit'
+ t.decimal 'price', precision: 8, scale: 2, default: '0.0', null: false
+ t.decimal 'tax', precision: 3, scale: 1, default: '7.0', null: false
+ t.decimal 'deposit', precision: 8, scale: 2, default: '0.0', null: false
+ t.decimal 'unit_quantity', precision: 4, scale: 1, default: '1.0', null: false
+ t.decimal 'scale_quantity', precision: 4, scale: 2
+ t.decimal 'scale_price', precision: 8, scale: 2
+ t.datetime 'created_on'
+ t.datetime 'updated_on'
+ t.string 'category'
+ t.index ['name'], name: 'index_articles_on_name'
+ t.index %w[number supplier_id], name: 'index_articles_on_number_and_supplier_id', unique: true
+ end
+
+ create_table 'suppliers', force: :cascade do |t|
+ t.string 'name', null: false
+ t.string 'address', null: false
+ t.string 'phone', null: false
+ t.string 'phone2'
+ t.string 'fax'
+ t.string 'email'
+ t.string 'url'
+ t.string 'delivery_days'
+ t.string 'note'
+ t.datetime 'created_on'
+ t.datetime 'updated_on'
+ t.boolean 'ftp_sync', default: false
+ t.string 'ftp_host'
+ t.string 'ftp_user'
+ t.string 'ftp_password'
+ t.string 'ftp_type', default: 'bnn', null: false
+ t.string 'ftp_regexp', default: '^([.]/)?PL'
+ t.boolean 'mail_sync'
+ t.string 'mail_from'
+ t.string 'mail_subject'
+ t.string 'mail_type'
+ t.string 'salt', null: true
+ t.index ['name'], name: 'index_suppliers_on_name', unique: true
+ end
+ end
+end
diff --git a/db_shared/schema.rb b/db_shared/schema.rb
new file mode 100644
index 00000000..19fbca91
--- /dev/null
+++ b/db_shared/schema.rb
@@ -0,0 +1,60 @@
+# This file is auto-generated from the current state of the database. Instead
+# 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).
+#
+# It's strongly recommended that you check this file into your version control system.
+
+ActiveRecord::Schema.define(version: 20_221_204_112_447) do
+ create_table 'articles', options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', force: :cascade do |t|
+ t.string 'name', null: false
+ t.integer 'supplier_id', null: false
+ t.string 'number'
+ t.string 'note'
+ t.string 'manufacturer'
+ t.string 'origin'
+ t.string 'unit'
+ t.decimal 'price', precision: 8, scale: 2, default: '0.0', null: false
+ t.decimal 'tax', precision: 3, scale: 1, default: '7.0', null: false
+ t.decimal 'deposit', precision: 8, scale: 2, default: '0.0', null: false
+ t.decimal 'unit_quantity', precision: 4, scale: 1, default: '1.0', null: false
+ t.decimal 'scale_quantity', precision: 4, scale: 2
+ t.decimal 'scale_price', precision: 8, scale: 2
+ t.datetime 'created_on'
+ t.datetime 'updated_on'
+ t.string 'category'
+ t.index ['name'], name: 'index_articles_on_name'
+ t.index %w[number supplier_id], name: 'index_articles_on_number_and_supplier_id', unique: true
+ end
+
+ create_table 'suppliers', options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', force: :cascade do |t|
+ t.string 'name', null: false
+ t.string 'address', null: false
+ t.string 'phone', null: false
+ t.string 'phone2'
+ t.string 'fax'
+ t.string 'email'
+ t.string 'url'
+ t.string 'delivery_days'
+ t.string 'note'
+ t.datetime 'created_on'
+ t.datetime 'updated_on'
+ t.boolean 'ftp_sync', default: false
+ t.string 'ftp_host'
+ t.string 'ftp_user'
+ t.string 'ftp_password'
+ t.string 'ftp_type', default: 'bnn', null: false
+ t.string 'ftp_regexp', default: '^([.]/)?PL'
+ t.boolean 'mail_sync'
+ t.string 'mail_from'
+ t.string 'mail_subject'
+ t.string 'mail_type'
+ t.string 'salt'
+ t.index ['name'], name: 'index_suppliers_on_name', unique: true
+ end
+end
diff --git a/db_shared/seeds.rb b/db_shared/seeds.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/doc/SETUP_DEVELOPMENT_DOCKER.md b/doc/SETUP_DEVELOPMENT_DOCKER.md
index 1fdf9da6..580e45a7 100644
--- a/doc/SETUP_DEVELOPMENT_DOCKER.md
+++ b/doc/SETUP_DEVELOPMENT_DOCKER.md
@@ -75,7 +75,9 @@ Open a rails console
Setup the test database
docker-compose-dev run --rm mariadb mariadb --host=mariadb --password=secret --execute="CREATE DATABASE test"
+ docker-compose-dev run --rm mariadb mariadb --host=mariadb --password=secret --execute="CREATE DATABASE db_shared"
docker-compose-dev run --rm foodsoft bundle exec rake db:schema:load RAILS_ENV=test DATABASE_URL=mysql2://root:secret@mariadb/test?encoding=utf8mb4
+ docker-compose-dev run --rm foodsoft bundle exec rake shared:db:schema:load RAILS_ENV=test DATABASE_URL=mysql2://root:secret@mariadb/db_shared?encoding=utf8mb4
Run the tests
diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml
index 0a8b3fec..43eeca7a 100644
--- a/docker-compose-dev.yml
+++ b/docker-compose-dev.yml
@@ -35,6 +35,8 @@ services:
- MYSQL_DATABASE=development
volumes:
- mariadb:/var/lib/mysql
+ ports:
+ - "3306:3306"
phpmyadmin:
image: phpmyadmin/phpmyadmin
diff --git a/lib/apple_bar.rb b/lib/apple_bar.rb
index a2176ea3..3f81197d 100644
--- a/lib/apple_bar.rb
+++ b/lib/apple_bar.rb
@@ -14,13 +14,11 @@ def initialize(ordergroup)
def group_bar_state
if apples >= 100
'success'
+ elsif FoodsoftConfig[:stop_ordering_under].present? and
+ 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
@@ -30,7 +28,9 @@ def group_bar_width
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/article_units_lib.rb b/lib/article_units_lib.rb
new file mode 100644
index 00000000..b99aded5
--- /dev/null
+++ b/lib/article_units_lib.rb
@@ -0,0 +1,181 @@
+class ArticleUnitsLib
+ @un_ece_20_units = YAML.safe_load(ERB.new(File.read(File.expand_path(
+ 'config/units-of-measure/un-ece-20-remastered.yml', Rails.root
+ ))).result)
+ @un_ece_21_units = YAML.safe_load(ERB.new(File.read(File.expand_path('config/units-of-measure/un-ece-21.yml',
+ Rails.root))).result)
+
+ def self.untranslated_units
+ return @untranslated_units unless @untranslated_units.nil?
+
+ options = {}
+
+ @un_ece_20_units.each do |unit|
+ code = unit['CommonCode']
+ base_unit = unit['conversion']['base_units'].nil? ? nil : unit['conversion']['base_units'][0]
+ options[code] =
+ { name: unit['Name'], description: unit['Description'], baseUnit: base_unit,
+ conversionFactor: unit['conversion']['factor'], symbol: unit['Symbol'] }
+ end
+
+ @un_ece_21_units.each do |unit|
+ code = 'X' + unit['Code']
+ name = unit['Name']
+ name[0] = name[0].downcase
+
+ options[code] =
+ { name: name, description: unit['Description'], baseUnit: nil, conversionFactor: nil, symbol: unit['Symbol'] }
+ end
+
+ options.each do |code, option|
+ option[:translation_available] = !ArticleUnitsLib.get_translated_name_for_code(code, default_nil: true).nil?
+ end
+
+ @untranslated_units = options
+ end
+
+ def self.unit_translations
+ return @unit_translations unless @unit_translations.nil?
+
+ @unit_translations = YAML.safe_load(ERB.new(File.read(File.expand_path(
+ "config/units-of-measure/locales/unece_#{I18n.locale}.yml", Rails.root
+ ))).result) || {}
+ end
+
+ def self.units
+ return @units unless @units.nil?
+
+ units = untranslated_units
+ @units = units.to_h do |code, unit|
+ translated_name = ArticleUnitsLib.get_translated_name_for_code(code, default_nil: true)
+ unit = unit.clone
+ unit[:name] = translated_name || unit[:name]
+ unit[:untranslated] = translated_name.nil?
+ unit[:symbol] = ArticleUnitsLib.get_translated_symbol_for_code(code)
+ unit[:aliases] = ArticleUnitsLib.get_translated_aliases_for_code(code)
+
+ [code, unit]
+ end
+ end
+
+ def self.unit_is_si_convertible(code)
+ !units.to_h[code]&.dig(:conversionFactor).nil?
+ end
+
+ def self.get_translated_name_for_code(code, default_nil: false)
+ return nil if code.blank?
+
+ unit_translations&.dig('unece_units')&.dig(code)&.dig('name') || (default_nil ? nil : untranslated_units[code][:name])
+ end
+
+ def self.get_translated_symbol_for_code(code)
+ return nil if code.blank?
+
+ unit_translations&.dig('unece_units')&.dig(code)&.dig('symbols')&.dig(0) || untranslated_units[code][:symbol]
+ end
+
+ def self.get_translated_aliases_for_code(code)
+ return nil if code.blank?
+
+ unit_translations&.dig('unece_units')&.dig(code)&.dig('aliases')
+ end
+
+ def self.get_code_for_unit_name(name)
+ return nil if name.blank?
+
+ translation = unit_translations&.dig('unece_units')&.find do |_code, translations|
+ translations['name'] == name
+ end
+
+ return translation[0] unless translation.nil?
+
+ matching_unit = units.find do |_code, unit|
+ unit[:name] == name
+ end
+
+ matching_unit[0]
+ end
+
+ def self.convert_old_unit(old_compound_unit_str, unit_quantity)
+ return nil if old_compound_unit_str.nil?
+
+ md = old_compound_unit_str.match(/([0-9]*)x(.*)/)
+ old_compound_unit_str = md[2] if !md.nil? && md[1].to_f == unit_quantity
+
+ md = old_compound_unit_str.match(%r{^\s*([0-9][0-9,./]*)?\s*([A-Za-z\u00C0-\u017F.]+)\s*$})
+ return nil if md.nil?
+
+ unit = get_unit_from_old_str(md[2])
+ return nil if unit.nil?
+
+ quantity = get_quantity_from_old_str(md[1])
+
+ if quantity == 1 && unit_quantity == 1
+ {
+ supplier_order_unit: unit,
+ first_ratio: nil,
+ group_order_granularity: 1.0,
+ group_order_unit: unit
+ }
+ else
+ supplier_order_unit = unit.starts_with?('X') && unit != 'XPK' ? 'XPK' : 'XPP'
+ {
+ supplier_order_unit: supplier_order_unit,
+ first_ratio: {
+ quantity: quantity * unit_quantity,
+ unit: unit
+ },
+ group_order_granularity: unit_quantity > 1 ? quantity : 1.0,
+ group_order_unit: unit_quantity > 1 ? unit : supplier_order_unit
+ }
+ end
+ end
+
+ def self.get_quantity_from_old_str(quantity_str)
+ return 1 if quantity_str.nil?
+
+ quantity_str = quantity_str
+ .gsub(',', '.')
+ .gsub(' ', '')
+
+ division_parts = quantity_str.split('/').map(&:to_f)
+
+ if division_parts.length == 2
+ division_parts[0] / division_parts[1]
+ else
+ quantity_str.to_f
+ end
+ end
+
+ def self.get_unit_from_old_str(old_unit_str)
+ unit_str = old_unit_str.strip.downcase
+ units = ArticleUnitsLib.untranslated_units
+ .sort { |a, b| sort_by_translation_state(a[1], b[1]) }
+ matching_unit_arr = units.select { |key, unit| matches_unit(key, unit, unit_str) }
+ .to_a
+ return nil if matching_unit_arr.empty?
+
+ matching_unit_arr[0][0]
+ end
+
+ def self.sort_by_translation_state(unit_a, unit_b)
+ return -1 if unit_a[:translation_available] && !unit_b[:translation_available]
+ return 1 if unit_b[:translation_available] && !unit_a[:translation_available]
+
+ 0
+ end
+
+ def self.matches_unit(unit_code, unit, unit_str)
+ return true if unit[:symbol] == unit_str
+
+ translation_data = unit_translations&.dig('unece_units')&.dig(unit_code)
+
+ return true if translation_data&.dig('symbols')&.include?(unit_str)
+
+ name = translation_data&.dig('name')&.downcase
+ return true if !name.nil? && name == unit_str
+
+ aliases = translation_data&.dig('aliases')&.map(&:strip)&.map(&:downcase)
+ !aliases.nil? && aliases.any? { |a| a == unit_str || "#{a}." == unit_str }
+ end
+end
diff --git a/lib/articles_csv.rb b/lib/articles_csv.rb
index 910de9be..8202547f 100644
--- a/lib/articles_csv.rb
+++ b/lib/articles_csv.rb
@@ -13,31 +13,45 @@ def header
Article.human_attribute_name(:price),
Article.human_attribute_name(:tax),
Article.human_attribute_name(:deposit),
- Article.human_attribute_name(:unit_quantity),
- '',
- '',
+ Article.human_attribute_name(:supplier_order_unit),
+ Article.human_attribute_name(:price_unit),
+ Article.human_attribute_name(:group_order_unit),
+ Article.human_attribute_name(:group_order_granularity),
+ Article.human_attribute_name(:minimum_order_quantity),
+ Article.human_attribute_name(:billing_unit),
Article.human_attribute_name(:article_category),
+ Article.human_attribute_name(:ratios_to_supplier_order_unit)
]
end
def data
- @object.each do |o|
+ @object.each do |article|
yield [
'',
- o.order_number,
- o.name,
- o.note,
- o.manufacturer,
- o.origin,
- o.unit,
- o.price,
- o.tax,
- o.deposit,
- o.unit_quantity,
- '',
- '',
- o.article_category.try(:name),
+ article.order_number,
+ article.name,
+ article.note,
+ article.manufacturer,
+ article.origin,
+ article.unit,
+ article.price,
+ article.tax,
+ article.deposit,
+ ArticleUnitsLib.get_translated_name_for_code(article.supplier_order_unit),
+ ArticleUnitsLib.get_translated_name_for_code(article.price_unit),
+ ArticleUnitsLib.get_translated_name_for_code(article.group_order_unit),
+ article.group_order_granularity,
+ article.minimum_order_quantity,
+ ArticleUnitsLib.get_translated_name_for_code(article.billing_unit),
+ article.article_category.try(:name),
+ article.article_unit_ratios.map do |ratio|
+ "#{ratio.quantity} #{escape_csv_ratio(ArticleUnitsLib.get_translated_name_for_code(ratio.unit))}"
+ end.join(', ')
]
end
end
+
+ def escape_csv_ratio(str)
+ str.gsub('\\', '\\\\').gsub(',', '\\,')
+ end
end
diff --git a/lib/bank_account_connector.rb b/lib/bank_account_connector.rb
index 93e7cc7c..b728ebb9 100644
--- a/lib/bank_account_connector.rb
+++ b/lib/bank_account_connector.rb
@@ -8,9 +8,7 @@ def name
nil
end
- def text
- @text
- end
+ attr_reader :text
end
class TextField
@@ -24,13 +22,7 @@ def type
nil
end
- def name
- @name
- end
-
- def value
- @value
- end
+ attr_reader :name, :value
def label
@label || @name.to_s
@@ -73,17 +65,7 @@ def iban
@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 @@ def finish
@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_information_importer.rb b/lib/bank_account_information_importer.rb
index 517f91c2..8e6626ad 100644
--- a/lib/bank_account_information_importer.rb
+++ b/lib/bank_account_information_importer.rb
@@ -24,16 +24,16 @@ def import!(content)
iban: entityAccount && entityAccount[:iban],
reference: t[:remittanceInformationUnstructured],
text: entityName,
- receipt: t[:additionalInformation],
+ receipt: t[:additionalInformation]
})
ret += 1
end
balances = Hash[data[:balances] ? data[:balances].map { |b| [b[:balanceType], b[:balanceAmount]] } : []]
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 then
+ if value
balance = value
break
end
diff --git a/lib/bank_transaction_reference.rb b/lib/bank_transaction_reference.rb
index d033c544..5e44f0ef 100644
--- a/lib/bank_transaction_reference.rb
+++ b/lib/bank_transaction_reference.rb
@@ -13,7 +13,7 @@ def self.parse(data)
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/date_time_attribute_validate.rb b/lib/date_time_attribute_validate.rb
index 08138d02..17b40d36 100644
--- a/lib/date_time_attribute_validate.rb
+++ b/lib/date_time_attribute_validate.rb
@@ -10,51 +10,67 @@ def date_time_attribute(*attributes)
super
attributes.each do |attribute|
- validate -> { self.send("#{attribute}_datetime_value_valid") }
+ validate -> { send("#{attribute}_datetime_value_valid") }
# allow resetting the field to nil
before_validation do
- if self.instance_variable_get("@#{attribute}_is_set")
- date = self.instance_variable_get("@#{attribute}_date_value")
- time = self.instance_variable_get("@#{attribute}_time_value")
- if date.blank? && time.blank?
- self.send("#{attribute}=", nil)
- end
+ if instance_variable_get("@#{attribute}_is_set")
+ date = instance_variable_get("@#{attribute}_date_value")
+ time = instance_variable_get("@#{attribute}_time_value")
+ send("#{attribute}=", nil) if date.blank? && time.blank?
end
end
# remember old date and time values
define_method("#{attribute}_date_value=") do |val|
- self.instance_variable_set("@#{attribute}_is_set", true)
- self.instance_variable_set("@#{attribute}_date_value", val)
- self.send("#{attribute}_date=", val) rescue nil
+ 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|
- self.instance_variable_set("@#{attribute}_is_set", true)
- self.instance_variable_set("@#{attribute}_time_value", val)
- self.send("#{attribute}_time=", val) rescue nil
+ 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
- self.instance_variable_get("@#{attribute}_date_value") || self.send("#{attribute}_date").try { |e| e.strftime('%Y-%m-%d') }
+ instance_variable_get("@#{attribute}_date_value") || send("#{attribute}_date").try do |e|
+ e.strftime('%Y-%m-%d')
+ end
end
define_method("#{attribute}_time_value") do
- self.instance_variable_get("@#{attribute}_time_value") || self.send("#{attribute}_time").try { |e| e.strftime('%H:%M') }
+ instance_variable_get("@#{attribute}_time_value") || send("#{attribute}_time").try { |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
+ date = instance_variable_get("@#{attribute}_date_value")
+ unless date.blank? || begin
+ Date.parse(date)
+ rescue StandardError
+ nil
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
+ errors.add(attribute, 'is not a valid date') # @todo I18n
end
+ time = instance_variable_get("@#{attribute}_time_value")
+ return if time.blank? || begin
+ Time.parse(time)
+ rescue StandardError
+ nil
+ end
+
+ errors.add(attribute, 'is not a valid time') # @todo I18n
end
end
end
diff --git a/lib/foodsoft/expansion_variables.rb b/lib/foodsoft/expansion_variables.rb
index bcf67e7a..5ee54acb 100644
--- a/lib/foodsoft/expansion_variables.rb
+++ b/lib/foodsoft/expansion_variables.rb
@@ -39,7 +39,7 @@ module ExpansionVariables
'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
@@ -54,8 +54,8 @@ def self.get(var)
# @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/lib/foodsoft_config.rb
index 33fa9e45..3f6b893d 100644
--- a/lib/foodsoft_config.rb
+++ b/lib/foodsoft_config.rb
@@ -68,7 +68,7 @@ def init(filename = APP_CONFIG_FILE)
# 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 +77,7 @@ def init(filename = APP_CONFIG_FILE)
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 +115,7 @@ def select_multifoodcoop(foodcoop)
# @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 +137,19 @@ def []=(key, value)
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 +178,17 @@ def allowed_foodcoop?(foodcoop)
# @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.map { |k| [k, self[k]] }.to_h
+ keys.index_with { |k| self[k] }
end
# for using active_model_serializer in the api/v1/configs controller
@@ -286,7 +285,9 @@ def get_default_config
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/lib/foodsoft_date_util.rb b/lib/foodsoft_date_util.rb
index 98dc1c61..b1bc7430 100644
--- a/lib/foodsoft_date_util.rb
+++ b/lib/foodsoft_date_util.rb
@@ -6,11 +6,13 @@ def self.next_occurrence(start = Time.now, from = start, options = {})
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)
+ 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
diff --git a/lib/foodsoft_file.rb b/lib/foodsoft_file.rb
index 95d06c60..4276fcea 100644
--- a/lib/foodsoft_file.rb
+++ b/lib/foodsoft_file.rb
@@ -4,22 +4,47 @@ class FoodsoftFile
# 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|
+ articles = []
+ SpreadsheetFile.parse file, options do |row|
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
+ article = { availability: row[0]&.strip&.downcase != 'x',
+ 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]),
+ supplier_order_unit: ArticleUnitsLib.get_code_for_unit_name(row[10]),
+ price_unit: ArticleUnitsLib.get_code_for_unit_name(row[11]),
+ group_order_unit: ArticleUnitsLib.get_code_for_unit_name(row[12]),
+ group_order_granularity: row[13],
+ minimum_order_quantity: row[14],
+ billing_unit: ArticleUnitsLib.get_code_for_unit_name(row[15]),
+ article_category: row[16],
+ article_unit_ratios: FoodsoftFile.parse_ratios_cell(row[17]) }
+ articles << article
end
+
+ articles
+ end
+
+ def self.parse_ratios_cell(ratios_cell)
+ return [] if ratios_cell.blank?
+
+ ratios = ratios_cell.split(/(?[+-]?(?:[0-9]*[.])?[0-9]+) (?.*)/)
+ {
+ sort: index + 1,
+ quantity: md[:quantity],
+ unit: ArticleUnitsLib.get_code_for_unit_name(md[:unit_name])
+ }
+ end
+
+ ratios.reject { |ratio| ratio[:unit].nil? }
end
end
diff --git a/lib/foodsoft_mail_receiver.rb b/lib/foodsoft_mail_receiver.rb
index 560e7edd..7c5918a7 100644
--- a/lib/foodsoft_mail_receiver.rb
+++ b/lib/foodsoft_mail_receiver.rb
@@ -19,31 +19,29 @@ def start
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?
+ 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 @@ def self.find_handler(recipient)
@@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/lib/invoices_csv.rb
index aa20cd08..ebd1f0a9 100644
--- a/lib/invoices_csv.rb
+++ b/lib/invoices_csv.rb
@@ -32,7 +32,7 @@ def data
t.deposit,
t.deposit_credit,
t.paid_on,
- t.note,
+ t.note
]
end
end
diff --git a/lib/order_csv.rb b/lib/order_csv.rb
index 6ec96581..173b74fb 100644
--- a/lib/order_csv.rb
+++ b/lib/order_csv.rb
@@ -1,6 +1,9 @@
require 'csv'
class OrderCsv < RenderCSV
+ include ApplicationHelper
+ include ArticlesHelper
+
def header
[
OrderArticle.human_attribute_name(:units_to_order),
@@ -8,20 +11,22 @@ def header
Article.human_attribute_name(:name),
Article.human_attribute_name(:unit),
Article.human_attribute_name(:unit_quantity_short),
- ArticlePrice.human_attribute_name(:price),
+ ArticleVersion.human_attribute_name(:price),
OrderArticle.human_attribute_name(:total_price)
]
end
def data
- @object.order_articles.ordered.includes([:article, :article_price]).all.map do |oa|
+ @object.order_articles.ordered.includes(:article_version).all.map do |oa|
yield [
oa.units_to_order,
- oa.article.order_number,
- oa.article.name,
- oa.article.unit,
- oa.price.unit_quantity > 1 ? oa.price.unit_quantity : nil,
- number_to_currency(oa.price.price * oa.price.unit_quantity),
+ oa.article_version.order_number,
+ oa.article_version.name,
+ format_supplier_order_unit_with_ratios(oa.article_version),
+ # TODO-article-units: Why should we show the supplier the group order unit quantity?:
+ oa.article_version.convert_quantity(1, oa.article_version.supplier_order_unit,
+ oa.article_version.group_order_unit),
+ number_to_currency(oa.article_version.price),
number_to_currency(oa.total_price)
]
end
diff --git a/lib/order_pdf.rb b/lib/order_pdf.rb
index 034ca51f..a5ca69c1 100644
--- a/lib/order_pdf.rb
+++ b/lib/order_pdf.rb
@@ -1,4 +1,5 @@
class OrderPdf < RenderPDF
+ include ArticlesHelper
attr_reader :order
def initialize(order, options = {})
@@ -47,21 +48,44 @@ def nice_table(name, data, dimrows = [])
# @return [Number] Price to show
# @see https://github.com/foodcoops/foodsoft/issues/445
def order_article_price(order_article)
- order_article.price.fc_price
+ order_article.article_version.fc_group_order_price
end
def order_article_price_per_unit(order_article)
- "#{number_to_currency(order_article_price(order_article))} / #{order_article.article.unit}"
+ "#{number_to_currency(order_article_price(order_article))} / #{format_group_order_unit_with_ratios(order_article.article_version)}"
end
- def group_order_article_quantity_with_tolerance(goa)
- goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : "#{goa.quantity}"
+ def price_per_billing_unit(goa)
+ article_version = goa.order_article.article_version
+ "#{number_to_currency(article_version.convert_quantity(article_version.fc_price, article_version.billing_unit,
+ article_version.supplier_order_unit))} / #{format_billing_unit_with_ratios(article_version)}"
+ end
+
+ def billign_quantity_with_tolerance(goa)
+ article_version = goa.order_article.article_version
+ quantity = number_with_precision(
+ article_version.convert_quantity(goa.quantity, article_version.group_order_unit,
+ article_version.billing_unit), strip_insignificant_zeros: true, precision: 2
+ )
+ tolerance = number_with_precision(
+ article_version.convert_quantity(goa.tolerance, article_version.group_order_unit,
+ article_version.billing_unit), strip_insignificant_zeros: true, precision: 2
+ )
+ goa.tolerance > 0 ? "#{quantity} + #{tolerance}" : quantity
end
def group_order_article_result(goa)
number_with_precision goa.result, strip_insignificant_zeros: true
end
+ def billing_article_result(goa)
+ article_version = goa.order_article.article_version
+ number_with_precision(
+ article_version.convert_quantity(goa.result, article_version.group_order_unit,
+ article_version.billing_unit), precision: 2, strip_insignificant_zeros: true
+ )
+ end
+
def group_order_articles(ordergroup)
GroupOrderArticle
.includes(:group_order)
@@ -71,11 +95,11 @@ def group_order_articles(ordergroup)
def order_articles
OrderArticle
.ordered
- .includes(article: :supplier)
+ .includes(article_version: { article: :supplier })
.includes(group_order_articles: { group_order: :ordergroup })
.where(order: @orders)
- .order('suppliers.name, articles.name, groups.name')
- .preload(:article_price)
+ .order('suppliers.name, article_versions.name, groups.name')
+ .preload(:article_version)
end
def ordergroups(offset = nil, limit = nil)
@@ -134,9 +158,9 @@ def each_group_order_article_for_order_article(order_article, &block)
def each_group_order_article_for_ordergroup(ordergroup, &block)
group_order_articles(ordergroup)
- .includes(order_article: { article: [:supplier] })
- .order('suppliers.name, articles.name')
- .preload(order_article: [:article_price, :order])
+ .includes(order_article: { article_version: { article: :supplier } })
+ .order('suppliers.name, article_versions.name')
+ .preload(order_article: %i[article_version order])
.each(&block)
end
diff --git a/lib/order_txt.rb b/lib/order_txt.rb
index acde6ac8..adb25698 100644
--- a/lib/order_txt.rb
+++ b/lib/order_txt.rb
@@ -1,5 +1,5 @@
class OrderTxt
- def initialize(order, options = {})
+ def initialize(order, _options = {})
@order = order
end
@@ -8,23 +8,24 @@ def initialize(order, options = {})
def to_txt
supplier = @order.supplier
contact = FoodsoftConfig[:contact].symbolize_keys
- text = I18n.t('orders.fax.heading', :name => FoodsoftConfig[:name])
- text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" unless supplier.customer_number.blank?
+ text = I18n.t('orders.fax.heading', name: FoodsoftConfig[:name])
+ text += "\n#{Supplier.human_attribute_name(:customer_number)}: #{supplier.customer_number}" if supplier.customer_number.present?
text += "\n" + I18n.t('orders.fax.delivery_day')
text += "\n\n#{supplier.name}\n#{supplier.address}\n#{Supplier.human_attribute_name(:fax)}: #{supplier.fax}\n\n"
- text += "****** " + I18n.t('orders.fax.to_address') + "\n\n"
+ text += '****** ' + I18n.t('orders.fax.to_address') + "\n\n"
text += "#{FoodsoftConfig[:name]}\n#{contact[:street]}\n#{contact[:zip_code]} #{contact[:city]}\n\n"
- text += "****** " + I18n.t('orders.fax.articles') + "\n\n"
- text += "%8s %8s %s\n" % [I18n.t('orders.fax.number'), I18n.t('orders.fax.amount'), I18n.t('orders.fax.name')]
+ text += '****** ' + I18n.t('orders.fax.articles') + "\n\n"
+ text += format("%8s %8s %s\n", I18n.t('orders.fax.number'), I18n.t('orders.fax.amount'),
+ I18n.t('orders.fax.name'))
# now display all ordered articles
- @order.order_articles.ordered.includes([:article, :article_price]).each do |oa|
- text += "%8s %8d %s\n" % [oa.article.order_number, oa.units_to_order.to_i, oa.article.name]
+ @order.order_articles.ordered.includes(:article_version).each do |oa|
+ text += format("%8s %8d %s\n", oa.article_version.order_number, oa.units_to_order.to_i, oa.article_version.name)
end
text
end
# Helper method to test pdf via rails console: OrderTxt.new(order).save_tmp
def save_tmp
- File.open("#{Rails.root}/tmp/#{self.class.to_s.underscore}.txt", 'w') { |f| f.write(to_csv.force_encoding("UTF-8")) }
+ File.write("#{Rails.root.join("tmp/#{self.class.to_s.underscore}.txt")}", to_csv.force_encoding('UTF-8'))
end
end
diff --git a/lib/ordergroups_csv.rb b/lib/ordergroups_csv.rb
index c41d2e83..534bfc06 100644
--- a/lib/ordergroups_csv.rb
+++ b/lib/ordergroups_csv.rb
@@ -14,7 +14,7 @@ def header
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] }
end
@@ -33,7 +33,7 @@ def data
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/lib/render_csv.rb
index aad84251..5c1203b9 100644
--- a/lib/render_csv.rb
+++ b/lib/render_csv.rb
@@ -13,7 +13,7 @@ def initialize(object, options = {})
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
@@ -34,7 +34,7 @@ def data
# Helper method to test pdf via rails console: OrderCsv.new(order).save_tmp
def save_tmp
encoding = @options[:encoding] || 'UTF-8'
- File.open("#{Rails.root}/tmp/#{self.class.to_s.underscore}.csv", 'w') { |f| f.write(to_csv.force_encoding(encoding)) }
+ File.write("#{Rails.root.join("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.
diff --git a/lib/render_pdf.rb b/lib/render_pdf.rb
index b79e2a55..4ef65064 100644
--- a/lib/render_pdf.rb
+++ b/lib/render_pdf.rb
@@ -18,7 +18,7 @@ def skew
(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
@@ -120,7 +120,7 @@ def to_pdf
# Helper method to test pdf via rails console: OrderByGroups.new(order).save_tmp
def save_tmp
- File.open("#{Rails.root}/tmp/#{self.class.to_s.underscore}.pdf", 'w') { |f| f.write(to_pdf.force_encoding("UTF-8")) }
+ File.write("#{Rails.root.join("tmp/#{self.class.to_s.underscore}.pdf")}", to_pdf.force_encoding('UTF-8'))
end
# @todo avoid underscore instead of unicode whitespace in pdf :/
@@ -166,6 +166,6 @@ def pdf_add_page_breaks?(docid = nil)
end
def font_path(name)
- File.join(Rails.root, 'vendor', 'assets', 'fonts', name)
+ Rails.root.join('vendor', 'assets', 'fonts', name).to_s
end
end
diff --git a/lib/tasks/db_shared.rake b/lib/tasks/db_shared.rake
new file mode 100644
index 00000000..3f4a7e49
--- /dev/null
+++ b/lib/tasks/db_shared.rake
@@ -0,0 +1,54 @@
+task spec: ['shared:db:test:prepare']
+
+namespace :shared do
+ namespace :db do |ns|
+ %i[drop create setup migrate rollback seed version].each do |task_name|
+ task task_name do
+ Rake::Task["db:#{task_name}"].invoke
+ end
+ end
+
+ namespace :schema do
+ %i[load dump].each do |task_name|
+ task task_name do
+ Rake::Task["db:schema:#{task_name}"].invoke
+ end
+ end
+ end
+
+ namespace :test do
+ task :prepare do
+ Rake::Task['db:test:prepare'].invoke
+ end
+ end
+
+ # append and prepend proper tasks to all the tasks defined here above
+ ns.tasks.each do |task|
+ task.enhance ['shared:set_custom_config'] do
+ Rake::Task['shared:revert_to_original_config'].invoke
+ end
+ end
+ end
+
+ task set_custom_config: :environment do
+ # save current vars
+ @original_config = {
+ env_schema: ENV.fetch('SCHEMA', nil),
+ config: Rails.application.config.dup
+ }
+
+ # set config variables for custom database
+ ENV['SCHEMA'] = 'db_shared/schema.rb'
+ Rails.application.config.paths['db'] = ['db_shared']
+ Rails.application.config.paths['db/migrate'] = ['db_shared/migrate']
+ # If you are using Rails 5 or higher change `paths['db/seeds']` to `paths['db/seeds.rb']`
+ Rails.application.config.paths['db/seeds'] = ['db_shared/seeds.rb']
+ Rails.application.config.paths['config/database'] = ['config/database_shared.yml']
+ end
+
+ task revert_to_original_config: :environment do
+ # reset config variables to original values
+ ENV['SCHEMA'] = @original_config[:env_schema]
+ Rails.application.config = @original_config[:config]
+ end
+end
diff --git a/lib/tasks/foodsoft.rake b/lib/tasks/foodsoft.rake
index 5a5c6fa0..218bb39f 100644
--- a/lib/tasks/foodsoft.rake
+++ b/lib/tasks/foodsoft.rake
@@ -1,71 +1,71 @@
# 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
+ 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
- FoodsoftMailReceiver.received ENV['RECIPIENT'], STDIN.read
+ 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['SMTP_SERVER_HOST']
+ host = ENV.fetch('SMTP_SERVER_HOST', nil)
rake_say "Started SMTP server for incoming email on port #{port}."
server = FoodsoftMailReceiver.new(ports: port, hosts: host, max_processings: 1, logger: Rails.logger)
server.start
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
diff --git a/lib/tasks/foodsoft_setup.rake b/lib/tasks/foodsoft_setup.rake
index baa483d1..70b96f26 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,31 +26,31 @@ 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_development
@@ -59,39 +59,39 @@ namespace :foodsoft do
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 +101,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 +110,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 +119,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 +143,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 +151,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 cb2f280a..1e5ef44d 100644
--- a/lib/tasks/multicoops.rake
+++ b/lib/tasks/multicoops.rake
@@ -4,26 +4,24 @@
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_to_run = ENV['TASK']
+ 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_to_run = ENV['TASK']
- FoodsoftConfig.select_foodcoop ENV['FOODCOOP']
- rake_say "Run '#{task_to_run}' for #{ENV['FOODCOOP']}"
+ 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)}"
Rake::Task[task_to_run].execute
end
end
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/lib/tasks/temp_export_supplier.rake b/lib/tasks/temp_export_supplier.rake
new file mode 100644
index 00000000..f0826768
--- /dev/null
+++ b/lib/tasks/temp_export_supplier.rake
@@ -0,0 +1,33 @@
+namespace :temp_supplier do
+ task export: :environment do
+ supplier_id = ENV['SUPPLIER_ID']&.to_i
+ raise 'Missing supplier id' if supplier_id.nil?
+
+ s = Supplier.find_by_id(supplier_id)
+ article_hashes = s.articles.map do |a|
+ article_attributes = a.attributes.delete_if { |key| %w[quantity type deleted_at shared_updated_on].include?(key) }
+ latest_version_attributes = a.latest_article_version.attributes.delete_if do |key|
+ %w[article_id id].include?(key)
+ end
+ aur = a.article_unit_ratios.map do |ratio|
+ attributes = ratio.attributes
+ attributes.delete_if { |key| %w[id sort].include?(key) }
+ attributes
+ end
+
+ cat = ArticleCategory.find_by_id(latest_version_attributes['article_category_id'])
+ latest_version_attributes['article_category'] = cat.attributes.delete('id')
+ latest_version_attributes.delete('article_category_id')
+
+ article_attributes.merge(latest_version_attributes).merge(article_unit_ratios: aur)
+ end
+
+ puts JSON.pretty_generate({
+ api_version: 1,
+ data: {
+ changed_after: Time.now,
+ articles: article_hashes
+ }
+ })
+ end
+end
diff --git a/lib/tasks/units.rake b/lib/tasks/units.rake
new file mode 100644
index 00000000..3dde517c
--- /dev/null
+++ b/lib/tasks/units.rake
@@ -0,0 +1,136 @@
+namespace :units do
+ task clean_unece20_source: :environment do
+ base_unit_entries = []
+
+ un_ece_20_units = YAML.safe_load(ERB.new(File.read(File.expand_path('config/units-of-measure/un-ece-20.yml',
+ Rails.root))).result)
+ re = /^(?:([, 0-9]*)(?: x )!?)?([, 0-9⁰-⁹⁻¹²³]*)?(.*)?$/
+ un_ece_20_units.each do |unit|
+ fixed_conversion_factor = fix_conversion_factor(unit['ConversionFactor'])
+ md = fixed_conversion_factor.match(re)
+ multiplier = md[1]
+ potency = md[2]
+ base_unit = md[3]
+
+ multiplier&.strip!
+ potency&.strip!
+ base_unit&.strip!
+
+ if multiplier.blank? && (!potency.nil? && !/[⁰-⁹⁻¹²³]+/.match(potency))
+ multiplier = potency
+ potency = ''
+ end
+
+ multiplier&.gsub!(',', '.')
+ multiplier&.gsub!(' ', '')
+ multiplier = multiplier.blank? ? 1 : BigDecimal(multiplier)
+
+ unit['conversion'] = {}
+ unit['conversion']['unit'] = base_unit
+
+ unit['Symbol'] = '' if unit['Symbol'] == unit['CommonCode']
+
+ power_of_ten = potency.match(/^10 *?([⁰-⁹⁻¹²³]+)$/)&.[](1)
+ power_of_ten = superscript_to_number(power_of_ten) unless power_of_ten.nil?
+
+ base_unit_multiplier = multiplier
+ base_unit_multiplier *= (10**power_of_ten) unless power_of_ten.nil?
+ unit['conversion']['factor'] = base_unit_multiplier.to_s.to_f
+ is_base_unit = base_unit.present? && potency.blank? && multiplier == 1
+
+ base_unit_entries << unit if is_base_unit
+ end
+
+ # rubocop:disable Style/CombinableLoops
+ un_ece_20_units.each do |unit|
+ next if unit['conversion']['unit'].blank?
+
+ base_units = base_unit_entries.select { |entry| entry['conversion']['unit'] == unit['conversion']['unit'] }
+ unit['conversion']['base_units'] = base_units.pluck('CommonCode')
+ end
+
+ un_ece_20_units.each do |unit|
+ unit['conversion'].delete('unit')
+ end
+ # rubocop:enable Style/CombinableLoops
+
+ result = "# This file has been autogenerated by `rake units:clean_unece20_source`\n#{un_ece_20_units.to_yaml}"
+ File.write(File.expand_path('config/units-of-measure/un-ece-20-remastered.yml', Rails.root), result)
+ end
+
+ task build_i18n_from_csv: :environment do
+ translations = { de: {}, en: {} }
+ csv_options = { csv_options: { col_sep: ',' } }
+ SpreadsheetFile.parse 'config/units-of-measure/translations_piece.csv', csv_options do |row, index|
+ next if index == 0
+
+ code = row[0]
+ next if code.nil? # empty csv row
+
+ translation_en = { name: row[2].strip }
+ translation_en[:abbreviation] = row[3].strip if row[3].present?
+ translation_en[:aliases] = row[4].split(', ') if row[4].present?
+ translations[:en][code] = translation_en
+
+ translation_de = { name: row[5].strip }
+ translation_de[:abbreviation] = row[6].strip if row[6].present?
+ translation_de[:aliases] = row[7].split(', ') if row[7].present?
+ translations[:de][code] = translation_de
+ end
+
+ %i[en de].each do |locale|
+ SpreadsheetFile.parse "config/units-of-measure/translations_scalar-#{locale}.csv", csv_options do |row, index|
+ next if index == 0
+
+ code = row[0]
+ next if code.nil? # empty csv row
+
+ translation = { name: row[2].strip, long_name: row[1].strip }
+ translation[:symbols] = []
+ translation[:symbols] << row[3].strip if row[3].present?
+ alternate_symbols = row[4]&.strip
+ if alternate_symbols.present?
+ alternate_symbols.split(', ').each do |symbol|
+ translation[:symbols] << symbol.strip
+ end
+ end
+ translation[:aliases] = row[5].split(', ') if row[5].present?
+ translations[locale][code] = translation
+ end
+ result = "# This file has been autogenerated by `rake units:build_i18n_from_csv`\n#{{ unece_units: translations[locale] }.deep_stringify_keys.to_yaml}"
+ File.write(File.expand_path("config/units-of-measure/locales/unece_#{locale}.yml", Rails.root), result)
+ end
+ end
+end
+
+def superscript_hash
+ {
+ '⁻' => '-', '⁰' => '0', '¹' => '1', '²' => '2', '³' => '3', '⁴' => '4', '⁵' => '5', '⁶' => '6', '⁷' => '7', '⁸' => '8', '⁹' => '9'
+ }
+end
+
+# I know this is stupid, but I couldn't find a default ruby/rails method for this and wouldn't be
+# bothered to write a nicer unicode mapper for these few characters:
+def superscript_to_number(str)
+ str = str.chars.map { |c| superscript_hash[c] }.join
+
+ BigDecimal(str) unless str == ''
+end
+
+def number_to_superscript(str)
+ conversion_hash = superscript_hash.invert
+ str.chars.map { |c| conversion_hash[c] }.join
+end
+
+def fix_conversion_factor(conversion_factor)
+ # m2 -> m²:
+ conversion_factor = conversion_factor.gsub(%r{(\s|^|/)m2(\s|$|/)}, '\1m²\2')
+
+ # m3 -> m³
+ conversion_factor = conversion_factor.gsub(%r{(\s|^|/)m3(\s|$|/)}, '\1m³\2')
+
+ # 10-15 m³ -> 10⁻¹⁵ m³:
+ conversion_factor.gsub(%r{(\s|^|/)10 ?(- ?[0-9]+)(\s|$|/)}) do
+ Regexp.last_match[1] + '10' + number_to_superscript(Regexp.last_match[2]) + Regexp.last_match[3]
+ end
+end
diff --git a/lib/token_verifier.rb b/lib/token_verifier.rb
index a8a0f1eb..5f389943 100644
--- a/lib/token_verifier.rb
+++ b/lib/token_verifier.rb
@@ -19,11 +19,9 @@ def verify(message)
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 InvalidScope < ActiveSupport::MessageVerifier::InvalidSignature; end
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/plugins/current_orders/app/controllers/current_orders/articles_controller.rb b/plugins/current_orders/app/controllers/current_orders/articles_controller.rb
index 0e4b7dd9..3f48d995 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,14 +26,14 @@ def show_on_group_order_article_update
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])
- @order_articles = @order_articles.ordered.merge(@q.result).includes(:article, :article_price)
+ @order_articles = @order_articles.ordered.merge(@q.result).includes(:article, :article_version)
@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..20f6e172 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,12 +4,12 @@ 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 }
+ .group_by { |a| a.order_article.article_version.article_category.name }
.sort { |a, b| a[0] <=> b[0] }
end
@@ -18,8 +18,8 @@ def index
# 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..62d5d36e 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,12 +34,14 @@ def find_group_orders
@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_version])
+ .where(group_orders: { order_id: @order_ids, ordergroup_id: @ordergroup.id }).ordered.all
end
helper_method \
def articles_for_adding
- OrderArticle.includes(:article, :article_price).where(order_id: @order_ids)
+ OrderArticle.includes(:article, :article_version).where(order_id: @order_ids)
end
end
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..43966e3b 100644
--- a/plugins/current_orders/app/documents/multiple_orders_by_articles.rb
+++ b/plugins/current_orders/app/documents/multiple_orders_by_articles.rb
@@ -40,8 +40,8 @@ def body
sum[:quantity],
nil] # number_to_currency(sum[:price])]
- text "#{order_article.article.name} " +
- "(#{order_article.article.unit}; #{number_to_currency order_article.price.fc_price}; " +
+ text "#{order_article.article_version.name} " +
+ "(#{order_article.article_version.unit}; #{number_to_currency order_article.article_version.fc_price}; " +
units_history_line(order_article, plain: true) + ')',
size: fontsize(10), inline_format: true
table rows, cell_style: { size: fontsize(8), overflow: :shrink_to_fit } do |table|
@@ -69,19 +69,19 @@ def body
protected
def pdf_add_page_breaks?
- super 'order_by_articles'
+ super('order_by_articles')
end
def order_articles
OrderArticle.where(order_id: order).ordered
.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(:article_version) # preload not join, just in case it went missing
+ .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 @@ def group_order_articles_for(order_article)
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..866f788d 100644
--- a/plugins/current_orders/app/documents/multiple_orders_by_groups.rb
+++ b/plugins/current_orders/app/documents/multiple_orders_by_groups.rb
@@ -28,18 +28,18 @@ def body
has_tolerance = false
each_group_order_article_for(ordergroup) do |goa|
- has_tolerance = true if goa.order_article.price.unit_quantity > 1
- price = goa.order_article.price.fc_price
+ has_tolerance = true if goa.order_article.article_version.unit_quantity > 1
+ price = goa.order_article.article_version.fc_price
sub_total = goa.total_price
total += sub_total
- rows << [goa.order_article.article.name,
+ rows << [goa.order_article.article_version.name,
goa.group_order.order.name.truncate(10, omission: ''),
number_to_currency(price),
- goa.order_article.article.unit,
+ goa.order_article.article_version.unit,
goa.tolerance > 0 ? "#{goa.quantity} + #{goa.tolerance}" : goa.quantity,
group_order_article_result(goa),
number_to_currency(sub_total),
- goa.order_article.price.unit_quantity]
+ goa.order_article.article_version.unit_quantity]
dimrows << rows.length if goa.result == 0
end
next if rows.length == 0
@@ -56,7 +56,7 @@ def body
I18n.t('shared.articles.ordered'),
I18n.t('shared.articles.received'),
I18n.t('shared.articles_by.price_sum'),
- { image: "#{Rails.root}/app/assets/images/package-bg.png", scale: 0.6, position: :center }
+ { image: "#{Rails.root.join('app/assets/images/package-bg.png')}", scale: 0.6, position: :center }
]
# last column showing unit_quantity is useless if they're all one
@@ -100,7 +100,7 @@ def body
protected
def pdf_add_page_breaks?
- super 'order_by_groups'
+ super('order_by_groups')
end
def ordergroups
@@ -113,7 +113,7 @@ def ordergroups
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 @@ def each_ordergroup
.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_version order])
+ ordergroups.each(&block)
end
end
@@ -130,7 +130,7 @@ def group_order_articles_for(ordergroup)
@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..40c0fe45 100644
--- a/plugins/current_orders/app/helpers/current_orders_helper.rb
+++ b/plugins/current_orders/app/helpers/current_orders_helper.rb
@@ -6,7 +6,8 @@ def to_pay_message(ordergroup)
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/app/views/current_orders/articles/_article_info.html.haml b/plugins/current_orders/app/views/current_orders/articles/_article_info.html.haml
index 6b2f0f4c..a901e3a8 100644
--- a/plugins/current_orders/app/views/current_orders/articles/_article_info.html.haml
+++ b/plugins/current_orders/app/views/current_orders/articles/_article_info.html.haml
@@ -1,22 +1,22 @@
#article_info
%h2{style: 'margin-bottom: 0'}
- = t('current_orders.articles.show.title', name: order_article.article.name)
- %span.normal= order_article.article.unit
+ = t('current_orders.articles.show.title', name: order_article.article_version.name)
+ %span.normal= order_article.article_version.unit
-# @todo unduplicate from group_orders's order_article_info
%p
- - if order_article.article.manufacturer.blank?
+ - if order_article.article_version.manufacturer.blank?
= raw t '.supplied_by', supplier: content_tag(:em, supplier_link(order_article.order))
- - elsif order_article.article.supplier && order_article.article.supplier.name == order_article.article.manufacturer
+ - elsif order_article.article_version.article.supplier && order_article.article_version.article.supplier.name == order_article.article_version.manufacturer
= raw t '.supplied_and_made_by', manufacturer: content_tag(:em, supplier_link(order_article.order))
- else
- = raw t '.supplied_by_made_by', supplier: content_tag(:em, supplier_link(order_article.order)), manufacturer: content_tag(:em, order_article.article.manufacturer)
- - unless order_article.article.origin.blank?
- = raw t '.origin_in', origin: content_tag(:em, order_article.article.origin)
+ = raw t '.supplied_by_made_by', supplier: content_tag(:em, supplier_link(order_article.order)), manufacturer: content_tag(:em, order_article.article_version.manufacturer)
+ - unless order_article.article_version.origin.blank?
+ = raw t '.origin_in', origin: content_tag(:em, order_article.article_version.origin)
- pkg_info = pkg_helper(order_article.price)
= ", #{pkg_info}".html_safe unless pkg_info.blank?
= ", "
= Article.human_attribute_name(:fc_price_short) + ": "
- = number_to_currency(order_article.price.fc_price)
- = t '.unit', unit: order_article.article.unit
+ = number_to_currency(order_article.article_version.fc_price)
+ = t '.unit', unit: order_article.article_version.unit
diff --git a/plugins/current_orders/app/views/current_orders/articles/_articles.html.haml b/plugins/current_orders/app/views/current_orders/articles/_articles.html.haml
index ab00d38b..e53476ac 100644
--- a/plugins/current_orders/app/views/current_orders/articles/_articles.html.haml
+++ b/plugins/current_orders/app/views/current_orders/articles/_articles.html.haml
@@ -1,4 +1,4 @@
- @order_articles.includes(:order => :supplier).reorder('suppliers.name, articles.article_category_id, articles.name').group_by {|oa| oa.order.name}.each do |name, articles|
%li.nav-header= name
- articles.each do |oa|
- %li= link_to oa.article.name, current_orders_articles_path(order_id: oa.order_id, id: oa.id, anchor: 'order_article'), remote: true
+ %li= link_to oa.article_version. name, current_orders_articles_path(order_id: oa.order_id, id: oa.id, anchor: 'order_article'), remote: true
diff --git a/plugins/current_orders/app/views/current_orders/articles/_ordergroups.html.haml b/plugins/current_orders/app/views/current_orders/articles/_ordergroups.html.haml
index 908b7571..03194ef2 100644
--- a/plugins/current_orders/app/views/current_orders/articles/_ordergroups.html.haml
+++ b/plugins/current_orders/app/views/current_orders/articles/_ordergroups.html.haml
@@ -30,5 +30,5 @@
%td
%td
%td.center
- %span.units_delta{data: {'quantity-expected' => order_article.units * order_article.price.unit_quantity}}
+ %span.units_delta{data: {'quantity-expected' => order_article.units * order_article.article_version.unit_quantity}}
%td
diff --git a/plugins/current_orders/app/views/current_orders/articles/show.html.haml b/plugins/current_orders/app/views/current_orders/articles/show.html.haml
index e6d600f8..0c77180c 100644
--- a/plugins/current_orders/app/views/current_orders/articles/show.html.haml
+++ b/plugins/current_orders/app/views/current_orders/articles/show.html.haml
@@ -1,3 +1,3 @@
-- title t('.title', name: @order_article.article.name), false
+- title t('.title', name: @order_article.article_version.name), false
= render 'form'
diff --git a/plugins/current_orders/app/views/current_orders/group_orders/_result.html.haml b/plugins/current_orders/app/views/current_orders/group_orders/_result.html.haml
index 7d247d7d..b891160d 100644
--- a/plugins/current_orders/app/views/current_orders/group_orders/_result.html.haml
+++ b/plugins/current_orders/app/views/current_orders/group_orders/_result.html.haml
@@ -30,21 +30,21 @@
- group_order_sum += r[:sub_total]
%tr{class: cycle('even', 'odd', name: 'articles') + " order-article " + order_article_class_name(r[:quantity], r[:tolerance], r[:result])}
-# article_info is present in foodcoop-adam only
- %td.name{style: "width:40%", title: (article_info_title(oa.article) rescue nil)}
- = article_info_icon oa.article rescue nil
- = oa.article.name
+ %td.name{style: "width:40%", title: (article_info_title(oa.article_version) rescue nil)}
+ = article_info_icon oa.article_version rescue nil
+ = oa.article_version. name
%td
- = oa.article.unit
+ = oa.article_version. unit
%span{style: 'opacity: 0.4; margin-left: 1em;'}= pkg_helper(oa.price, soft_uq: true)
- %td= number_to_currency oa.price.fc_price
+ %td= number_to_currency oa.article_version.fc_price
%td
= r[:quantity]
- = "+ #{r[:tolerance]}" if oa.price.unit_quantity > 1
+ = "+ #{r[:tolerance]}" if oa.article_version.unit_quantity > 1
%td= r[:result] > 0 ? r[:result] : "0"
%td= number_to_currency(r[:sub_total])
- - unless oa.article.note.blank?
+ - unless oa.article_version. note.blank?
%tr{id: "note_#{oa.id}", class: "note even", style: "display:none"}
- %td{colspan: "6"}=h oa.article.note
+ %td{colspan: "6"}=h oa.article_version. note
%tr{class: cycle('even', 'odd', name: 'articles')}
%th{colspan: "5"}= heading_helper GroupOrder, :price
%th= number_to_currency(group_order_sum)
diff --git a/plugins/current_orders/app/views/current_orders/ordergroups/_article.html.haml b/plugins/current_orders/app/views/current_orders/ordergroups/_article.html.haml
index 4659f788..ad58d1a3 100644
--- a/plugins/current_orders/app/views/current_orders/ordergroups/_article.html.haml
+++ b/plugins/current_orders/app/views/current_orders/ordergroups/_article.html.haml
@@ -1,12 +1,12 @@
-# output row
%tr{:class => [cycle('even', 'odd', :name => 'articles'), if goa.result == 0 then 'unavailable' end], id: "goa_#{goa.id}"}
- %td.name= goa.order_article.article.name
+ %td.name= goa.order_article.article_version.name
%td{title: goa.order_article.order.name}= link_to goa.order_article.order.name.truncate(15), goa.order_article.order
- %td= goa.order_article.article.unit
+ %td= goa.order_article.article_version.unit
%td.center= "#{goa.quantity} + #{goa.tolerance}"
%td.center.input-delta= group_order_article_edit_result(goa)
%td.symbol
×
- %td= number_to_currency goa.order_article.price.fc_price
+ %td= number_to_currency goa.order_article.article_version.fc_price
%td.symbol =
%td.price{data: {value: goa.total_price}}= number_to_currency goa.total_price
diff --git a/plugins/current_orders/app/views/current_orders/ordergroups/_articles.html.haml b/plugins/current_orders/app/views/current_orders/ordergroups/_articles.html.haml
index 881baa0c..9db7e475 100644
--- a/plugins/current_orders/app/views/current_orders/ordergroups/_articles.html.haml
+++ b/plugins/current_orders/app/views/current_orders/ordergroups/_articles.html.haml
@@ -28,7 +28,7 @@
%tfoot
%tr
%td{colspan: 9}
- - new_article_data = articles_for_select2(articles_for_adding) {|a| "#{a.article.name} (#{a.article.unit}, #{number_to_currency a.price.fc_price})"}
+ - new_article_data = articles_for_select2(articles_for_adding) {|a| "#{a.article.name} (#{a.article.unit}, #{number_to_currency a.article_version.fc_price})"}
= form_for GroupOrderArticle.new, remote: true, html: {'data-submit-onchange' => true, style: 'margin: 0'} do |f|
= f.select :order_article_id,
options_for_select(new_article_data.map {|a| [a[:text], a[:id]]}),
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 @@ def navigation(primary, context)
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
old mode 100644
new mode 100755
index cb56e2e5..abec3d42
--- 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 @@ def valid_signature?
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 @@ def parse_payload
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 @@ def callback
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 @@ def sso
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/foodsoft_discourse.gemspec b/plugins/discourse/foodsoft_discourse.gemspec
index 6d2fb5d4..1e6113ce 100644
--- a/plugins/discourse/foodsoft_discourse.gemspec
+++ b/plugins/discourse/foodsoft_discourse.gemspec
@@ -1,21 +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.test_files = Dir["test/**/*"]
+ 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 @@ def redirect_to_login(options = {})
# 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
old mode 100644
new mode 100755
index 2834c5f3..861a530a
--- 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..7d1aea30 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
@@ -40,16 +40,16 @@ def create
@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 @@ def destroy
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/foodsoft_documents.gemspec b/plugins/documents/foodsoft_documents.gemspec
index c0a4f8fd..63d3b428 100644
--- a/plugins/documents/foodsoft_documents.gemspec
+++ b/plugins/documents/foodsoft_documents.gemspec
@@ -1,22 +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.test_files = Dir["test/**/*"]
+ 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 @@ def navigation(primary, context)
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
old mode 100644
new mode 100755
index fb6356f8..a9902fd3
--- 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 e7b60aa5..580c8a93 100644
--- a/plugins/links/app/controllers/admin/links_controller.rb
+++ b/plugins/links/app/controllers/admin/links_controller.rb
@@ -37,8 +37,8 @@ def destroy
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..27615517 100644
--- a/plugins/links/app/controllers/links_controller.rb
+++ b/plugins/links/app/controllers/links_controller.rb
@@ -5,9 +5,7 @@ def show
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 @@ def show
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
end
end
diff --git a/plugins/links/foodsoft_links.gemspec b/plugins/links/foodsoft_links.gemspec
index 04fc03f9..c879f4b1 100644
--- a/plugins/links/foodsoft_links.gemspec
+++ b/plugins/links/foodsoft_links.gemspec
@@ -1,21 +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.test_files = Dir["test/**/*"]
+ 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 @@ def navigation(primary, context)
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
old mode 100644
new mode 100755
index ac014bdd..9e2cbeab
--- 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 @@ def destroy
@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 2c594da3..bd043d58 100644
--- a/plugins/messages/app/controllers/messages_controller.rb
+++ b/plugins/messages/app/controllers/messages_controller.rb
@@ -10,21 +10,20 @@ def index
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"
+ 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
end
@@ -33,18 +32,18 @@ def create
@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: 'Nachricht ist privat!'
- end
+ return if @message.is_readable_for?(current_user)
+
+ redirect_to messages_url, alert: 'Nachricht ist privat!'
end
def toggle_private
diff --git a/plugins/messages/app/helpers/messages_helper.rb b/plugins/messages/app/helpers/messages_helper.rb
index d5371fe4..adb8fe88 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, 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..7b8a0356 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 @@ def initialize(match)
@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 @@ def received(data)
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,16 +60,14 @@ def get_mail_part(mail)
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
class BlankBodyException < MidiSmtpServer::SmtpdException
def initialize(msg = nil)
- super msg, 541, 'The recipient address rejected your message because of a blank plain body'
+ super(msg, 541, 'The recipient address rejected your message because of a blank plain body')
end
end
end
diff --git a/plugins/messages/app/models/message.rb b/plugins/messages/app/models/message.rb
index f6b03c10..b6554322 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,7 @@ class Message < ApplicationRecord
}
validates_presence_of :message_recipients, :subject, :body
- validates_length_of :subject, :in => 1..255
+ validates_length_of :subject, in: 1..255
after_initialize do
@recipients_ids ||= []
@@ -33,7 +33,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 +47,7 @@ def add_recipients(users)
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 +96,29 @@ def mail_to=(user_id)
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 +135,6 @@ def can_toggle_private?(user)
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/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 13050bf8..e7967191 100644
--- a/plugins/messages/foodsoft_messages.gemspec
+++ b/plugins/messages/foodsoft_messages.gemspec
@@ -1,26 +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.test_files = Dir["test/**/*"]
+ 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 @@ def navigation(primary, context)
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 @@ def show_user_link(user = @current_user)
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 @@ def show_user_link(user = @current_user)
# 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
old mode 100644
new mode 100755
index 2834c5f3..861a530a
--- 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 aac4ef0e..91934f6e 100644
--- a/plugins/polls/app/controllers/polls_controller.rb
+++ b/plugins/polls/app/controllers/polls_controller.rb
@@ -27,9 +27,9 @@ def create
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 @@ def destroy
@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 @@ def vote
@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
+ if @poll.single_select?
+ choices = {}
+ choice = params[:choice]
+ choices[choice] = '1' if choice
+ else
+ choices = params[:choices].try(:to_h) || {}
+ end
- redirect_to @poll
+ @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/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 @@ def change
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 e5b25173..607a1276 100644
--- a/plugins/polls/foodsoft_polls.gemspec
+++ b/plugins/polls/foodsoft_polls.gemspec
@@ -1,21 +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.test_files = Dir["test/**/*"]
+ 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 @@ def navigation(primary, context)
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
old mode 100644
new mode 100755
index 1c9d9839..fbf50e1d
--- 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..f07ac805 100644
--- a/plugins/printer/app/controllers/printer_controller.rb
+++ b/plugins/printer/app/controllers/printer_controller.rb
@@ -37,9 +37,7 @@ def update_job(data)
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
@@ -53,6 +51,7 @@ def bearer_token
def authenticate_printer
return head(:unauthorized) unless bearer_token
- return head(:forbidden) if bearer_token != FoodsoftConfig[:printer_token]
+
+ head(:forbidden) if bearer_token != FoodsoftConfig[:printer_token]
end
end
diff --git a/plugins/printer/app/controllers/printer_jobs_controller.rb b/plugins/printer/app/controllers/printer_jobs_controller.rb
index 17333fb5..da89d106 100644
--- a/plugins/printer/app/controllers/printer_jobs_controller.rb
+++ b/plugins/printer/app/controllers/printer_jobs_controller.rb
@@ -15,7 +15,7 @@ def create
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
@@ -39,7 +39,7 @@ def destroy
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/routes.rb b/plugins/printer/config/routes.rb
index 5b1a4784..84a634c5 100644
--- a/plugins/printer/config/routes.rb
+++ b/plugins/printer/config/routes.rb
@@ -4,7 +4,7 @@
get :socket, on: :collection
end
- resources :printer_jobs, only: [:index, :create, :show, :destroy] do
+ resources :printer_jobs, only: %i[index create show destroy] do
get :document, on: :member
end
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 @@ def change
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 cf249593..a6e54455 100644
--- a/plugins/printer/foodsoft_printer.gemspec
+++ b/plugins/printer/foodsoft_printer.gemspec
@@ -1,22 +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.test_files = Dir["test/**/*"]
+ 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 @@ class Engine < ::Rails::Engine
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 @@ def self.included(base) # :nodoc:
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..19b52ddc 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 @@ def add_uservoice_script
# 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].reject { |k, _v| k == '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 @@ def add_uservoice_script
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
old mode 100644
new mode 100755
index 5d2e31db..dd14bed8
--- 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..4a8c9a5f 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 @@ def show
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 @@ def show
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 @@ def edit
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 @@ def destroy
@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 @@ def all
@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,13 @@ def revert
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
+ variables if params[:id] == 'Help:Foodsoft_variables'
end
end
diff --git a/plugins/wiki/app/helpers/pages_helper.rb b/plugins/wiki/app/helpers/pages_helper.rb
index 551a169d..60ab988a 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
+ 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..fae004ce 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 @@ def diff
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..e0340188 100644
--- a/plugins/wiki/app/views/pages/all.rss.builder
+++ b/plugins/wiki/app/views/pages/all.rss.builder
@@ -1,14 +1,14 @@
-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.link wiki_page_path(page.permalink)
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 @@ def self.up
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 07f0c182..58be331d 100644
--- a/plugins/wiki/foodsoft_wiki.gemspec
+++ b/plugins/wiki/foodsoft_wiki.gemspec
@@ -1,27 +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.test_files = Dir["test/**/*"]
+ 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 @@ def navigation(primary, ctx)
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 Mailer
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 @@ def additonal_welcome_text(user)
# 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 @@ class WikiParser < WikiCloth::Parser
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 @@ class WikiParser < WikiCloth::Parser
end
end
- section_link do |section|
- ""
+ section_link do |_section|
+ ''
end
def to_html(render_options = {})
@@ -41,7 +41,7 @@ def link_attributes_if_number_sign_contained_in_nonexistent(page, referer)
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
index 2639a41d..8be3b463 100644
--- a/spec/api/v1/order_articles_spec.rb
+++ b/spec/api/v1/order_articles_spec.rb
@@ -6,12 +6,13 @@
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"] } }
+ let(:json_order_article_ids) { json_order_articles.map { |joa| joa['id'] } }
- describe "GET :index" do
- context "with param q[ordered]" do
+ 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_attributes! quantity: 0, tolerance: 0, units_to_order: 0
order_articles[1].update_attributes! quantity: 1, tolerance: 0, units_to_order: 0
@@ -19,35 +20,36 @@
order_articles[3].update_attributes! quantity: 0, tolerance: 0, units_to_order: 1
end
- it "(unset)" do
+ it '(unset)' do
get :index, params: { foodcoop: 'f' }
expect(json_order_articles.count).to eq 4
end
- it "all" do
+ 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
+ it 'supplier' do
get :index, params: { foodcoop: 'f', q: { ordered: 'supplier' } }
- expect(json_order_article_ids).to match_array [order_articles[3].id]
+ expect(json_order_article_ids).to contain_exactly(order_articles[3].id)
end
- it "member" do
+ 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
+ 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
+ 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
diff --git a/spec/api/v1/swagger_spec.rb b/spec/api/v1/swagger_spec.rb
index a481155c..5c0f043a 100644
--- a/spec/api/v1/swagger_spec.rb
+++ b/spec/api/v1/swagger_spec.rb
@@ -4,22 +4,22 @@
# 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))
+ YAML.load_file(swagger_path)
end
end
-describe 'API v1', type: :apivore, order: :defined do
+describe 'API v1', order: :defined, type: :apivore do
include ApiHelper
- subject { SwaggerCheckerFile.instance_for Rails.root.join('doc', 'swagger.v1.yml') }
+ 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 }
+ 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) }
@@ -29,7 +29,7 @@ def fetch_swagger!
context 'user/financial_overview' do
let(:api_scopes) { ['finance:user'] }
- let!(:user) { create :user, :ordergroup }
+ 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) }
@@ -39,26 +39,39 @@ def fetch_swagger!
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 }
+ 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 })) }
+
+ it {
+ expect(subject).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(: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) do
+ { '_data' => { financial_transaction: { amount: 1, financial_transaction_type_id: ft_1.financial_transaction_type.id,
+ note: 'note' } } }
+ end
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 })) }
- let(:create_params) { { '_data' => { financial_transaction: { amount: 1, financial_transaction_type_id: ft_1.financial_transaction_type.id, note: 'note' } } } }
+ it {
+ expect(subject).to validate(:get, '/user/financial_transactions/{id}', 404, api_auth({ 'id' => other_ft_1.id }))
+ }
+
+ it {
+ expect(subject).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)) }
@@ -70,19 +83,25 @@ def fetch_swagger!
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' } } } }
+ let(:create_params) do
+ { '_data' => { financial_transaction: { amount: 1, financial_transaction_type_id: -1, note: 'note' } } }
+ end
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 } } } }
+ let(:create_params) do
+ { '_data' => { financial_transaction: { amount: 1,
+ financial_transaction_type_id: ft_1.financial_transaction_type.id } } }
+ end
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
@@ -97,10 +116,14 @@ def fetch_swagger!
let(:api_scopes) { ['group_orders:user'] }
let(:order) { create(:order, article_count: 2) }
- let(:user_2) { create :user, :ordergroup }
+ 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! }
+ let!(:goa_2) { create(:group_order_article, order_article: order.order_articles[0], group_order: group_order_2) }
+
+ before do
+ group_order_2.update_price!
+ user_2.ordergroup.update_stats!
+ end
context 'without ordergroup' do
it { is_expected.to validate(:get, '/user/group_order_articles', 403, api_auth) }
@@ -108,31 +131,46 @@ def fetch_swagger!
end
context 'with ordergroup' do
- let(:user) { create :user, :ordergroup }
+ let(:user) { create(:user, :ordergroup) }
+ let(:update_params) do
+ { 'id' => goa.id, '_data' => { group_order_article: { quantity: goa.quantity + 1, tolerance: 0 } } }
+ end
+ let(:create_params) do
+ { '_data' => { group_order_article: { order_article_id: order.order_articles[1].id, quantity: 1 } } }
+ end
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! }
+ let!(:goa) { create(:group_order_article, order_article: order.order_articles[0], group_order: group_order) }
+
+ before do
+ group_order.update_price!
+ user.ordergroup.update_stats!
+ end
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 })) }
- let(:create_params) { { '_data' => { group_order_article: { order_article_id: order.order_articles[1].id, quantity: 1 } } } }
- let(:update_params) { { 'id' => goa.id, '_data' => { group_order_article: { quantity: goa.quantity + 1, tolerance: 0 } } } }
+ it {
+ expect(subject).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 } } } }
+ let(:create_params) do
+ { '_data' => { group_order_article: { order_article_id: order.order_articles[0].id, quantity: 1 } } }
+ end
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(:create_params) do
+ { '_data' => { group_order_article: { order_article_id: order.order_articles[0].id, quantity: -1 } } }
+ end
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)) }
@@ -149,6 +187,7 @@ def fetch_swagger!
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 })) }
@@ -156,6 +195,7 @@ def fetch_swagger!
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 })) }
@@ -165,7 +205,9 @@ def fetch_swagger!
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 }) })
+ it_handles_invalid_token_and_scope(:delete, '/user/group_order_articles/{id}', lambda {
+ api_auth({ 'id' => goa.id })
+ })
end
end
@@ -188,16 +230,21 @@ def fetch_swagger!
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 }
+ 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 })) }
+
+ it {
+ expect(subject).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
@@ -207,20 +254,23 @@ def fetch_swagger!
end
context 'financial_transaction_classes' do
- let!(:cla_1) { create :financial_transaction_class }
- let!(:cla_2) { create :financial_transaction_class }
+ 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 {
+ expect(subject).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 }
+ 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 })) }
@@ -232,7 +282,7 @@ def fetch_swagger!
context 'orders' do
let(:api_scopes) { ['orders:read'] }
- let!(:order) { create :order }
+ 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 })) }
@@ -258,8 +308,8 @@ def fetch_swagger!
end
context 'article_categories' do
- let!(:cat_1) { create :article_category }
- let!(:cat_2) { create :article_category }
+ 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 })) }
@@ -273,7 +323,7 @@ def fetch_swagger!
# 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
+ expect(subject).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
index a5952b67..62b50e4c 100644
--- a/spec/api/v1/user/financial_transactions_spec.rb
+++ b/spec/api/v1/user/financial_transactions_spec.rb
@@ -6,29 +6,29 @@
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(: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
+ shared_examples 'financial_transactions endpoint success' do
before { request }
- it "returns status 200" do
+ it 'returns status 200' do
expect(response.status).to eq 200
end
end
- shared_examples "financial_transactions create/update success" do
- include_examples "financial_transactions endpoint success"
+ shared_examples 'financial_transactions create/update success' do
+ include_examples 'financial_transactions endpoint success'
- it "returns the financial_transaction" do
+ 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
@@ -37,7 +37,7 @@
expect(json_ft['user_id']).to eq user.id
end
- it "updates the financial_transaction" do
+ 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
@@ -47,58 +47,62 @@
end
end
- shared_examples "financial_transactions endpoint failure" do |status|
+ 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 {
+ it 'does not change the ordergroup' do
+ expect { request }.not_to(change do
user.ordergroup.attributes
- }
+ end)
end
- it "does not change the financial_transactions of ordergroup" do
- expect { request }.to_not change {
+ it 'does not change the financial_transactions of ordergroup' do
+ expect { request }.not_to(change do
user.ordergroup.financial_transactions.count
- }
+ end)
end
end
- describe "POST :create" do
+ 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
+ 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"
+ context 'with no existing financial transaction' do
+ include_examples 'financial_transactions create/update success'
end
- context "with existing financial transaction" do
+ context 'with existing financial transaction' do
before { user.ordergroup.add_financial_transaction! 5000, 'for ordering', user, ftt3 }
- include_examples "financial_transactions create/update success"
+
+ include_examples 'financial_transactions create/update success'
end
- context "with invalid financial transaction type" do
+ 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
+
+ include_examples 'financial_transactions endpoint failure', 404
end
- context "without note" do
+ context 'without note' do
let(:ft_params) { { amount: amount, financial_transaction_type_id: ftt1.id } }
- include_examples "financial_transactions endpoint failure", 422
+
+ 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
+
+ include_examples 'financial_transactions endpoint failure', 403
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
index 27fbe843..d440d60b 100644
--- a/spec/api/v1/user/group_order_articles_spec.rb
+++ b/spec/api/v1/user/group_order_articles_spec.rb
@@ -4,6 +4,8 @@
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) }
@@ -13,46 +15,50 @@
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! }
+ let!(:goa_other) do
+ create(:group_order_article, group_order: go_other, order_article: oa_1, quantity: other_quantity,
+ tolerance: other_tolerance)
+ end
- let(:json_goa) { json_response['group_order_article'] }
- let(:json_oa) { json_response['order_article'] }
+ before do
+ go_other.update_price!
+ user_other.ordergroup.update_stats!
+ end
- shared_examples "group_order_articles endpoint success" do
+ shared_examples 'group_order_articles endpoint success' do
before { request }
- it "returns status 200" do
+ it 'returns status 200' do
expect(response.status).to eq 200
end
- it "returns the order_article" do
+ 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
+ it 'updates the group_order' do
go = nil
- expect {
+ expect do
request
go = user.ordergroup.group_orders.where(order: order).last
- }.to change { go&.updated_by }.to(user)
- .and change { go&.price }
+ end.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"
+ shared_examples 'group_order_articles create/update success' do
+ include_examples 'group_order_articles endpoint success'
- it "returns the group_order_article" do
+ 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
+ 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
@@ -60,147 +66,170 @@
end
end
- shared_examples "group_order_articles endpoint failure" do |status|
+ 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 {
+ it 'does not change the group_order' do
+ expect { request }.not_to(change do
go = user.ordergroup.group_orders.where(order: order).last
go&.attributes
- }
+ end)
end
- it "does not change the group_order_article" do
- expect { request }.to_not change {
+ it 'does not change the group_order_article' do
+ expect { request }.not_to(change do
goa = GroupOrderArticle.joins(:group_order)
.where(order_article_id: oa_1.id, group_orders: { ordergroup: user.ordergroup }).last
goa&.attributes
- }
+ end)
end
end
- describe "POST :create" do
+ 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"
+ context 'with no existing group_order' do
+ include_examples 'group_order_articles create/update success'
end
- context "with an existing group_order" do
+ 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"
+
+ include_examples 'group_order_articles create/update success'
end
- context "with an existing group_order_article" do
+ 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
+
+ before do
+ go.update_price!
+ user.ordergroup.update_stats!
+ end
+
+ include_examples 'group_order_articles endpoint failure', 422
end
- context "with invalid parameter values" do
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ include_examples 'group_order_articles endpoint failure', 403
end
end
- describe "PATCH :update" do
+ 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! }
- let(:goa_params) { { quantity: new_quantity, tolerance: new_tolerance } }
- let(:request) { patch :update, params: { id: goa.id, group_order_article: goa_params, foodcoop: 'f' } }
+ before do
+ go.update_price!
+ user.ordergroup.update_stats!
+ end
- context "happy flow" do
- include_examples "group_order_articles create/update success"
+ context 'happy flow' do
+ include_examples 'group_order_articles create/update success'
end
- context "with invalid parameter values" do
+ 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
+
+ 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
+
+ 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
+
+ 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
+
+ include_examples 'group_order_articles endpoint failure', 403
end
end
- describe "DELETE :destroy" do
+ 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! }
- let(:request) { delete :destroy, params: { id: goa.id, foodcoop: 'f' } }
+ before do
+ go.update_price!
+ user.ordergroup.update_stats!
+ end
- shared_examples "group_order_articles destroy success" do
- include_examples "group_order_articles endpoint success"
+ shared_examples 'group_order_articles destroy success' do
+ include_examples 'group_order_articles endpoint success'
- it "does not return the group_order_article" do
+ it 'does not return the group_order_article' do
expect(json_goa).to be_nil
end
- it "deletes the group_order_article" do
+ 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"
+ 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
+
+ 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"
+
+ 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"
+
+ 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
index 32d1053a..afc764f9 100644
--- a/spec/api/v1/user/ordergroup_spec.rb
+++ b/spec/api/v1/user/ordergroup_spec.rb
@@ -2,26 +2,26 @@
describe Api::V1::User::OrdergroupController, type: :controller do
include ApiOAuth
- let(:user) { create :user, :ordergroup }
+ 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(: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
+ 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! }
-
- let(:json_financial_overview) { json_response['financial_overview'] }
before do
+ go.update_price!
+ user.ordergroup.update_stats!
og = user.ordergroup
og.add_financial_transaction!(-1, '-1', user, ftt1)
og.add_financial_transaction!(2, '2', user, ftt1)
@@ -36,12 +36,12 @@
og.add_financial_transaction!(300, '300', user, ftt3)
end
- it "returns correct values" do
+ 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] }]
+ ftcs = json_financial_overview['financial_transaction_class_sums'].index_by { |x| x['id'] }
ftcs1 = ftcs[ftc1.id]
expect(ftcs1['name']).to eq ftc1.name
diff --git a/spec/app_config.yml b/spec/app_config.yml
index 2e146be9..e6ceada0 100644
--- a/spec/app_config.yml
+++ b/spec/app_config.yml
@@ -27,6 +27,16 @@ development:
test:
<<: *defaults
+ shared_lists:
+ adapter: mysql2
+ encoding: utf8mb4
+ reconnect: false
+ database: db_shared
+ pool: 5
+ host: mariadb
+ username: root
+ password: secret
+
production:
<<: *defaults
diff --git a/spec/factories/article.rb b/spec/factories/article.rb
index 3c85288d..c920968f 100644
--- a/spec/factories/article.rb
+++ b/spec/factories/article.rb
@@ -2,33 +2,35 @@
FactoryBot.define do
factory :_article do
- unit { Faker::Unit.unit }
- price { rand(0.1..26.0).round(2) }
- tax { [6, 21].sample }
- deposit { rand(10) < 8 ? 0 : [0.0, 0.80, 1.20, 12.00].sample }
- unit_quantity { rand(5) < 3 ? 1 : rand(1..20) }
-
factory :article do
- sequence(:name) { |n| Faker::Lorem.words(number: rand(2..4)).join(' ') + " ##{n}" }
supplier
- article_category
- end
- factory :shared_article, class: SharedArticle do
- sequence(:name) { |n| Faker::Lorem.words(number: rand(2..4)).join(' ') + " s##{n}" }
- order_number { Faker::Lorem.characters(number: rand(1..12)) }
- shared_supplier
+ transient do
+ article_version_count { 1 }
+ order_number { nil }
+ unit_quantity { nil }
+ end
+
+ after(:create) do |article, evaluator|
+ create_list(:article_version, evaluator.article_version_count, article: article,
+ order_number: evaluator.order_number, unit_quantity: evaluator.unit_quantity)
+
+ article.reload
+ end
end
- factory :stock_article, class: StockArticle do
- sequence(:name) { |n| Faker::Lorem.words(number: rand(2..4)).join(' ') + " ##{n}" }
- unit_quantity { 1 }
+ factory :stock_article, class: 'StockArticle' do
supplier
- article_category
- end
- end
- factory :article_category do
- sequence(:name) { |n| Faker::Lorem.characters(number: rand(2..12)) + " ##{n}" }
+ transient do
+ stock_article_version_count { 1 }
+ end
+
+ after(:create) do |stock_article, evaluator|
+ create_list(:article_version, evaluator.stock_article_version_count, article: stock_article)
+
+ stock_article.reload
+ end
+ end
end
end
diff --git a/spec/factories/article_category.rb b/spec/factories/article_category.rb
new file mode 100644
index 00000000..1c825e8e
--- /dev/null
+++ b/spec/factories/article_category.rb
@@ -0,0 +1,7 @@
+require 'factory_bot'
+
+FactoryBot.define do
+ factory :article_category do
+ sequence(:name) { |n| Faker::Lorem.characters(number: rand(2..12)) + " ##{n}" }
+ end
+end
diff --git a/spec/factories/article_unit_ratio.rb b/spec/factories/article_unit_ratio.rb
new file mode 100644
index 00000000..a0bbff15
--- /dev/null
+++ b/spec/factories/article_unit_ratio.rb
@@ -0,0 +1,10 @@
+require 'factory_bot'
+
+FactoryBot.define do
+ factory :article_unit_ratio do
+ unit { Faker::Unit.unit }
+ sort { 1 }
+ quantity { 1 } # TODO
+ article_version
+ end
+end
diff --git a/spec/factories/article_version.rb b/spec/factories/article_version.rb
new file mode 100644
index 00000000..df620e10
--- /dev/null
+++ b/spec/factories/article_version.rb
@@ -0,0 +1,35 @@
+require 'factory_bot'
+FactoryBot.define do
+ factory :article_version do
+ sequence(:name) { |n| Faker::Lorem.words(number: rand(2..4)).join(' ') + " ##{n}" }
+ supplier_order_unit { 'XBO' } # TODO
+ group_order_unit { 'XBO' } # TODO
+ price { rand(0.1..26.0).round(2) }
+ tax { [6, 21].sample }
+ deposit { rand(10) < 8 ? 0 : [0.0, 0.80, 1.20, 12.00].sample }
+ article_category
+ article
+
+ transient do
+ article_unit_ratio_count { 1 }
+ unit_quantity { 1 }
+ end
+
+ after(:create) do |article_version, evaluator|
+ unless evaluator.unit_quantity.nil?
+ article_version.supplier_order_unit = nil
+ article_version.save
+ end
+ build_list(:article_unit_ratio, evaluator.article_unit_ratio_count,
+ article_version: article_version) do |record, i|
+ if !evaluator.unit_quantity.nil? && i == 0
+ record.quantity = evaluator.unit_quantity
+ record.unit = 'XPP'
+ end
+ record.sort = i + 1
+ record.save
+ record.reload
+ end
+ end
+ end
+end
diff --git a/spec/factories/doorkeeper.rb b/spec/factories/doorkeeper.rb
index 95de4eb6..81cefd35 100644
--- a/spec/factories/doorkeeper.rb
+++ b/spec/factories/doorkeeper.rb
@@ -2,12 +2,12 @@
require 'doorkeeper'
FactoryBot.define do
- factory :oauth2_application, class: Doorkeeper::Application do
+ factory :oauth2_application, class: 'Doorkeeper::Application' do
name { Faker::App.name }
redirect_uri { 'https://example.com:1234/app' }
end
- factory :oauth2_access_token, class: Doorkeeper::AccessToken do
+ factory :oauth2_access_token, class: 'Doorkeeper::AccessToken' do
application factory: :oauth2_application
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 @@
# 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 @@
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 @@
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 c8b680fc..be082f77 100644
--- a/spec/factories/supplier.rb
+++ b/spec/factories/supplier.rb
@@ -10,8 +10,7 @@
article_count { 0 }
end
- before :create do |supplier, evaluator|
- next if supplier.class == SharedSupplier
+ before :create do |supplier, _evaluator|
next if supplier.supplier_category_id?
supplier.supplier_category = create :supplier_category
@@ -20,10 +19,8 @@
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
end
factory :supplier_category do
diff --git a/spec/factories/user.rb b/spec/factories/user.rb
index eb12196f..fec68254 100644
--- a/spec/factories/user.rb
+++ b/spec/factories/user.rb
@@ -10,21 +10,21 @@
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|
- trait "role_#{role}".to_sym do
- after :create do |user, evaluator|
- create :workgroup, "role_#{role}" => true, user_ids: [user.id]
+ %i[ordergroup finance invoices article_meta suppliers pickups orders].each do |role|
+ trait :"role_#{role}" do
+ after :create do |user, _evaluator|
+ create(:workgroup, "role_#{role}" => true, user_ids: [user.id])
end
end
end
@@ -42,7 +42,9 @@
sequence(:name) { |n| "Order group ##{n}" }
# workaround to avoid needing to save the ordergroup
# avoids e.g. error after logging in related to applebar
- after :create do |group| Ordergroup.find(group.id).update_stats! end
+ after :create do |group|
+ Ordergroup.find(group.id).update_stats!
+ end
end
end
end
diff --git a/spec/integration/articles_spec.rb b/spec/integration/articles_spec.rb
index 820317b7..c453c42a 100644
--- a/spec/integration/articles_spec.rb
+++ b/spec/integration/articles_spec.rb
@@ -1,12 +1,13 @@
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 }
+describe 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
+ describe ':index', :js do
before { visit supplier_articles_path(supplier_id: supplier.id) }
it 'can visit supplier articles path' do
@@ -16,16 +17,16 @@
it 'can create a new article' do
click_on I18n.t('articles.index.new')
- expect(page).to have_selector('form#new_article')
- article = FactoryBot.build :article, supplier: supplier, article_category: article_category
+ expect(page).to have_css('form#new_article')
+ article = FactoryBot.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_version', 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();')
@@ -37,6 +38,7 @@
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
@@ -45,24 +47,26 @@
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('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
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes'
@@ -73,34 +77,36 @@
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')
find('input[type="submit"]').click
diff --git a/spec/integration/balancing_spec.rb b/spec/integration/balancing_spec.rb
index 87a7757f..78ea97ad 100644
--- a/spec/integration/balancing_spec.rb
+++ b/spec/integration/balancing_spec.rb
@@ -1,17 +1,18 @@
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 }
+describe 'settling an order', :js 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(: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)
goa2.update_quantities(1, 0)
@@ -19,6 +20,10 @@
order.finish!(admin)
goa1.reload
goa2.reload
+ login admin
+ visit new_finance_order_path(order_id: order.id)
+ visit new_finance_order_path(order_id: order.id)
+ login admin
end
it 'has correct order result' do
@@ -28,25 +33,22 @@
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}")
+ expect(page).to have_css("#order_article_#{oa.id}")
end
it 'shows order result' do
click_link article.name
- expect(page).to have_selector("#group_order_articles_#{oa.id}")
+ expect(page).to have_css("#group_order_articles_#{oa.id}")
within("#group_order_articles_#{oa.id}") do
# make sure these ordergroup names are in the list for this product
expect(page).to have_content(go1.ordergroup_name)
expect(page).to have_content(go2.ordergroup_name)
# and that their order results match what we expect
- expect(page).to have_selector("#r_#{goa1.id}")
+ expect(page).to have_css("#r_#{goa1.id}")
expect(find("#r_#{goa1.id}").value.to_f).to eq(3)
- expect(page).to have_selector("#r_#{goa2.id}")
+ expect(page).to have_css("#r_#{goa2.id}")
expect(find("#r_#{goa2.id}").value.to_f).to eq(1)
end
end
@@ -57,7 +59,7 @@
click_link I18n.t('ui.delete')
end
end
- expect(page).to_not have_selector("#order_article_#{oa.id}")
+ expect(page).to have_no_css("#order_article_#{oa.id}")
expect(OrderArticle.exists?(oa.id)).to be true
oa.reload
expect(oa.quantity).to eq(4)
@@ -75,17 +77,17 @@
click_link I18n.t('ui.delete')
end
end
- expect(page).to_not have_selector("#order_article_#{oa.id}")
+ expect(page).to have_no_css("#order_article_#{oa.id}")
expect(OrderArticle.exists?(oa.id)).to be false
end
it 'keeps ordered quantities when GroupOrderArticle is deleted from resulting order' do
click_link article.name
- expect(page).to have_selector("#group_order_article_#{goa1.id}")
+ expect(page).to have_css("#group_order_article_#{goa1.id}")
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).to have_no_css("#group_order_article_#{goa1.id}")
expect(OrderArticle.exists?(oa.id)).to be true
expect(GroupOrderArticle.exists?(goa1.id)).to be true
goa1.reload
@@ -95,13 +97,13 @@
end
it 'deletes a GroupOrderArticle with no ordered amounts' do
- goa1.update_attributes({ :quantity => 0, :tolerance => 0 })
+ goa1.update_attributes({ quantity: 0, tolerance: 0 })
click_link article.name
- expect(page).to have_selector("#group_order_article_#{goa1.id}")
+ expect(page).to have_css("#group_order_article_#{goa1.id}")
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).to have_no_css("#group_order_article_#{goa1.id}")
expect(OrderArticle.exists?(oa.id)).to be true
expect(GroupOrderArticle.exists?(goa1.id)).to be false
end
@@ -111,14 +113,14 @@
click_link I18n.t('ui.edit')
end
within("#edit_order_article_#{oa.id}") do
- find('#order_article_units_to_order').set(0)
+ find_by_id('order_article_units_to_order').set(0)
sleep 0.25
find('input[type="submit"]').click
end
- expect(page).to have_selector("#order_article_#{oa.id}")
+ expect(page).to have_css("#order_article_#{oa.id}")
# make sure it still works after reloading
visit new_finance_order_path(order_id: order.id)
- expect(page).to have_selector("#order_article_#{oa.id}")
+ expect(page).to have_css("#order_article_#{oa.id}")
expect(OrderArticle.exists?(oa.id)).to be true
oa.reload
expect(oa.units_to_order).to eq(0)
@@ -130,19 +132,19 @@
within("#group_order_articles_#{oa.id}") do
click_link I18n.t('finance.balancing.group_order_articles.add_group')
end
- expect(page).to have_selector('form#new_group_order_article')
+ expect(page).to have_css('form#new_group_order_article')
within('#new_group_order_article') do
- select user.ordergroup.name, :from => 'group_order_article_ordergroup_id'
- find('#group_order_article_result').set(8)
+ 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).to have_no_css('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(page).to have_css("#group_order_article_#{goa.id}")
expect(find("#r_#{goa.id}").value.to_f).to eq 8
end
@@ -151,7 +153,7 @@
within("#group_order_articles_#{oa.id}") do
find("#r_#{goa1.id}").set(5).send_keys(:tab) # tab to blur and let js update
end
- expect(page).to have_selector('#summaryChangedWarning') # becomes visible after request is done
+ expect(page).to have_css('#summaryChangedWarning') # becomes visible after request is done
expect(goa1.reload.result).to eq 5
expect(find("#group_order_articles_#{oa.id} tfoot td:nth-child(3)").text.to_f).to eq 6
end
@@ -161,23 +163,23 @@
within("#group_order_article_#{goa1.id}") do
4.times { find('button[data-increment]').click }
end
- expect(page).to have_selector('#summaryChangedWarning') # becomes visible after request is done
+ expect(page).to have_css('#summaryChangedWarning') # becomes visible after request is done
expect(goa1.reload.result).to eq 7
expect(find("#group_order_articles_#{oa.id} tfoot td:nth-child(3)").text.to_f).to eq 8
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).to have_no_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')
+ expect(page).to have_css('form#new_order_article')
within('#new_order_article') do
- find('#order_article_article_id').select(new_article.name)
+ find_by_id('order_article_article_id').select(new_article.name)
sleep 0.25
find('input[type="submit"]').click
end
- expect(page).to_not have_selector('form#new_order_article')
+ expect(page).to have_no_css('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..d7d496a3 100644
--- a/spec/integration/config_spec.rb
+++ b/spec/integration/config_spec.rb
@@ -1,9 +1,9 @@
require_relative '../spec_helper'
-feature 'admin/configs' do
+describe '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 @@ def get_full_config
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.reject! { |_k, v| v.blank? }
end
end
- h.reject! { |k, v| v.blank? }
+ h.reject! { |_k, v| v.blank? }
h
end
end
diff --git a/spec/integration/login_spec.rb b/spec/integration/login_spec.rb
index 0195ec8c..e0e63ffd 100644
--- a/spec/integration/login_spec.rb
+++ b/spec/integration/login_spec.rb
@@ -1,18 +1,19 @@
require_relative '../spec_helper'
-feature LoginController do
- let(:user) { create :user }
+describe LoginController do
+ let(:user) { create(:user) }
describe 'forgot password' do
before { visit forgot_password_path }
+
it 'is accessible' do
- expect(page).to have_selector 'input[id=user_email]'
+ expect(page).to have_css 'input[id=user_email]'
end
it 'sends a reset email' do
fill_in 'user_email', with: user.email
find('input[type=submit]').click
- expect(page).to have_selector '.alert-success'
+ expect(page).to have_css '.alert-success'
email = ActionMailer::Base.deliveries.first
expect(email.to).to eq [user.email]
end
@@ -21,18 +22,22 @@
describe 'and reset it' do
let(:token) { user.reset_password_token }
let(:newpass) { user.new_random_password }
- before { user.request_password_reset! }
- before { visit new_password_path(id: user.id, token: token) }
+
+ before do
+ user.request_password_reset!
+ visit new_password_path(id: user.id, token: token)
+ end
it 'is accessible' do
- expect(page).to have_selector 'input[type=password]'
+ expect(page).to have_css 'input[type=password]'
end
describe 'with wrong token' do
let(:token) { 'foobar' }
+
it 'is not accessible' do
- expect(page).to have_selector '.alert-error'
- expect(page).to_not have_selector 'input[type=password]'
+ expect(page).to have_css '.alert-error'
+ expect(page).to have_no_css 'input[type=password]'
end
end
diff --git a/spec/integration/order_spec.rb b/spec/integration/order_spec.rb
index dd768997..12e027a2 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 }
+describe Order, :js 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(: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 }
@@ -32,7 +32,7 @@
visit new_order_path(supplier_id: article.supplier_id)
expect(page).to have_text I18n.t('orders.new.title')
find('input[type="submit"]').click
- expect(page).to have_selector('.alert-success')
+ expect(page).to have_css('.alert-success')
expect(Order.count).to eq 1
expect(Order.first.supplier).to eq article.supplier
end
@@ -40,7 +40,7 @@
it 'can close an order' do
setup_and_close_order
expect(order).to be_finished
- expect(page).not_to have_link I18n.t('orders.index.action_end')
+ expect(page).to have_no_link I18n.t('orders.index.action_end')
expect(oa.units_to_order).to eq 1
end
@@ -53,7 +53,7 @@ def setup_and_close_order
accept_confirm do
click_link_or_button I18n.t('orders.index.action_end')
end
- expect(page).to have_selector('.alert-success')
+ expect(page).to have_css('.alert-success')
order.reload
oa.reload
end
diff --git a/spec/integration/product_distribution_example_spec.rb b/spec/integration/product_distribution_example_spec.rb
index e15642f1..b3e85def 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 }
+describe 'product distribution', :js 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(:order) { create(:order, supplier: supplier, article_ids: [article.id]) }
let(:oa) { order.order_articles.first }
@@ -27,14 +27,14 @@
2.times { find("[data-increase_quantity='#{oa.id}']").click }
3.times { find("[data-increase_tolerance='#{oa.id}']").click }
find('input[type=submit]').click
- expect(page).to have_selector('body')
+ expect(page).to have_css('body')
# gruppe b bestellt 2(0)
login user_b
visit new_group_order_path(order_id: order.id)
scrolldown
2.times { find("[data-increase_quantity='#{oa.id}']").click }
find('input[type=submit]').click
- expect(page).to have_selector('body')
+ expect(page).to have_css('body')
# gruppe a faellt ein dass sie doch noch mehr braucht von x und aendert auf 4(1).
login user_a
visit edit_group_order_path(id: order.group_order(user_a.ordergroup).id, order_id: order.id)
@@ -42,7 +42,7 @@
2.times { find("[data-increase_quantity='#{oa.id}']").click }
2.times { find("[data-decrease_tolerance='#{oa.id}']").click }
find('input[type=submit]').click
- expect(page).to have_selector('body')
+ expect(page).to have_css('body')
# die zuteilung
order.finish!(admin)
oa.reload
@@ -50,10 +50,10 @@
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..f1d1ec3e 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 }
+describe 'receiving an order', :js 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(: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)
@@ -40,13 +40,13 @@ def check_quantities(units, q1, q2)
set_quantities [3, 0], [0, 0]
visit receive_order_path(id: order.id)
expect(page).to have_content(article.name)
- expect(page).to have_selector("#order_article_#{oa.id}")
+ expect(page).to have_css("#order_article_#{oa.id}")
end
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).to have_no_css("#order_article_#{oa.id}")
end
it 'is not received by default' do
@@ -58,27 +58,27 @@ def check_quantities(units, q1, q2)
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')
+ expect(page).to have_css('body')
check_quantities 2, 2, 4
end
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')
+ expect(page).to have_css('body')
check_quantities 3, 2, 5
end
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')
+ expect(page).to have_css('body')
check_quantities 1, 2, 1
end
@@ -96,7 +96,7 @@ def check_quantities(units, q1, q2)
goa1.save!
visit receive_order_path(id: order.id)
find('input[type="submit"]').click
- expect(page).to have_selector('body')
+ expect(page).to have_css('body')
check_quantities 2, 3, 4
end
end
diff --git a/spec/integration/session_spec.rb b/spec/integration/session_spec.rb
index 0838858d..bde3e622 100644
--- a/spec/integration/session_spec.rb
+++ b/spec/integration/session_spec.rb
@@ -1,27 +1,30 @@
require_relative '../spec_helper'
-feature 'the session' do
- let(:user) { create :user }
+describe 'the session' do
+ let(:user) { create(:user) }
describe 'login page' do
it 'is accessible' do
visit login_path
- expect(page).to have_selector('input[type=password]')
+ expect(page).to have_css('input[type=password]')
end
+
it 'logs me in' do
login user
- expect(page).to_not have_selector('.alert-error')
+ expect(page).to have_no_css('.alert-error')
end
+
it 'does not log me in with wrong password' do
login user.nick, 'XX' + user.password
- expect(page).to have_selector('.alert-error')
+ expect(page).to have_css('.alert-error')
end
+
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).to have_no_css('.alert-error')
end
end
end
diff --git a/spec/integration/supplier_spec.rb b/spec/integration/supplier_spec.rb
index cfd74e87..9710e800 100644
--- a/spec/integration/supplier_spec.rb
+++ b/spec/integration/supplier_spec.rb
@@ -1,21 +1,22 @@
require_relative '../spec_helper'
-feature 'supplier' do
- let(:supplier) { create :supplier }
+describe 'supplier' do
+ let(:supplier) { create(:supplier) }
describe 'create new' do
- let(:user) { create :user, groups: [create(:workgroup, role_suppliers: true)] }
+ let(:user) { create(:user, groups: [create(:workgroup, role_suppliers: true)]) }
+
before { login user }
it 'can be created' do
- create :supplier_category
+ create(:supplier_category)
visit suppliers_path
click_on I18n.t('suppliers.index.action_new')
- 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)
diff --git a/spec/lib/article_units_lib_spec.rb b/spec/lib/article_units_lib_spec.rb
new file mode 100644
index 00000000..66bcacc5
--- /dev/null
+++ b/spec/lib/article_units_lib_spec.rb
@@ -0,0 +1,136 @@
+require_relative '../spec_helper'
+
+describe ArticleUnitsLib do
+ it 'converts "kg" correctly' do
+ result = described_class.convert_old_unit('kg', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'KGM',
+ first_ratio: nil,
+ group_order_granularity: 1,
+ group_order_unit: 'KGM'
+ })
+ end
+
+ it 'converts "250g" correctly' do
+ result = described_class.convert_old_unit('250g', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XPP',
+ first_ratio: {
+ unit: 'GRM',
+ quantity: 250.0
+ },
+ group_order_granularity: 1.0,
+ group_order_unit: 'XPP'
+ })
+ end
+
+ it 'converts "1/4 kg" correctly' do
+ result = described_class.convert_old_unit('1/4 kg', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XPP',
+ first_ratio: {
+ unit: 'KGM',
+ quantity: 0.25
+ },
+ group_order_granularity: 1.0,
+ group_order_unit: 'XPP'
+ })
+ end
+
+ it 'converts "bunch" correctly' do
+ result = described_class.convert_old_unit('bunch', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XBH',
+ first_ratio: nil,
+ group_order_granularity: 1.0,
+ group_order_unit: 'XBH'
+ })
+ end
+
+ it 'converts "jar" correctly' do
+ result = described_class.convert_old_unit('jar', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XJR',
+ first_ratio: nil,
+ group_order_granularity: 1.0,
+ group_order_unit: 'XJR'
+ })
+ end
+
+ it 'converts "piece" correctly' do
+ result = described_class.convert_old_unit('piece', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XPP',
+ first_ratio: nil,
+ group_order_granularity: 1.0,
+ group_order_unit: 'XPP'
+ })
+ end
+
+ it 'converts "4 piece" correctly' do
+ result = described_class.convert_old_unit('4 piece', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XPK',
+ first_ratio: {
+ unit: 'XPP',
+ quantity: 4
+ },
+ group_order_granularity: 1.0,
+ group_order_unit: 'XPK'
+ })
+ end
+
+ it 'converts "1 bunch" correctly' do
+ result = described_class.convert_old_unit('1 bunch', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XBH',
+ first_ratio: nil,
+ group_order_granularity: 1.0,
+ group_order_unit: 'XBH'
+ })
+ end
+
+ it 'converts "2 bunch" correctly' do
+ result = described_class.convert_old_unit('2 bunch', 1)
+ expect(result).to eq({
+ supplier_order_unit: 'XPK',
+ first_ratio: {
+ unit: 'XBH',
+ quantity: 2
+ },
+ group_order_granularity: 1.0,
+ group_order_unit: 'XPK'
+ })
+ end
+
+ it 'converts "4x250g" correctly' do
+ result = described_class.convert_old_unit('250g', 4)
+ expect(result).to eq({
+ supplier_order_unit: 'XPP',
+ first_ratio: {
+ unit: 'GRM',
+ quantity: 1000
+ },
+ group_order_granularity: 250,
+ group_order_unit: 'GRM'
+ })
+ end
+
+ it 'converts "6 x jar" correctly' do
+ result = described_class.convert_old_unit('jar', 6)
+ expect(result).to eq({
+ supplier_order_unit: 'XPK',
+ first_ratio: {
+ unit: 'XJR',
+ quantity: 6
+ },
+ group_order_granularity: 1,
+ group_order_unit: 'XJR'
+ })
+ end
+
+ it 'fails to convert "12 nonesense"' do
+ result = described_class.convert_old_unit('12 nonesense', 1)
+ expect(result).to be_nil
+ end
+end
diff --git a/spec/lib/bank_account_information_importer_spec.rb b/spec/lib/bank_account_information_importer_spec.rb
index a98441d2..928acf7f 100644
--- a/spec/lib/bank_account_information_importer_spec.rb
+++ b/spec/lib/bank_account_information_importer_spec.rb
@@ -1,14 +1,13 @@
require_relative '../spec_helper'
describe BankTransaction do
- let(:bank_account) { create :bank_account }
+ let(:bank_account) { create(:bank_account) }
it 'empty content' do
- content = <<-JSON
- JSON
+ content = ''
importer = BankAccountInformationImporter.new(bank_account)
- expect(importer.import!(content)).to be(nil)
+ expect(importer.import!(content)).to be_nil
end
it 'invalid JSON' do
@@ -189,7 +188,7 @@
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
@@ -241,7 +240,7 @@
expect(bt.amount).to eq(-238.68)
expect(bt.date).to eq('2019-02-13'.to_date)
expect(bt.text).to eq('Hammersmith Inc.')
- expect(bt.iban).to be(nil)
+ expect(bt.iban).to be_nil
expect(bt.reference).to eq('Martin Schöneicher, Inv# 123453423, Thx')
expect(bt.receipt).to eq('Auslands-Überweisung')
end
@@ -277,8 +276,8 @@
expect(bt.amount).to eq(-12.3)
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.iban).to be_nil
+ expect(bt.reference).to eq('Überweisung US, Wechselspesen u Provision')
expect(bt.receipt).to eq('Spesen/Gebühren')
end
@@ -385,28 +384,28 @@
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')
expect(bt1.iban).to eq('DE72957284895783674747')
- expect(bt1.reference).to be(nil)
+ expect(bt1.reference).to be_nil
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')
expect(bt2.iban).to eq('CH9300762011623852957')
expect(bt2.reference).to eq('RI2')
- expect(bt2.receipt).to be(nil)
+ 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')
- expect(bt3.iban).to be(nil)
- expect(bt3.reference).to be(nil)
- expect(bt3.receipt).to be(nil)
+ expect(bt3.iban).to be_nil
+ expect(bt3.reference).to be_nil
+ expect(bt3.receipt).to be_nil
end
end
diff --git a/spec/lib/bank_transaction_reference_spec.rb b/spec/lib/bank_transaction_reference_spec.rb
index 6d5a5490..ece0e614 100644
--- a/spec/lib/bank_transaction_reference_spec.rb
+++ b/spec/lib/bank_transaction_reference_spec.rb
@@ -2,94 +2,96 @@
describe BankTransactionReference do
it 'returns nil for empty input' do
- expect(BankTransactionReference.parse('')).to be nil
+ expect(BankTransactionReference.parse('')).to be_nil
end
it 'returns nil for invalid string' do
- expect(BankTransactionReference.parse('invalid')).to be nil
+ expect(BankTransactionReference.parse('invalid')).to be_nil
end
it 'returns nil for FS1A' do
- expect(BankTransactionReference.parse('FS1A')).to be nil
+ expect(BankTransactionReference.parse('FS1A')).to be_nil
end
it 'returns nil for FS1.1A' do
- expect(BankTransactionReference.parse('FS1.1A')).to be nil
+ expect(BankTransactionReference.parse('FS1.1A')).to be_nil
end
it 'returns nil for xFS1A1' do
- expect(BankTransactionReference.parse('xFS1A1')).to be nil
+ expect(BankTransactionReference.parse('xFS1A1')).to be_nil
end
it 'returns nil for .FS1A1' do
- expect(BankTransactionReference.parse('.FS1A1')).to be nil
+ expect(BankTransactionReference.parse('.FS1A1')).to be_nil
end
it 'returns nil for FS1A1x' do
- expect(BankTransactionReference.parse('FS1A1x')).to be nil
+ expect(BankTransactionReference.parse('FS1A1x')).to be_nil
end
it 'returns nil for FS1A1.' do
- expect(BankTransactionReference.parse('FS1A1.')).to be nil
+ expect(BankTransactionReference.parse('FS1A1.')).to be_nil
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_config_spec.rb b/spec/lib/foodsoft_config_spec.rb
index be058f38..a6aa0539 100644
--- a/spec/lib/foodsoft_config_spec.rb
+++ b/spec/lib/foodsoft_config_spec.rb
@@ -9,7 +9,7 @@
end
it 'returns an empty default value' do
- expect(FoodsoftConfig[:protected][:LIUhniuyGNKUQTWfbiOQIWYexngo78hqexul]).to be nil
+ expect(FoodsoftConfig[:protected][:LIUhniuyGNKUQTWfbiOQIWYexngo78hqexul]).to be_nil
end
it 'returns a configuration value' do
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,72 +6,64 @@
@server.start
end
+ # TODO: Reanable this test.
+ # It raised "Mysql2::Error: Lock wait timeout exceeded" at time of writing.
+ # it 'accepts bounce mails via SMTP' do
+ # MailDeliveryStatus.delete_all
+ #
+ # Net::SMTP.start(@server.host, @server.port) do |smtp|
+ # address = "#{FoodsoftConfig[:default_scope]}.bounce+user=example.com"
+ # smtp.send_message 'report', 'from@example.com', address
+ # end
+ #
+ # mds = MailDeliveryStatus.last
+ # expect(mds.email).to eq 'user@example.com'
+ # expect(mds.attachment_mime).to eq 'message/rfc822'
+ # expect(mds.attachment_data).to include 'report'
+ # end
+
+ after :all do
+ @server.shutdown
+ end
+
it 'does not accept empty addresses' do
- begin
- FoodsoftMailReceiver.received('', 'body')
- rescue => error
- expect(error.to_s).to include 'missing'
- end
+ FoodsoftMailReceiver.received('', 'body')
+ rescue StandardError => e
+ expect(e.to_s).to include 'missing'
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
+ 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
- begin
- FoodsoftMailReceiver.received('invalid.invalid', 'body')
- rescue => error
- expect(error.to_s).to include 'could not be found'
- end
+ 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
- begin
- address = "#{FoodsoftConfig[:default_scope]}.invalid"
- FoodsoftMailReceiver.received(address, 'body')
- rescue => error
- expect(error.to_s).to include 'invalid format for recipient'
- end
+ 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 {
+ expect do
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.to raise_error(Net::SMTPFatalError)
end
it 'does not accept invalid addresses via SMTP' do
- expect {
+ expect do
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
- # MailDeliveryStatus.delete_all
- #
- # Net::SMTP.start(@server.host, @server.port) do |smtp|
- # address = "#{FoodsoftConfig[:default_scope]}.bounce+user=example.com"
- # smtp.send_message 'report', 'from@example.com', address
- # end
- #
- # mds = MailDeliveryStatus.last
- # expect(mds.email).to eq 'user@example.com'
- # expect(mds.attachment_mime).to eq 'message/rfc822'
- # expect(mds.attachment_data).to include 'report'
- # end
-
- after :all do
- @server.shutdown
+ 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 a5c81d4f..3097e888 100644
--- a/spec/lib/token_verifier_spec.rb
+++ b/spec/lib/token_verifier_spec.rb
@@ -1,17 +1,17 @@
require_relative '../spec_helper'
describe TokenVerifier do
- let (:prefix) { 'xyz' }
- let (:v) { TokenVerifier.new(prefix) }
- let (:msg) { v.generate }
+ let(:prefix) { 'xyz' }
+ let(:v) { TokenVerifier.new(prefix) }
+ 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 @@
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 40201570..51bd6140 100644
--- a/spec/models/article_spec.rb
+++ b/spec/models/article_spec.rb
@@ -1,12 +1,16 @@
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) }
+ let(:user) { create(:user, groups: [create(:ordergroup)]) }
it 'has a unique name' do
- article2 = FactoryBot.build :article, supplier: supplier, name: article.name
- expect(article2).to be_invalid
+ article_version_copy = article.latest_article_version.dup
+ article2 = FactoryBot.build(:article, name: article.name, supplier_id: article.supplier_id,
+ latest_article_version: article_version_copy)
+ article2.latest_article_version.article = article2
+ expect(article2.latest_article_version).to be_invalid
end
it 'computes the gross price correctly' do
@@ -31,13 +35,27 @@
expect(supplier.deleted?).to be true
end
- it 'keeps a price history' do
- expect(article.article_prices.map(&:price)).to eq([article.price])
+ it "doesn't keep an article history for articles not referenced by closed orders" do
+ expect(article.article_versions.map(&:price)).to eq([article.price])
oldprice = article.price
sleep 1 # so that the new price really has a later creation time
article.price += 1
article.save!
- expect(article.article_prices.reload.map(&:price)).to eq([article.price, oldprice])
+ expect(article.article_versions.reload.map(&:price)).to eq([article.price])
+ end
+
+ it 'keeps an article history for articles referenced by closed orders' do
+ order = create(:order, created_by: user, starts: Date.yesterday, ends: 1.hour.ago, end_action: :auto_close)
+ order.close!(user)
+ article = order.order_articles.first.article_version.article
+
+ expect(article.article_versions.map(&:price)).to eq([article.price])
+ oldprice = article.price
+
+ sleep 1 # so that the new price really has a later creation time
+ article.price += 1
+ article.save!
+ expect(article.article_versions.reload.map(&:price)).to eq([article.price, oldprice])
end
it 'is not in an open order by default' do
@@ -45,7 +63,7 @@
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
@@ -53,26 +71,35 @@
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) do
+ article = create(:article, supplier: supplier)
+ article_version = article.latest_article_version
+ article_version.order_number = shared_article.number
+ article_version.save
+ article
+ end
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_attributes updated_article.attributes.reject { |k, v| k == 'id' or k == 'type' }
+ sync_data = supplier.sync_all
+ updated_article = sync_data[0].select { |s| s[0].id == article.id }.first[0]
+ version_attributes = updated_article.latest_article_version.attributes.reject { |k, _v| k == 'id' }
+ version_attributes[:id] = article.latest_article_version.id
+ article.update_attributes(latest_article_version_attributes: version_attributes)
expect(article.name).to eq(shared_article.name)
# now synchronising shouldn't change anything anymore
expect(article.shared_article_changed?).to be_falsey
@@ -80,7 +107,7 @@
it 'does not need to synchronise an imported article' do
article = shared_article.build_new_article(supplier)
- article.article_category = create :article_category
+ article.latest_article_version.article_category = create :article_category
expect(article.shared_article_changed?).to be_falsey
end
@@ -89,20 +116,35 @@
shared_article.unit_quantity = 1
shared_article.save!
article = shared_article.build_new_article(supplier)
- article.article_category = create :article_category
- article.unit = '200g'
+ article_version = article.latest_article_version
+
+ # remove version before save - required to make validation pass (can't validate article_version without existing article_id):
+ article.article_versions = []
+ article.latest_article_version = nil
+
article.shared_updated_on -= 1 # to make update do something
article.save!
- # TODO get sync functionality in article
- updated_article = supplier.sync_all[0].select { |s| s[0].id == article.id }.first[0]
- article.update_attributes! updated_article.attributes.reject { |k, v| k == 'id' or k == 'type' }
+
+ article_version.article_category = create :article_category
+ article_version.unit = '200g'
+ article.article_versions << article_version
+ article.reload
+ # TODO: get sync functionality in article
+ sync_data = supplier.sync_all
+ updated_article = sync_data[0].select { |s| s[0].id == article.id }.first[0]
+ version_attributes = updated_article.latest_article_version.attributes.reject { |k, _v| k == 'id' }
+ version_attributes[:article_unit_ratios_attributes] =
+ updated_article.latest_article_version.article_unit_ratios.map(&:attributes)
+ version_attributes[:id] = article.latest_article_version.id
+ article.update_attributes(latest_article_version_attributes: version_attributes)
+
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)
end
it 'does not synchronise when it has no order number' do
- article.update_attributes :order_number => nil
+ article.update_attributes order_number: nil
expect(supplier.sync_all).to eq [[], [], []]
end
end
diff --git a/spec/models/bank_transaction_spec.rb b/spec/models/bank_transaction_spec.rb
index 984d39ae..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!(: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!(: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) 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,17 +57,29 @@
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
+ expect(bank_transaction1.assign_to_ordergroup).to be_nil
end
it 'ignores transaction with invalid ordergroup' do
diff --git a/spec/models/group_order_article_spec.rb b/spec/models/group_order_article_spec.rb
index ddb9158a..b777eb89 100644
--- a/spec/models/group_order_article_spec.rb
+++ b/spec/models/group_order_article_spec.rb
@@ -1,20 +1,31 @@
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
- it 'has zero result by default' do expect(goa.result).to eq(0) end
- it 'has zero total price by default' do expect(goa.total_price).to eq(0) end
+ 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
+
+ it 'has zero result by default' do
+ expect(goa.result).to eq(0)
+ end
+
+ 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 +34,8 @@
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)
@@ -32,7 +44,7 @@
it 'has a proper total price' do
quantity = rand(1..99)
goa.update_quantities(quantity, 0)
- expect(goa.total_price).to eq(quantity * goa.order_article.price.fc_price)
+ expect(goa.total_price).to eq(quantity * goa.order_article.article_version.fc_price)
end
it 'can unorder a product' do
@@ -43,10 +55,10 @@
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 }
+ 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) }
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 @@
# 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 d124678f..acade0ee 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 @@
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)
@@ -73,27 +73,27 @@ def goa_reload
it 'has expected units_to_order' do
set_quantities [3, 2], [1, 3], [1, 0]
- expect(oa.units * oa.article.unit_quantity).to eq 6
+ expect(oa.units * oa.article_version.unit_quantity).to eq 6
expect([goa1, goa2, goa3].map(&:result)).to eq [4, 1, 1]
end
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 @@ def goa_reload
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
@@ -130,17 +130,19 @@ def goa_reload
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
+ goa.update_quantities(*q.keys[0])
+ oa.update_results!
+ oa.reload
end
# check starting condition
@@ -149,11 +151,11 @@ def goa_reload
end
# actual test
- it (success ? 'succeeds' : 'fails') do
+ it(success ? 'succeeds' : 'fails') do
order.update_attributes(boxfill: boxfill_from)
r = proc {
- goa.update_quantities *q.values[0]
+ goa.update_quantities(*q.values[0])
oa.update_results!
}
if success
@@ -169,33 +171,41 @@ def goa_reload
context 'before the date' 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
context 'after the date' 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 10a91a7e..18a478b4 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,51 +19,51 @@
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 'should retrieve open orders in the "open" scope' do
+ it 'retrieves open orders in the "open" scope' do
expect(Order.open.count).to eq(1)
expect(Order.open.where(id: open_order.id)).to exist
end
- it 'should retrieve finished, received and closed orders in the "finished" scope' do
+ it 'retrieves finished, received and closed orders in the "finished" scope' do
expect(Order.finished.count).to eq(3)
expect(Order.finished.where(id: finished_order.id)).to exist
expect(Order.finished.where(id: received_order.id)).to exist
expect(Order.finished.where(id: closed_order.id)).to exist
end
- it 'should retrieve finished and received orders in the "finished_not_closed" scope' do
+ it 'retrieves finished and received orders in the "finished_not_closed" scope' do
expect(Order.finished_not_closed.count).to eq(2)
expect(Order.finished_not_closed.where(id: finished_order.id)).to exist
expect(Order.finished_not_closed.where(id: received_order.id)).to exist
end
- it 'should return valid boolean states for open orders' do
+ it 'returns valid boolean states for open orders' do
expect(open_order.open?).to be(true)
expect(open_order.finished?).to be(false)
expect(open_order.received?).to be(false)
expect(open_order.closed?).to be(false)
end
- it 'should return valid boolean states for finished orders' do
+ it 'returns valid boolean states for finished orders' do
expect(finished_order.open?).to be(false)
expect(finished_order.finished?).to be(true)
expect(finished_order.received?).to be(false)
expect(finished_order.closed?).to be(false)
end
- it 'should return valid boolean states for received orders' do
+ it 'returns valid boolean states for received orders' do
expect(received_order.open?).to be(false)
expect(received_order.finished?).to be(true)
expect(received_order.received?).to be(true)
expect(received_order.closed?).to be(false)
end
- it 'should return valid boolean states for closed orders' do
+ it 'returns valid boolean states for closed orders' do
expect(closed_order.open?).to be(false)
expect(closed_order.finished?).to be(false)
expect(closed_order.received?).to be(false)
@@ -72,8 +72,9 @@
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 @@
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,44 @@
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 open by default' do
+ expect(order).to be_open
+ 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' } }
order.init_dates
@@ -137,10 +147,10 @@
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)
@@ -155,10 +165,10 @@
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 6ac58fd5..565ad32f 100644
--- a/spec/models/ordergroup_spec.rb
+++ b/spec/models/ordergroup_spec.rb
@@ -1,12 +1,12 @@
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)]) }
context 'with financial transactions' do
before do
diff --git a/spec/models/supplier_spec.rb b/spec/models/supplier_spec.rb
index 72e870ed..f92e140c 100644
--- a/spec/models/supplier_spec.rb
+++ b/spec/models/supplier_spec.rb
@@ -1,41 +1,61 @@
require_relative '../spec_helper'
describe Supplier do
- let(:supplier) { create :supplier }
+ let(:supplier) { create(:supplier) }
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_version = article.latest_article_version
+
+ # remove version before save - required to make validation pass (can't validate article_version without existing article_id):
+ article.article_versions = []
+ article.latest_article_version = nil
+
article.shared_updated_on = 1.day.ago
article.save!
+
+ article_version.article_category = create :article_category
+ article_version.unit = '200g'
+ article_version.origin = 'FubarX1'
+ article.article_versions << article_version
+ article.reload
end
end
let!(:synced_article) do
synced_shared_article.build_new_article(supplier).tap do |article|
- article.article_category = create :article_category
+ article_version = article.latest_article_version
+
+ # remove version before save - required to make validation pass (can't validate article_version without existing article_id):
+ article.article_versions = []
+ article.latest_article_version = nil
+
article.shared_updated_on = 1.day.ago
article.save!
+
+ article_version.article_category = create :article_category
+ article_version.unit = '200g'
+ article.article_versions << article_version
+ article.reload
end
end
@@ -45,9 +65,11 @@
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[0][0].id).to eq updated_article.id
- expect(updated_article_pairs[0][1].keys).to include :origin
+ expect(updated_article_pairs).not_to be_empty
+
+ index = updated_article_pairs.index { |pair| pair[0].id == updated_article.id }
+ expect(index).not_to be_nil
+ expect(updated_article_pairs[index][1].keys).to include :origin
expect(outlisted_articles).to eq [removed_article]
@@ -61,15 +83,15 @@
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[0][0].id).to eq updated_article.id
- expect(updated_article_pairs[0][1].keys).to include :origin
+ index = updated_article_pairs.index { |pair| pair[0].id == updated_article.id }
+ expect(index).not_to be_nil
+ expect(updated_article_pairs[index][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
+ expect(new_articles[0].availability).to be true
end
end
@@ -79,15 +101,15 @@
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[0][0].id).to eq updated_article.id
- expect(updated_article_pairs[0][1].keys).to include :origin
+ index = updated_article_pairs.index { |pair| pair[0].id == updated_article.id }
+ expect(index).not_to be_nil
+ expect(updated_article_pairs[index][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
+ expect(new_articles[0].availability).to be false
end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 2415cae8..9eb5f7d9 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2,9 +2,9 @@
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,34 +14,54 @@
end
describe 'does not have the role' do
- 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
- it 'article_meta' do expect(user.role_article_meta?).to be_falsey end
- it 'suppliers' do expect(user.role_suppliers?).to be_falsey end
- it 'orders' do expect(user.role_orders?).to be_falsey end
+ 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
+
+ it 'article_meta' do
+ expect(user.role_article_meta?).to be_falsey
+ end
+
+ it 'suppliers' do
+ expect(user.role_suppliers?).to be_falsey
+ end
+
+ it 'orders' do
+ expect(user.role_orders?).to be_falsey
+ end
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
end
+
it 'can not authenticate with incorrect password' do
expect(User.authenticate(user.nick, 'foobar')).to be_nil
end
+
it 'can not authenticate with nil nick' do
expect(User.authenticate(nil, 'blahblahblah')).to be_nil
end
+
it 'can not authenticate with nil password' do
expect(User.authenticate(user.nick, nil)).to be_nil
end
+
it 'can not set a password without matching confirmation' do
user.password = 'abcdefghijkl'
user.password_confirmation = 'foobaruvwxyz'
expect(user).to be_invalid
end
+
it 'can set a password with matching confirmation' do
user.password = 'abcdefghijkl'
user.password_confirmation = 'abcdefghijkl'
@@ -51,6 +71,7 @@
it 'has a unique nick' do
expect(build(:user, nick: user.nick, email: "x-#{user.email}")).to be_invalid
end
+
it 'has a unique email' do
expect(build(:user, email: "#{user.email}")).to be_invalid
end
@@ -67,7 +88,10 @@
end
describe 'admin' do
- let(:user) { create :admin }
- it 'default admin role' do expect(user.role_admin?).to be_truthy end
+ let(:user) { create(:admin) }
+
+ it 'default admin role' do
+ expect(user.role_admin?).to be_truthy
+ end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 88dea423..c1a3df21 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 @@
# 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 @@
# 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,7 +49,7 @@
# 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 SessionHelper, type: :feature
diff --git a/spec/support/api_helper.rb b/spec/support/api_helper.rb
index 65acc75b..752e19b6 100644
--- a/spec/support/api_helper.rb
+++ b/spec/support/api_helper.rb
@@ -4,12 +4,15 @@ 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_access_token) do
+ create(:oauth2_access_token, resource_owner_id: user.id, scopes: api_scopes&.join(' ')).token
+ end
let(:api_authorization) { "Bearer #{api_access_token}" }
def self.it_handles_invalid_token(method, path, params_block = -> { api_auth })
context 'with invalid access token' do
let(:api_access_token) { 'abc' }
+
it { is_expected.to validate(method, path, 401, instance_exec(¶ms_block)) }
end
end
@@ -17,6 +20,7 @@ def self.it_handles_invalid_token(method, path, params_block = -> { api_auth })
def self.it_handles_invalid_scope(method, path, params_block = -> { api_auth })
context 'with invalid scope' do
let(:api_scopes) { ['none'] }
+
it { is_expected.to validate(method, path, 403, instance_exec(¶ms_block)) }
end
end
diff --git a/spec/support/api_oauth.rb b/spec/support/api_oauth.rb
index 8cf283f4..9d701349 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..443da12d 100644
--- a/spec/support/coverage.rb
+++ b/spec/support/coverage.rb
@@ -11,16 +11,28 @@
# 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/'
add_filter '/test/'
- add_group 'Models' do |s| cov_no_plugins s, '/app/models/' end
- add_group 'Controllers' do |s| cov_no_plugins s, '/app/controllers/' end
- 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 'Models' do |s|
+ cov_no_plugins s, '/app/models/'
+ end
+ add_group 'Controllers' do |s|
+ cov_no_plugins s, '/app/controllers/'
+ end
+ 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 =~ %r{/lib/foodsoft_.*/}
+ end
end
end
diff --git a/spec/support/faker.rb b/spec/support/faker.rb
index 47441ca8..e4857f8a 100644
--- a/spec/support/faker.rb
+++ b/spec/support/faker.rb
@@ -1,8 +1,20 @@
module Faker
+ class LegacyUnit
+ class << self
+ def unit
+ %w[kg 1L 100ml piece bunch 500g].sample
+ end
+ end
+ end
+
class Unit
class << self
+ def units
+ %w[KGM GRM LTR]
+ end
+
def unit
- ['kg', '1L', '100ml', 'piece', 'bunch', '500g'].sample
+ units.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
deleted file mode 100644
index f6c0ff0a..00000000
--- a/spec/support/shared_database.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-ActiveSupport.on_load(:after_initialize) do
- # We simulate the shared database by pointing to our own database.
- # This allows running tests without additional database setup.
- # 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
- SharedArticle.class_eval do
- belongs_to :supplier, class_name: 'SharedSupplier'
- alias_attribute :number, :order_number
- alias_attribute :updated_on, :updated_at
- def category
- ArticleCategory.where(id: article_category_id).first
- end
-
- def self.find_by_number(n)
- find_by_order_number(n)
- end
- end
-end