diff --git a/app/models/group_order_article.rb b/app/models/group_order_article.rb index af31fef0..0b30af48 100644 --- a/app/models/group_order_article.rb +++ b/app/models/group_order_article.rb @@ -128,15 +128,18 @@ class GroupOrderArticle < ApplicationRecord order_quantities = GroupOrderArticleQuantity.where(group_order_article_id: order_article.group_order_article_ids).order('created_on') logger.debug "GroupOrderArticleQuantity records found: #{order_quantities.size}" + first_order_first_serve = (FoodsoftConfig[:distribution_strategy] == FoodsoftConfig::DistributionStrategy::FIRST_ORDER_FIRST_SERVE) + # Determine quantities to be ordered... order_quantities.each do |goaq| - q = [goaq.quantity, total - total_quantity].min + 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 logger.debug "increasing quantity by #{q}" quantity += q end - break if total_quantity >= total + break if total_quantity >= total && first_order_first_serve end # Determine tolerance to be ordered... diff --git a/app/serializers/config_serializer.rb b/app/serializers/config_serializer.rb index a196a372..d5b6c138 100644 --- a/app/serializers/config_serializer.rb +++ b/app/serializers/config_serializer.rb @@ -1,11 +1,10 @@ class ConfigSerializer < ActiveModel::Serializer - # details attributes :name, :homepage, :contact # settings attributes :currency_unit, :currency_space, :default_locale, :price_markup, - :tolerance_is_costly, :use_apple_points, :use_tolerance + :tolerance_is_costly, :distribution_strategy, :use_apple_points, :use_tolerance # layout attributes :page_footer_html, :webstats_tracking_code_html diff --git a/app/views/admin/configs/_tab_others.html.haml b/app/views/admin/configs/_tab_others.html.haml index 96747794..907cf840 100644 --- a/app/views/admin/configs/_tab_others.html.haml +++ b/app/views/admin/configs/_tab_others.html.haml @@ -1,5 +1,8 @@ = config_input form, :use_nick, as: :boolean = config_input form, :tolerance_is_costly, as: :boolean +- distribution_strategy_options = FoodsoftConfig::DistributionStrategy.constants.map { |c| FoodsoftConfig::DistributionStrategy.const_get(c) } += config_input form, :distribution_strategy, as: :select, collection: distribution_strategy_options, + include_blank: false, input_html: {class: 'input-xxlarge'}, label_method: ->(s){ t("config.keys.distribution_strategy_options.#{s}") } = config_input form, :disable_invite, as: :boolean = config_input form, :help_url, as: :url, input_html: {class: 'input-xlarge'} = config_input form, :webstats_tracking_code, as: :text, input_html: {class: 'input-xxlarge', rows: 3} diff --git a/config/locales/de.yml b/config/locales/de.yml index 87f667ed..69f31774 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -597,6 +597,7 @@ de: tasks_upfront_days: Anzahl der Tage, für welche du im Voraus wiederkehrende Aufgaben definieren möchtest tax_default: Standard Mehrwertsteuersatz für neue Artikel tolerance_is_costly: Eine möglichst große Menge im Rahmen der Tolerenz bestellen. Wenn dies nicht aktiviert ist, wird im Rahmen der Toleranz nur so viel bestellt, dass damit komplette Einheiten (Boxen) bestellt werden können. Die Option wirkt sich auch auf die Toleranz des Gesamtpreises einer offenen Mitgliederbestellung aus. + distribution_strategy: Wie bei der Verteilung von Artikeln nach dem Empfangen einer Bestellung vorgegangen werden soll. use_apple_points: Wenn das Apfel Punktesystem aktiviert ist, ist es erforderlich, dass Mitglieder Aufgaben erledigen um bestellen zu können. use_boxfill: Wenn aktiviert, können Benutzer nahe am Ende der Bestellung diese nur mehr so verändern, dass sich die Gesamtsumme erhöht. Dies hilft beim auffüllen der verbleibenden Kisten. Es muss trotzdem noch das Kistenauffülldatum bei der Bestellung gesetzt werden. use_iban: Zusätzlich Feld für die internationale Kontonummer bei Benutzern und Lieferanten anzeigen @@ -650,6 +651,10 @@ de: tax_default: Mehrwertsteuer time_zone: Zeitzone tolerance_is_costly: Bestelltoleranz maximal ausnutzen, um möglichst große Mengen zu bestellen + distribution_strategy: Verteilungs-Strategie + distribution_strategy_options: + first_order_first_serve: Zuerst an die verteilen, die zuerst bestellt haben + no_automatic_distribution: Keine automatische Verteilung use_apple_points: Apfelpunkte verwenden use_boxfill: Kistenauffüllphase use_iban: IBAN verwenden diff --git a/config/locales/en.yml b/config/locales/en.yml index 2f63d54f..c40843e9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -597,6 +597,7 @@ en: tasks_upfront_days: For how many days in advance you would like to schedule periodic tasks. tax_default: Default VAT percentage for new articles. tolerance_is_costly: Order as much of the member tolerance as possible (compared to only as much needed to fill the last box). Enabling this also includes the tolerance in the total price of the open member order. + distribution_strategy: How articles should be distributed after an order has been received. use_apple_points: When the apple point system is enabled, members are required to do some tasks to be able to keep ordering. use_boxfill: When enabled, near end of an order, members are only able to change their order when increases the total amount ordered. This helps to fill any remaining boxes. You still need to set a box-fill date for the orders. use_iban: When enabled, supplier and user provide an additonal field for storing the international bank account number. @@ -650,6 +651,10 @@ en: tax_default: Default VAT time_zone: Time zone tolerance_is_costly: Tolerance is costly + distribution_strategy: Distribution strategy + distribution_strategy_options: + first_order_first_serve: First distribute to those who ordered first + no_automatic_distribution: No automatic distribution use_apple_points: Apple points use_boxfill: Box-fill phase use_iban: Use IBAN diff --git a/config/locales/es.yml b/config/locales/es.yml index 18eb058c..464e5f6f 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -561,6 +561,7 @@ es: tasks_upfront_days: Con cuántos días de antelación te gustaría programar las tareas periódicas. tax_default: Porcentaje por defecto del IVA para artículos nuevos. tolerance_is_costly: Pide lo que más permita la tolerancia de los miembros (en lugar de sólo lo necesario para llenar la última caja). Permitir esto también incluye la tolerancia en el precio final del pedido abierto de cada miembro. + distribution_strategy: Cómo se deben distribuir los artículos después de recibir un pedido. use_apple_points: Cuando el sistema de puntos-manzana está habilitado los miembros deberán realizar algunas tareas para poder hacer pedidos. use_boxfill: Cuando está activado, cerca del cierre de un pedido los miembros no podrán cambiar su pedido a menos que se incremente el valor pedido total. Esto ayudará a llenar las cajas que faltan. Igualmente deberás decidir una fecha de llenado de cajas para los pedidos. use_iban: Cuando esta opción está habilitada, el proveedor y el usuario pueden guardan también su número de cuenta bancaria internacional (IBAN). @@ -605,6 +606,10 @@ es: tax_default: IVA por defecto time_zone: Zona horaria tolerance_is_costly: La tolerancia es prioritaria + distribution_strategy: Estrategia de distribución + distribution_strategy_options: + first_order_first_serve: Primero distribuya a quienes ordenaron primero + no_automatic_distribution: Sin distribución automática use_apple_points: Puntos-manzana use_boxfill: Fase de llenar las cajas use_iban: Usar IBAN diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 0e2c3b84..a3070ac4 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -403,6 +403,7 @@ fr: hints: homepage: Site de votre coop. name: Le nom de votre coop. + distribution_strategy: Comment procéder à la distribution des articles après réception d'une commande. keys: contact: city: Ville @@ -413,6 +414,10 @@ fr: zip_code: Code postal currency_unit: Monnaie name: Nom + distribution_strategy: Stratégie de distribution + distribution_strategy_options: + first_order_first_serve: Distribuez d'abord à ceux qui ont commandé en premier + no_automatic_distribution: Pas de distribution automatique tabs: language: Langue list: Liste diff --git a/config/locales/nl.yml b/config/locales/nl.yml index a984db6c..b2b7cfbf 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -597,6 +597,7 @@ nl: tasks_upfront_days: Hoeveel dagen vantevoren je periodieke taken wil aanmaken. tax_default: Standaard BTW percentage voor nieuwe artikelen. tolerance_is_costly: Bestel zoveel artikelen als mogelijk in de tolerantie (in plaats van net genoeg om de laatste doos te vullen). Dit zorgt er ook voor dat de tolerantie in de prijs van open ledenbestellingen wordt meegenomen. + distribution_strategy: Hoe verder te gaan met de distributie van artikelen na ontvangst van een bestelling. use_apple_points: Wanneer het appelpunten systeem is geactiveerd, kunnen leden slechts bestellen wanneer ze meewerken aan taken. use_boxfill: Wanneer dit aan staat, kunnen gebruikers aan het einde van de bestelfase hun bestelling alleen wijzigen als het de totale bestelling vergroot. Dit helpt om dozen te vullen. Bij de bestelling moet nog wel de dozen-vul-datum ingevuld worden. use_iban: Om leverancier en gebruiker een extra veld te geven voor het internationale bankrekeningnummer (IBAN). @@ -650,6 +651,10 @@ nl: tax_default: Standaard BTW time_zone: Tijdzone tolerance_is_costly: Tolerantie is duur + distribution_strategy: Distributie strategie + distribution_strategy_options: + first_order_first_serve: Verdeel eerst onder degenen die het eerst hebben besteld + no_automatic_distribution: Geen automatische distributie use_apple_points: Appelpunten use_boxfill: Dozen vullen fase use_iban: IBAN gebruiken diff --git a/lib/foodsoft_config.rb b/lib/foodsoft_config.rb index 316a9b2b..713a954a 100644 --- a/lib/foodsoft_config.rb +++ b/lib/foodsoft_config.rb @@ -54,6 +54,12 @@ class FoodsoftConfig # Loaded configuration APP_CONFIG = ActiveSupport::HashWithIndifferentAccess.new + # distribution strategy config values enum + module DistributionStrategy + FIRST_ORDER_FIRST_SERVE = 'first_order_first_serve' + NO_AUTOMATIC_DISTRIBUTION = 'no_automatic_distribution' + end + class << self # Load and initialize foodcoop configuration file. @@ -260,6 +266,7 @@ class FoodsoftConfig tasks_period_days: 7, tasks_upfront_days: 49, shared_supplier_article_sync_limit: 200, + distribution_strategy: FoodsoftConfig::DistributionStrategy::FIRST_ORDER_FIRST_SERVE, # The following keys cannot, by default, be set by foodcoops themselves. protected: { multi_coop_install: true, diff --git a/spec/factories/group_order_article_quantity.rb b/spec/factories/group_order_article_quantity.rb new file mode 100644 index 00000000..f3fa5bb6 --- /dev/null +++ b/spec/factories/group_order_article_quantity.rb @@ -0,0 +1,9 @@ +require 'factory_bot' + +FactoryBot.define do + + # requires order_article + factory :group_order_article_quantity do + end + +end diff --git a/spec/models/group_order_article_spec.rb b/spec/models/group_order_article_spec.rb index 9ad21c44..fd47e603 100644 --- a/spec/models/group_order_article_spec.rb +++ b/spec/models/group_order_article_spec.rb @@ -42,4 +42,23 @@ describe GroupOrderArticle do end 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 } + + it 'can calculate the result for the distribution strategy "first order first serve"' do + res = goa.calculate_result(2) + expect(res).to eq(quantity: 2, tolerance: 0, total: 2) + end + + it 'can calculate the result for the distribution strategy "no automatic distribution"' do + FoodsoftConfig[:distribution_strategy] = FoodsoftConfig::DistributionStrategy::NO_AUTOMATIC_DISTRIBUTION + + res = goa.calculate_result(2) + expect(res).to eq(quantity: 4, tolerance: 0, total: 4) + end + end + end