From 876e73a989f9c6be5c621f79ca1371ef60351ea2 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 13 Dec 2018 12:27:08 +1100 Subject: [PATCH 01/16] Rewrite task to generate sample data https://github.com/openfoodfoundation/openfoodnetwork/issues/2072 We need more and better structured sample data for dev testing and to bootstrap staging data. This new task aims to replace openfoodnetwork:dev:sample_data. --- lib/tasks/sample_data.rake | 654 +++++++++++++++++++++++++++++++++++++ 1 file changed, 654 insertions(+) create mode 100644 lib/tasks/sample_data.rake diff --git a/lib/tasks/sample_data.rake b/lib/tasks/sample_data.rake new file mode 100644 index 0000000000..d93300e51f --- /dev/null +++ b/lib/tasks/sample_data.rake @@ -0,0 +1,654 @@ +# The sample data generated by this task is supposed to save some time during +# manual testing. It is not meant to be complete, but we try to improve it +# over time. How much is hardcoded here is a trade off between developer time +# and tester time. We also can't include secrets like payment gateway +# configurations in the code since it's public. We have been discussing this for +# a while: +# +# - https://community.openfoodnetwork.org/t/seed-data-development-provisioning-deployment/910 +# - https://github.com/openfoodfoundation/openfoodnetwork/issues/2072 +# +namespace :openfoodnetwork do + desc 'load sample data for development or staging' + task sample_data: :environment do + raise "Please run `rake db:seed` first." unless seeded? + + users = UserFactory.new.create_samples + + enterprises = EnterpriseFactory.new.create_samples(users) + + PermissionFactory.new.create_samples(enterprises) + + FeeFactory.new.create_samples(enterprises) + + ShippingMethodFactory.new.create_samples(enterprises) + + PaymentMethodFactory.new.create_samples(enterprises) + + TaxonFactory.new.create_samples + + products = ProductFactory.new.create_samples(enterprises) + + InventoryFactory.new.create_samples(products) + + OrderCycleFactory.new.create_samples + + CustomerFactory.new.create_samples(users) + + GroupFactory.new.create_samples + end + + def seeded? + Spree::User.count > 0 && + Spree::Country.count > 0 && + Spree::State.count > 0 + end + + module Logging + private + + def log(message) + puts "[openfoodnetwork:sample_data:load] #{message}" + end + end + + module Addressing + private + + def address(string) + state = country.states.first + parts = string.split(", ") + Spree::Address.new( + address1: parts[0], + city: parts[1], + zipcode: parts[2], + state: state, + country: country + ) + end + + def zone + zone = Spree::Zone.find_or_create_by_name!("Australia") + zone.members.create(zonable: country) + zone + end + + def country + Spree::Country.find_by_iso(ENV.fetch('DEFAULT_COUNTRY_CODE')) + end + end + + class UserFactory + include Logging + + def create_samples + log "Creating users:" + usernames.map { |name| + create_user(name) + }.to_h + end + + private + + def usernames + [ + "Manel Super Admin", + "Penny Profile", + "Fred Farmer", + "Freddy Shop Farmer", + "Fredo Hub Farmer", + "Mary Retailer", + "Maryse Private", + "Jane Customer" + ] + end + + def create_user(name) + email = "#{name.downcase.tr(' ', '.')}@example.org" + password = Spree::User.friendly_token + log "- #{email}" + user = Spree::User.create_with( + password: password, + password_confirmation: password, + confirmation_sent_at: Time.zone.now, + confirmed_at: Time.zone.now + ).find_or_create_by_email!(email) + [name, user] + end + end + + class EnterpriseFactory + include Logging + include Addressing + + def create_samples(users) + log "Creating enterprises:" + enterprise_data(users).map do |data| + name = data[:name] + log "- #{name}" + Enterprise.create_with(data).find_or_create_by_name!(name) + end + end + + private + + # rubocop:disable Metrics/MethodLength + def enterprise_data(users) + [ + { + name: "Penny's Profile", + owner: users["Penny Profile"], + is_primary_producer: false, + sells: "none", + address: address("25 Myrtle Street, Bayswater, 3153") + }, + { + name: "Fred's Farm", + owner: users["Fred Farmer"], + is_primary_producer: true, + sells: "none", + address: address("6 Rollings Road, Upper Ferntree Gully, 3156") + }, + { + name: "Freddy's Farm Shop", + owner: users["Freddy Shop Farmer"], + is_primary_producer: true, + sells: "own", + address: address("72 Lake Road, Blackburn, 3130") + }, + { + name: "Fredo's Farm Hub", + owner: users["Fredo Hub Farmer"], + is_primary_producer: true, + sells: "any", + address: address("7 Verbena Street, Mordialloc, 3195") + }, + { + name: "Mary's Online Shop", + owner: users["Mary Retailer"], + is_primary_producer: false, + sells: "any", + address: address("20 Galvin Street, Altona, 3018") + }, + { + name: "Maryse's Private Shop", + owner: users["Maryse Private"], + is_primary_producer: false, + sells: "any", + address: address("6 Martin Street, Belgrave, 3160"), + require_login: true + } + ] + end + # rubocop:enable Metrics/MethodLength + end + + class PaymentMethodFactory + include Logging + include Addressing + + def create_samples(enterprises) + log "Creating payment methods:" + distributors = enterprises.select(&:is_distributor) + distributors.each do |enterprise| + create_payment_methods(enterprise) + end + end + + private + + def create_payment_methods(enterprise) + return if enterprise.payment_methods.present? + log "- #{enterprise.name}" + create_cash_method(enterprise) + create_card_method(enterprise) + end + + def create_cash_method(enterprise) + create_payment_method( + enterprise, + "Cash on collection", + "Pay on collection!", + Spree::Calculator::FlatRate.new + ) + end + + def create_card_method(enterprise) + create_payment_method( + enterprise, + "Credit card (fake)", + "We charge 1%, but won't ask for you details. ;-)", + Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 1) + ) + end + + def create_payment_method(enterprise, name, description, calculator) + card = enterprise.payment_methods.new( + name: name, + description: description, + distributor_ids: [enterprise.id] + ) + card.calculator = calculator + card.save! + end + end + + class ShippingMethodFactory + include Logging + include Addressing + + def create_samples(enterprises) + log "Creating shipping methods:" + distributors = enterprises.select(&:is_distributor) + distributors.each do |enterprise| + create_shipping_methods(enterprise) + end + end + + private + + def create_shipping_methods(enterprise) + return if enterprise.shipping_methods.present? + log "- #{enterprise.name}" + create_pickup(enterprise) + create_delivery(enterprise) + end + + def create_pickup(enterprise) + create_shipping_method( + enterprise, + name: "Pickup", + description: "pick-up at your awesome hub gathering place", + require_ship_address: false, + calculator_type: "Calculator::Weight" + ) + end + + def create_delivery(enterprise) + delivery = create_shipping_method( + enterprise, + name: "Home delivery", + description: "yummy food delivered at your door", + require_ship_address: true, + calculator_type: "Spree::Calculator::FlatRate" + ) + delivery.calculator.preferred_amount = 2 + delivery.calculator.save! + end + + def create_shipping_method(enterprise, params) + params[:distributor_ids] = [enterprise.id] + method = enterprise.shipping_methods.new(params) + method.zone = zone + method.save! + method + end + end + + class FeeFactory + include Logging + + def create_samples(enterprises) + log "Creating fees:" + enterprises.each do |enterprise| + next if enterprise.enterprise_fees.present? + log "- #{enterprise.name} charges markup" + calculator = Calculator::FlatPercentPerItem.new(preferred_flat_percent: 10) + create_fee(enterprise, calculator) + calculator.save! + end + end + + private + + def create_fee(enterprise, calculator) + fee = enterprise.enterprise_fees.new( + fee_type: "sales", + name: "markup", + inherits_tax_category: true, + ) + fee.calculator = calculator + fee.save! + end + end + + class PermissionFactory + include Logging + + def create_samples(enterprises) + all_permissions = [ + :add_to_order_cycle, + :manage_products, + :edit_profile, + :create_variant_overrides + ] + enterprises.each do |enterprise| + log "#{enterprise.name} permits everybody to do everything." + enterprise_permits_to(enterprise, enterprises, all_permissions) + end + end + + private + + def enterprise_permits_to(enterprise, receivers, permissions) + receivers.each do |receiver| + EnterpriseRelationship.where( + parent_id: enterprise, + child_id: receiver + ).first_or_create!( + parent: enterprise, + child: receiver, + permissions_list: permissions + ) + end + end + end + + class TaxonFactory + include Logging + + def create_samples + log "Creating taxonomies:" + taxonomy = Spree::Taxonomy.find_or_create_by_name!('Products') + taxons = ['Vegetables', 'Fruit', 'Oils', 'Preserves and Sauces', 'Dairy', 'Meat and Fish'] + taxons.each do |taxon_name| + create_taxon(taxonomy, taxon_name) + end + end + + private + + def create_taxon(taxonomy, taxon_name) + return if Spree::Taxon.where(name: taxon_name).exists? + log "- #{taxon_name}" + Spree::Taxon.create!( + name: taxon_name, + parent_id: taxonomy.root.id, + taxonomy_id: taxonomy.id + ) + end + end + + class ProductFactory + include Logging + + def create_samples(enterprises) + log "Creating products:" + product_data(enterprises).map do |hash| + create_product(hash) + end + end + + private + + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + def product_data(enterprises) + vegetables = Spree::Taxon.find_by_name('Vegetables') + fruit = Spree::Taxon.find_by_name('Fruit') + meat = Spree::Taxon.find_by_name('Meat and Fish') + producers = enterprises.select(&:is_primary_producer) + distributors = enterprises.select(&:is_distributor) + [ + { + name: 'Garlic', + price: 20.00, + supplier: producers[0], + taxons: [vegetables], + distributor: distributors[0] + }, + { + name: 'Fuji Apple', + price: 5.00, + supplier: producers[1], + taxons: [fruit], + distributor: distributors[0] + }, + { + name: 'Beef - 5kg Trays', + price: 50.00, + supplier: producers[1], + taxons: [meat], + distributor: distributors[0] + }, + { + name: 'Carrots', + price: 3.00, + supplier: producers[2], + taxons: [vegetables], + distributor: distributors[0] + }, + { + name: 'Potatoes', + price: 2.00, + supplier: producers[2], + taxons: [vegetables], + distributor: distributors[0] + }, + { + name: 'Tomatoes', + price: 2.00, + supplier: producers[2], + taxons: [vegetables], + distributor: distributors[0] + } + ] + end + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize + + def create_product(hash) + log "- #{hash[:name]}" + params = hash.merge( + supplier_id: hash[:supplier].id, + primary_taxon_id: hash[:taxons].first.id, + variant_unit: "weight", + variant_unit_scale: 1, + unit_value: 1, + on_demand: true + ) + create_product_with_distribution(params) + end + + def create_product_with_distribution(params) + product = Spree::Product.create_with(params).find_or_create_by_name(params[:name]) + ProductDistribution.create( + product: product, + distributor: params[:distributor] + ) + product + end + end + + class InventoryFactory + include Logging + + def create_samples(products) + log "Creating inventories" + marys_shop = Enterprise.find_by_name("Mary's Online Shop") + products.each do |product| + create_item(marys_shop, product) + end + end + + private + + def create_item(shop, product) + InventoryItem.create_with( + enterprise: shop, + variant: product.variants.first, + visible: true + ).find_or_create_by_variant_id(product.variants.first.id) + create_override(shop, product) + end + + def create_override(shop, product) + VariantOverride.create_with( + variant: product.variants.first, + hub: shop, + price: 12, + count_on_hand: 5 + ).find_or_create_by_variant_id(product.variants.first.id) + end + end + + class OrderCycleFactory + include Logging + # rubocop:disable Metrics/MethodLength + def create_samples + log "Creating order cycles" + create_order_cycle( + "Freddy's Farm Shop OC", + "Freddy's Farm Shop", + ["Freddy's Farm Shop"], + ["Freddy's Farm Shop"], + receival_instructions: "Dear self, don't forget the keys.", + pickup_time: "the weekend", + pickup_instructions: "Bring your own shopping bags or boxes." + ) + + create_order_cycle( + "Fredo's Farm Hub OC", + "Fredo's Farm Hub", + ["Fred's Farm", "Fredo's Farm Hub"], + ["Fredo's Farm Hub"], + receival_instructions: "Under the shed, please.", + pickup_time: "Wednesday 2pm", + pickup_instructions: "Boxes for packaging under the roof." + ) + + create_order_cycle( + "Mary's Online Shop OC", + "Mary's Online Shop", + ["Fred's Farm", "Freddy's Farm Shop", "Fredo's Farm Hub"], + ["Mary's Online Shop"], + receival_instructions: "Please shut the gate.", + pickup_time: "midday" + ) + + create_order_cycle( + "Multi Shop OC", + "Mary's Online Shop", + ["Fred's Farm", "Freddy's Farm Shop", "Fredo's Farm Hub"], + ["Mary's Online Shop", "Maryse's Private Shop"], + receival_instructions: "Please shut the gate.", + pickup_time: "dusk" + ) + end + # rubocop:enable Metrics/MethodLength + + private + + def create_order_cycle(name, coordinator_name, supplier_names, distributor_names, data) + coordinator = Enterprise.find_by_name(coordinator_name) + return if OrderCycle.active.where(name: name).exists? + + log "- #{name}" + cycle = create_order_cycle_with_fee(name, coordinator) + create_exchanges(cycle, supplier_names, distributor_names, data) + end + + def create_order_cycle_with_fee(name, coordinator) + cycle = OrderCycle.create!( + name: name, + orders_open_at: 1.day.ago, + orders_close_at: 1.month.from_now, + coordinator: coordinator + ) + cycle.coordinator_fees << coordinator.enterprise_fees.first + cycle + end + + def create_exchanges(cycle, supplier_names, distributor_names, data) + suppliers = Enterprise.where(name: supplier_names) + distributors = Enterprise.where(name: distributor_names) + + incoming = incoming_exchanges(cycle, suppliers, data) + outgoing = outgoing_exchanges(cycle, distributors, data) + all_exchanges = incoming + outgoing + add_products(suppliers, all_exchanges) + end + + def incoming_exchanges(cycle, suppliers, data) + suppliers.map do |supplier| + Exchange.create!( + order_cycle: cycle, + sender: supplier, + receiver: cycle.coordinator, + incoming: true, + receival_instructions: data[:receival_instructions] + ) + end + end + + def outgoing_exchanges(cycle, distributors, data) + distributors.map do |distributor| + Exchange.create!( + order_cycle: cycle, + sender: cycle.coordinator, + receiver: distributor, + incoming: false, + pickup_time: data[:pickup_time], + pickup_instructions: data[:pickup_instructions] + ) + end + end + + def add_products(suppliers, exchanges) + products = suppliers.flat_map(&:supplied_products) + products.each do |product| + exchanges.each { |exchange| exchange.variants << product.variants.first } + end + end + end + + class CustomerFactory + include Logging + + def create_samples(users) + log "Creating customers" + jane = users["Jane Customer"] + maryse_shop = Enterprise.find_by_name("Maryse's Private Shop") + return if Customer.where(user_id: jane, enterprise_id: maryse_shop).exists? + log "- #{jane.email}" + Customer.create!( + email: jane.email, + user: jane, + enterprise: maryse_shop + ) + end + end + + class GroupFactory + include Logging + include Addressing + + def create_samples + log "Creating groups" + return if EnterpriseGroup.where(name: "Producer group").exists? + + create_group( + name: "Producer group", + owner: enterprises.first.owner, + on_front_page: true, + description: "The seed producers", + address: "6 Rollings Road, Upper Ferntree Gully, 3156" + ) + end + + private + + def create_group(params) + group = EnterpriseGroup.new(params) + group.address = address(params[:address]) + group.enterprises = enterprises + group.save! + end + + def enterprises + @enterprises ||= Enterprise.where(name: [ + "Fred's Farm", + "Freddy's Farm Shop", + "Fredo's Farm Hub" + ]) + end + end +end From fae05ff713640d14a257304b9748f5036225ad10 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 13 Dec 2018 12:44:51 +1100 Subject: [PATCH 02/16] Deprecate old sample data task --- lib/tasks/dev.rake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 2eff925395..a1ead17f5c 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -316,6 +316,15 @@ namespace :ofn do if EnterpriseRole.count < 1 EnterpriseRole.create!(user: Spree::User.first, enterprise: enterprise2) end + puts < Date: Tue, 8 Jan 2019 14:27:25 +1100 Subject: [PATCH 03/16] Move sample data logger to own module --- lib/tasks/sample_data.rake | 10 ++-------- lib/tasks/sample_data/logging.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 lib/tasks/sample_data/logging.rb diff --git a/lib/tasks/sample_data.rake b/lib/tasks/sample_data.rake index d93300e51f..a78ff15ae7 100644 --- a/lib/tasks/sample_data.rake +++ b/lib/tasks/sample_data.rake @@ -1,3 +1,5 @@ +require "tasks/sample_data/logging" + # The sample data generated by this task is supposed to save some time during # manual testing. It is not meant to be complete, but we try to improve it # over time. How much is hardcoded here is a trade off between developer time @@ -44,14 +46,6 @@ namespace :openfoodnetwork do Spree::State.count > 0 end - module Logging - private - - def log(message) - puts "[openfoodnetwork:sample_data:load] #{message}" - end - end - module Addressing private diff --git a/lib/tasks/sample_data/logging.rb b/lib/tasks/sample_data/logging.rb new file mode 100644 index 0000000000..ed68d7e52e --- /dev/null +++ b/lib/tasks/sample_data/logging.rb @@ -0,0 +1,8 @@ +module Logging + private + + def log(message) + @logger ||= ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) + @logger.tagged("openfoodnetwork:sample_data:load") { @logger.info(message) } + end +end From 3ab02d90df3f3040fba4924ebd9f1075ce663c02 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 8 Jan 2019 14:34:00 +1100 Subject: [PATCH 04/16] Move sample data address module to own file --- lib/tasks/sample_data.rake | 27 +-------------------------- lib/tasks/sample_data/addressing.rb | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 26 deletions(-) create mode 100644 lib/tasks/sample_data/addressing.rb diff --git a/lib/tasks/sample_data.rake b/lib/tasks/sample_data.rake index a78ff15ae7..b31f9c05f3 100644 --- a/lib/tasks/sample_data.rake +++ b/lib/tasks/sample_data.rake @@ -1,3 +1,4 @@ +require "tasks/sample_data/addressing" require "tasks/sample_data/logging" # The sample data generated by this task is supposed to save some time during @@ -46,32 +47,6 @@ namespace :openfoodnetwork do Spree::State.count > 0 end - module Addressing - private - - def address(string) - state = country.states.first - parts = string.split(", ") - Spree::Address.new( - address1: parts[0], - city: parts[1], - zipcode: parts[2], - state: state, - country: country - ) - end - - def zone - zone = Spree::Zone.find_or_create_by_name!("Australia") - zone.members.create(zonable: country) - zone - end - - def country - Spree::Country.find_by_iso(ENV.fetch('DEFAULT_COUNTRY_CODE')) - end - end - class UserFactory include Logging diff --git a/lib/tasks/sample_data/addressing.rb b/lib/tasks/sample_data/addressing.rb new file mode 100644 index 0000000000..1a50a8df95 --- /dev/null +++ b/lib/tasks/sample_data/addressing.rb @@ -0,0 +1,25 @@ +module Addressing + private + + def address(string) + state = country.states.first + parts = string.split(", ") + Spree::Address.new( + address1: parts[0], + city: parts[1], + zipcode: parts[2], + state: state, + country: country + ) + end + + def zone + zone = Spree::Zone.find_or_create_by_name!("Australia") + zone.members.create(zonable: country) + zone + end + + def country + Spree::Country.find_by_iso(ENV.fetch('DEFAULT_COUNTRY_CODE')) + end +end From 4b389ba357de597ee378f3c9bc093c52da95afb0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 8 Jan 2019 14:51:35 +1100 Subject: [PATCH 05/16] Move sample data factories into their own files --- lib/tasks/sample_data.rake | 588 +----------------- lib/tasks/sample_data/customer_factory.rb | 18 + lib/tasks/sample_data/enterprise_factory.rb | 69 ++ lib/tasks/sample_data/fee_factory.rb | 28 + lib/tasks/sample_data/group_factory.rb | 39 ++ lib/tasks/sample_data/inventory_factory.rb | 34 + lib/tasks/sample_data/order_cycle_factory.rb | 111 ++++ .../sample_data/payment_method_factory.rb | 52 ++ lib/tasks/sample_data/permission_factory.rb | 33 + lib/tasks/sample_data/product_factory.rb | 90 +++ .../sample_data/shipping_method_factory.rb | 54 ++ lib/tasks/sample_data/taxon_factory.rb | 26 + lib/tasks/sample_data/user_factory.rb | 40 ++ 13 files changed, 606 insertions(+), 576 deletions(-) create mode 100644 lib/tasks/sample_data/customer_factory.rb create mode 100644 lib/tasks/sample_data/enterprise_factory.rb create mode 100644 lib/tasks/sample_data/fee_factory.rb create mode 100644 lib/tasks/sample_data/group_factory.rb create mode 100644 lib/tasks/sample_data/inventory_factory.rb create mode 100644 lib/tasks/sample_data/order_cycle_factory.rb create mode 100644 lib/tasks/sample_data/payment_method_factory.rb create mode 100644 lib/tasks/sample_data/permission_factory.rb create mode 100644 lib/tasks/sample_data/product_factory.rb create mode 100644 lib/tasks/sample_data/shipping_method_factory.rb create mode 100644 lib/tasks/sample_data/taxon_factory.rb create mode 100644 lib/tasks/sample_data/user_factory.rb diff --git a/lib/tasks/sample_data.rake b/lib/tasks/sample_data.rake index b31f9c05f3..bb41d2c7d6 100644 --- a/lib/tasks/sample_data.rake +++ b/lib/tasks/sample_data.rake @@ -1,5 +1,15 @@ -require "tasks/sample_data/addressing" -require "tasks/sample_data/logging" +require "tasks/sample_data/customer_factory" +require "tasks/sample_data/enterprise_factory" +require "tasks/sample_data/fee_factory" +require "tasks/sample_data/group_factory" +require "tasks/sample_data/inventory_factory" +require "tasks/sample_data/order_cycle_factory" +require "tasks/sample_data/payment_method_factory" +require "tasks/sample_data/permission_factory" +require "tasks/sample_data/product_factory" +require "tasks/sample_data/shipping_method_factory" +require "tasks/sample_data/taxon_factory" +require "tasks/sample_data/user_factory" # The sample data generated by this task is supposed to save some time during # manual testing. It is not meant to be complete, but we try to improve it @@ -46,578 +56,4 @@ namespace :openfoodnetwork do Spree::Country.count > 0 && Spree::State.count > 0 end - - class UserFactory - include Logging - - def create_samples - log "Creating users:" - usernames.map { |name| - create_user(name) - }.to_h - end - - private - - def usernames - [ - "Manel Super Admin", - "Penny Profile", - "Fred Farmer", - "Freddy Shop Farmer", - "Fredo Hub Farmer", - "Mary Retailer", - "Maryse Private", - "Jane Customer" - ] - end - - def create_user(name) - email = "#{name.downcase.tr(' ', '.')}@example.org" - password = Spree::User.friendly_token - log "- #{email}" - user = Spree::User.create_with( - password: password, - password_confirmation: password, - confirmation_sent_at: Time.zone.now, - confirmed_at: Time.zone.now - ).find_or_create_by_email!(email) - [name, user] - end - end - - class EnterpriseFactory - include Logging - include Addressing - - def create_samples(users) - log "Creating enterprises:" - enterprise_data(users).map do |data| - name = data[:name] - log "- #{name}" - Enterprise.create_with(data).find_or_create_by_name!(name) - end - end - - private - - # rubocop:disable Metrics/MethodLength - def enterprise_data(users) - [ - { - name: "Penny's Profile", - owner: users["Penny Profile"], - is_primary_producer: false, - sells: "none", - address: address("25 Myrtle Street, Bayswater, 3153") - }, - { - name: "Fred's Farm", - owner: users["Fred Farmer"], - is_primary_producer: true, - sells: "none", - address: address("6 Rollings Road, Upper Ferntree Gully, 3156") - }, - { - name: "Freddy's Farm Shop", - owner: users["Freddy Shop Farmer"], - is_primary_producer: true, - sells: "own", - address: address("72 Lake Road, Blackburn, 3130") - }, - { - name: "Fredo's Farm Hub", - owner: users["Fredo Hub Farmer"], - is_primary_producer: true, - sells: "any", - address: address("7 Verbena Street, Mordialloc, 3195") - }, - { - name: "Mary's Online Shop", - owner: users["Mary Retailer"], - is_primary_producer: false, - sells: "any", - address: address("20 Galvin Street, Altona, 3018") - }, - { - name: "Maryse's Private Shop", - owner: users["Maryse Private"], - is_primary_producer: false, - sells: "any", - address: address("6 Martin Street, Belgrave, 3160"), - require_login: true - } - ] - end - # rubocop:enable Metrics/MethodLength - end - - class PaymentMethodFactory - include Logging - include Addressing - - def create_samples(enterprises) - log "Creating payment methods:" - distributors = enterprises.select(&:is_distributor) - distributors.each do |enterprise| - create_payment_methods(enterprise) - end - end - - private - - def create_payment_methods(enterprise) - return if enterprise.payment_methods.present? - log "- #{enterprise.name}" - create_cash_method(enterprise) - create_card_method(enterprise) - end - - def create_cash_method(enterprise) - create_payment_method( - enterprise, - "Cash on collection", - "Pay on collection!", - Spree::Calculator::FlatRate.new - ) - end - - def create_card_method(enterprise) - create_payment_method( - enterprise, - "Credit card (fake)", - "We charge 1%, but won't ask for you details. ;-)", - Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 1) - ) - end - - def create_payment_method(enterprise, name, description, calculator) - card = enterprise.payment_methods.new( - name: name, - description: description, - distributor_ids: [enterprise.id] - ) - card.calculator = calculator - card.save! - end - end - - class ShippingMethodFactory - include Logging - include Addressing - - def create_samples(enterprises) - log "Creating shipping methods:" - distributors = enterprises.select(&:is_distributor) - distributors.each do |enterprise| - create_shipping_methods(enterprise) - end - end - - private - - def create_shipping_methods(enterprise) - return if enterprise.shipping_methods.present? - log "- #{enterprise.name}" - create_pickup(enterprise) - create_delivery(enterprise) - end - - def create_pickup(enterprise) - create_shipping_method( - enterprise, - name: "Pickup", - description: "pick-up at your awesome hub gathering place", - require_ship_address: false, - calculator_type: "Calculator::Weight" - ) - end - - def create_delivery(enterprise) - delivery = create_shipping_method( - enterprise, - name: "Home delivery", - description: "yummy food delivered at your door", - require_ship_address: true, - calculator_type: "Spree::Calculator::FlatRate" - ) - delivery.calculator.preferred_amount = 2 - delivery.calculator.save! - end - - def create_shipping_method(enterprise, params) - params[:distributor_ids] = [enterprise.id] - method = enterprise.shipping_methods.new(params) - method.zone = zone - method.save! - method - end - end - - class FeeFactory - include Logging - - def create_samples(enterprises) - log "Creating fees:" - enterprises.each do |enterprise| - next if enterprise.enterprise_fees.present? - log "- #{enterprise.name} charges markup" - calculator = Calculator::FlatPercentPerItem.new(preferred_flat_percent: 10) - create_fee(enterprise, calculator) - calculator.save! - end - end - - private - - def create_fee(enterprise, calculator) - fee = enterprise.enterprise_fees.new( - fee_type: "sales", - name: "markup", - inherits_tax_category: true, - ) - fee.calculator = calculator - fee.save! - end - end - - class PermissionFactory - include Logging - - def create_samples(enterprises) - all_permissions = [ - :add_to_order_cycle, - :manage_products, - :edit_profile, - :create_variant_overrides - ] - enterprises.each do |enterprise| - log "#{enterprise.name} permits everybody to do everything." - enterprise_permits_to(enterprise, enterprises, all_permissions) - end - end - - private - - def enterprise_permits_to(enterprise, receivers, permissions) - receivers.each do |receiver| - EnterpriseRelationship.where( - parent_id: enterprise, - child_id: receiver - ).first_or_create!( - parent: enterprise, - child: receiver, - permissions_list: permissions - ) - end - end - end - - class TaxonFactory - include Logging - - def create_samples - log "Creating taxonomies:" - taxonomy = Spree::Taxonomy.find_or_create_by_name!('Products') - taxons = ['Vegetables', 'Fruit', 'Oils', 'Preserves and Sauces', 'Dairy', 'Meat and Fish'] - taxons.each do |taxon_name| - create_taxon(taxonomy, taxon_name) - end - end - - private - - def create_taxon(taxonomy, taxon_name) - return if Spree::Taxon.where(name: taxon_name).exists? - log "- #{taxon_name}" - Spree::Taxon.create!( - name: taxon_name, - parent_id: taxonomy.root.id, - taxonomy_id: taxonomy.id - ) - end - end - - class ProductFactory - include Logging - - def create_samples(enterprises) - log "Creating products:" - product_data(enterprises).map do |hash| - create_product(hash) - end - end - - private - - # rubocop:disable Metrics/MethodLength, Metrics/AbcSize - def product_data(enterprises) - vegetables = Spree::Taxon.find_by_name('Vegetables') - fruit = Spree::Taxon.find_by_name('Fruit') - meat = Spree::Taxon.find_by_name('Meat and Fish') - producers = enterprises.select(&:is_primary_producer) - distributors = enterprises.select(&:is_distributor) - [ - { - name: 'Garlic', - price: 20.00, - supplier: producers[0], - taxons: [vegetables], - distributor: distributors[0] - }, - { - name: 'Fuji Apple', - price: 5.00, - supplier: producers[1], - taxons: [fruit], - distributor: distributors[0] - }, - { - name: 'Beef - 5kg Trays', - price: 50.00, - supplier: producers[1], - taxons: [meat], - distributor: distributors[0] - }, - { - name: 'Carrots', - price: 3.00, - supplier: producers[2], - taxons: [vegetables], - distributor: distributors[0] - }, - { - name: 'Potatoes', - price: 2.00, - supplier: producers[2], - taxons: [vegetables], - distributor: distributors[0] - }, - { - name: 'Tomatoes', - price: 2.00, - supplier: producers[2], - taxons: [vegetables], - distributor: distributors[0] - } - ] - end - # rubocop:enable Metrics/MethodLength, Metrics/AbcSize - - def create_product(hash) - log "- #{hash[:name]}" - params = hash.merge( - supplier_id: hash[:supplier].id, - primary_taxon_id: hash[:taxons].first.id, - variant_unit: "weight", - variant_unit_scale: 1, - unit_value: 1, - on_demand: true - ) - create_product_with_distribution(params) - end - - def create_product_with_distribution(params) - product = Spree::Product.create_with(params).find_or_create_by_name(params[:name]) - ProductDistribution.create( - product: product, - distributor: params[:distributor] - ) - product - end - end - - class InventoryFactory - include Logging - - def create_samples(products) - log "Creating inventories" - marys_shop = Enterprise.find_by_name("Mary's Online Shop") - products.each do |product| - create_item(marys_shop, product) - end - end - - private - - def create_item(shop, product) - InventoryItem.create_with( - enterprise: shop, - variant: product.variants.first, - visible: true - ).find_or_create_by_variant_id(product.variants.first.id) - create_override(shop, product) - end - - def create_override(shop, product) - VariantOverride.create_with( - variant: product.variants.first, - hub: shop, - price: 12, - count_on_hand: 5 - ).find_or_create_by_variant_id(product.variants.first.id) - end - end - - class OrderCycleFactory - include Logging - # rubocop:disable Metrics/MethodLength - def create_samples - log "Creating order cycles" - create_order_cycle( - "Freddy's Farm Shop OC", - "Freddy's Farm Shop", - ["Freddy's Farm Shop"], - ["Freddy's Farm Shop"], - receival_instructions: "Dear self, don't forget the keys.", - pickup_time: "the weekend", - pickup_instructions: "Bring your own shopping bags or boxes." - ) - - create_order_cycle( - "Fredo's Farm Hub OC", - "Fredo's Farm Hub", - ["Fred's Farm", "Fredo's Farm Hub"], - ["Fredo's Farm Hub"], - receival_instructions: "Under the shed, please.", - pickup_time: "Wednesday 2pm", - pickup_instructions: "Boxes for packaging under the roof." - ) - - create_order_cycle( - "Mary's Online Shop OC", - "Mary's Online Shop", - ["Fred's Farm", "Freddy's Farm Shop", "Fredo's Farm Hub"], - ["Mary's Online Shop"], - receival_instructions: "Please shut the gate.", - pickup_time: "midday" - ) - - create_order_cycle( - "Multi Shop OC", - "Mary's Online Shop", - ["Fred's Farm", "Freddy's Farm Shop", "Fredo's Farm Hub"], - ["Mary's Online Shop", "Maryse's Private Shop"], - receival_instructions: "Please shut the gate.", - pickup_time: "dusk" - ) - end - # rubocop:enable Metrics/MethodLength - - private - - def create_order_cycle(name, coordinator_name, supplier_names, distributor_names, data) - coordinator = Enterprise.find_by_name(coordinator_name) - return if OrderCycle.active.where(name: name).exists? - - log "- #{name}" - cycle = create_order_cycle_with_fee(name, coordinator) - create_exchanges(cycle, supplier_names, distributor_names, data) - end - - def create_order_cycle_with_fee(name, coordinator) - cycle = OrderCycle.create!( - name: name, - orders_open_at: 1.day.ago, - orders_close_at: 1.month.from_now, - coordinator: coordinator - ) - cycle.coordinator_fees << coordinator.enterprise_fees.first - cycle - end - - def create_exchanges(cycle, supplier_names, distributor_names, data) - suppliers = Enterprise.where(name: supplier_names) - distributors = Enterprise.where(name: distributor_names) - - incoming = incoming_exchanges(cycle, suppliers, data) - outgoing = outgoing_exchanges(cycle, distributors, data) - all_exchanges = incoming + outgoing - add_products(suppliers, all_exchanges) - end - - def incoming_exchanges(cycle, suppliers, data) - suppliers.map do |supplier| - Exchange.create!( - order_cycle: cycle, - sender: supplier, - receiver: cycle.coordinator, - incoming: true, - receival_instructions: data[:receival_instructions] - ) - end - end - - def outgoing_exchanges(cycle, distributors, data) - distributors.map do |distributor| - Exchange.create!( - order_cycle: cycle, - sender: cycle.coordinator, - receiver: distributor, - incoming: false, - pickup_time: data[:pickup_time], - pickup_instructions: data[:pickup_instructions] - ) - end - end - - def add_products(suppliers, exchanges) - products = suppliers.flat_map(&:supplied_products) - products.each do |product| - exchanges.each { |exchange| exchange.variants << product.variants.first } - end - end - end - - class CustomerFactory - include Logging - - def create_samples(users) - log "Creating customers" - jane = users["Jane Customer"] - maryse_shop = Enterprise.find_by_name("Maryse's Private Shop") - return if Customer.where(user_id: jane, enterprise_id: maryse_shop).exists? - log "- #{jane.email}" - Customer.create!( - email: jane.email, - user: jane, - enterprise: maryse_shop - ) - end - end - - class GroupFactory - include Logging - include Addressing - - def create_samples - log "Creating groups" - return if EnterpriseGroup.where(name: "Producer group").exists? - - create_group( - name: "Producer group", - owner: enterprises.first.owner, - on_front_page: true, - description: "The seed producers", - address: "6 Rollings Road, Upper Ferntree Gully, 3156" - ) - end - - private - - def create_group(params) - group = EnterpriseGroup.new(params) - group.address = address(params[:address]) - group.enterprises = enterprises - group.save! - end - - def enterprises - @enterprises ||= Enterprise.where(name: [ - "Fred's Farm", - "Freddy's Farm Shop", - "Fredo's Farm Hub" - ]) - end - end end diff --git a/lib/tasks/sample_data/customer_factory.rb b/lib/tasks/sample_data/customer_factory.rb new file mode 100644 index 0000000000..4c1904f2cd --- /dev/null +++ b/lib/tasks/sample_data/customer_factory.rb @@ -0,0 +1,18 @@ +require "tasks/sample_data/logging" + +class CustomerFactory + include Logging + + def create_samples(users) + log "Creating customers" + jane = users["Jane Customer"] + maryse_shop = Enterprise.find_by_name("Maryse's Private Shop") + return if Customer.where(user_id: jane, enterprise_id: maryse_shop).exists? + log "- #{jane.email}" + Customer.create!( + email: jane.email, + user: jane, + enterprise: maryse_shop + ) + end +end diff --git a/lib/tasks/sample_data/enterprise_factory.rb b/lib/tasks/sample_data/enterprise_factory.rb new file mode 100644 index 0000000000..3bf18109a0 --- /dev/null +++ b/lib/tasks/sample_data/enterprise_factory.rb @@ -0,0 +1,69 @@ +require "tasks/sample_data/addressing" +require "tasks/sample_data/logging" + +class EnterpriseFactory + include Logging + include Addressing + + def create_samples(users) + log "Creating enterprises:" + enterprise_data(users).map do |data| + name = data[:name] + log "- #{name}" + data[:long_description] = data[:long_description].strip_heredoc.tr("\n", " ") + Enterprise.create_with(data).find_or_create_by_name!(name) + end + end + + private + + # rubocop:disable Metrics/MethodLength + def enterprise_data(users) + [ + { + name: "Penny's Profile", + owner: users["Penny Profile"], + is_primary_producer: false, + sells: "none", + address: address("25 Myrtle Street, Bayswater, 3153") + }, + { + name: "Fred's Farm", + owner: users["Fred Farmer"], + is_primary_producer: true, + sells: "none", + address: address("6 Rollings Road, Upper Ferntree Gully, 3156") + }, + { + name: "Freddy's Farm Shop", + owner: users["Freddy Shop Farmer"], + is_primary_producer: true, + sells: "own", + address: address("72 Lake Road, Blackburn, 3130") + }, + { + name: "Fredo's Farm Hub", + owner: users["Fredo Hub Farmer"], + is_primary_producer: true, + sells: "any", + address: address("7 Verbena Street, Mordialloc, 3195") + }, + { + name: "Mary's Online Shop", + owner: users["Mary Retailer"], + is_primary_producer: false, + sells: "any", + address: address("20 Galvin Street, Altona, 3018") + }, + { + name: "Maryse's Private Shop", + owner: users["Maryse Private"], + is_primary_producer: false, + sells: "any", + address: address("6 Martin Street, Belgrave, 3160"), + require_login: true + } + ] + end + # rubocop:enable Metrics/MethodLength +end diff --git a/lib/tasks/sample_data/fee_factory.rb b/lib/tasks/sample_data/fee_factory.rb new file mode 100644 index 0000000000..72d062e852 --- /dev/null +++ b/lib/tasks/sample_data/fee_factory.rb @@ -0,0 +1,28 @@ +require "tasks/sample_data/logging" + +class FeeFactory + include Logging + + def create_samples(enterprises) + log "Creating fees:" + enterprises.each do |enterprise| + next if enterprise.enterprise_fees.present? + log "- #{enterprise.name} charges markup" + calculator = Calculator::FlatPercentPerItem.new(preferred_flat_percent: 10) + create_fee(enterprise, calculator) + calculator.save! + end + end + + private + + def create_fee(enterprise, calculator) + fee = enterprise.enterprise_fees.new( + fee_type: "sales", + name: "markup", + inherits_tax_category: true, + ) + fee.calculator = calculator + fee.save! + end +end diff --git a/lib/tasks/sample_data/group_factory.rb b/lib/tasks/sample_data/group_factory.rb new file mode 100644 index 0000000000..15e1ed1fb9 --- /dev/null +++ b/lib/tasks/sample_data/group_factory.rb @@ -0,0 +1,39 @@ +require "tasks/sample_data/addressing" +require "tasks/sample_data/logging" + +class GroupFactory + include Logging + include Addressing + + def create_samples + log "Creating groups" + return if EnterpriseGroup.where(name: "Producer group").exists? + + create_group( + name: "Producer group", + owner: enterprises.first.owner, + on_front_page: true, + description: "The seed producers", + address: "6 Rollings Road, Upper Ferntree Gully, 3156" + ) + end + + private + + def create_group(params) + group = EnterpriseGroup.new(params) + group.address = address(params[:address]) + group.enterprises = enterprises + group.save! + end + + def enterprises + @enterprises ||= Enterprise.where( + name: [ + "Fred's Farm", + "Freddy's Farm Shop", + "Fredo's Farm Hub" + ] + ) + end +end diff --git a/lib/tasks/sample_data/inventory_factory.rb b/lib/tasks/sample_data/inventory_factory.rb new file mode 100644 index 0000000000..d37557f67f --- /dev/null +++ b/lib/tasks/sample_data/inventory_factory.rb @@ -0,0 +1,34 @@ +require "tasks/sample_data/logging" + +class InventoryFactory + include Logging + + def create_samples(products) + log "Creating inventories" + marys_shop = Enterprise.find_by_name("Mary's Online Shop") + products.each do |product| + create_item(marys_shop, product) + end + end + + private + + def create_item(shop, product) + InventoryItem.create_with( + enterprise: shop, + variant: product.variants.first, + visible: true + ).find_or_create_by_variant_id(product.variants.first.id) + create_override(shop, product) + end + + def create_override(shop, product) + VariantOverride.create_with( + variant: product.variants.first, + hub: shop, + price: 12, + on_demand: false, + count_on_hand: 5 + ).find_or_create_by_variant_id(product.variants.first.id) + end +end diff --git a/lib/tasks/sample_data/order_cycle_factory.rb b/lib/tasks/sample_data/order_cycle_factory.rb new file mode 100644 index 0000000000..6ba5b8e5ef --- /dev/null +++ b/lib/tasks/sample_data/order_cycle_factory.rb @@ -0,0 +1,111 @@ +require "tasks/sample_data/logging" + +class OrderCycleFactory + include Logging + # rubocop:disable Metrics/MethodLength + def create_samples + log "Creating order cycles" + create_order_cycle( + "Freddy's Farm Shop OC", + "Freddy's Farm Shop", + ["Freddy's Farm Shop"], + ["Freddy's Farm Shop"], + receival_instructions: "Dear self, don't forget the keys.", + pickup_time: "the weekend", + pickup_instructions: "Bring your own shopping bags or boxes." + ) + + create_order_cycle( + "Fredo's Farm Hub OC", + "Fredo's Farm Hub", + ["Fred's Farm", "Fredo's Farm Hub"], + ["Fredo's Farm Hub"], + receival_instructions: "Under the shed, please.", + pickup_time: "Wednesday 2pm", + pickup_instructions: "Boxes for packaging under the roof." + ) + + create_order_cycle( + "Mary's Online Shop OC", + "Mary's Online Shop", + ["Fred's Farm", "Freddy's Farm Shop", "Fredo's Farm Hub"], + ["Mary's Online Shop"], + receival_instructions: "Please shut the gate.", + pickup_time: "midday" + ) + + create_order_cycle( + "Multi Shop OC", + "Mary's Online Shop", + ["Fred's Farm", "Freddy's Farm Shop", "Fredo's Farm Hub"], + ["Mary's Online Shop", "Maryse's Private Shop"], + receival_instructions: "Please shut the gate.", + pickup_time: "dusk" + ) + end + # rubocop:enable Metrics/MethodLength + + private + + def create_order_cycle(name, coordinator_name, supplier_names, distributor_names, data) + coordinator = Enterprise.find_by_name(coordinator_name) + return if OrderCycle.active.where(name: name).exists? + + log "- #{name}" + cycle = create_order_cycle_with_fee(name, coordinator) + create_exchanges(cycle, supplier_names, distributor_names, data) + end + + def create_order_cycle_with_fee(name, coordinator) + cycle = OrderCycle.create!( + name: name, + orders_open_at: 1.day.ago, + orders_close_at: 1.month.from_now, + coordinator: coordinator + ) + cycle.coordinator_fees << coordinator.enterprise_fees.first + cycle + end + + def create_exchanges(cycle, supplier_names, distributor_names, data) + suppliers = Enterprise.where(name: supplier_names) + distributors = Enterprise.where(name: distributor_names) + + incoming = incoming_exchanges(cycle, suppliers, data) + outgoing = outgoing_exchanges(cycle, distributors, data) + all_exchanges = incoming + outgoing + add_products(suppliers, all_exchanges) + end + + def incoming_exchanges(cycle, suppliers, data) + suppliers.map do |supplier| + Exchange.create!( + order_cycle: cycle, + sender: supplier, + receiver: cycle.coordinator, + incoming: true, + receival_instructions: data[:receival_instructions] + ) + end + end + + def outgoing_exchanges(cycle, distributors, data) + distributors.map do |distributor| + Exchange.create!( + order_cycle: cycle, + sender: cycle.coordinator, + receiver: distributor, + incoming: false, + pickup_time: data[:pickup_time], + pickup_instructions: data[:pickup_instructions] + ) + end + end + + def add_products(suppliers, exchanges) + products = suppliers.flat_map(&:supplied_products) + products.each do |product| + exchanges.each { |exchange| exchange.variants << product.variants.first } + end + end +end diff --git a/lib/tasks/sample_data/payment_method_factory.rb b/lib/tasks/sample_data/payment_method_factory.rb new file mode 100644 index 0000000000..43fd43348f --- /dev/null +++ b/lib/tasks/sample_data/payment_method_factory.rb @@ -0,0 +1,52 @@ +require "tasks/sample_data/addressing" +require "tasks/sample_data/logging" + +class PaymentMethodFactory + include Logging + include Addressing + + def create_samples(enterprises) + log "Creating payment methods:" + distributors = enterprises.select(&:is_distributor) + distributors.each do |enterprise| + create_payment_methods(enterprise) + end + end + + private + + def create_payment_methods(enterprise) + return if enterprise.payment_methods.present? + log "- #{enterprise.name}" + create_cash_method(enterprise) + create_card_method(enterprise) + end + + def create_cash_method(enterprise) + create_payment_method( + enterprise, + "Cash on collection", + "Pay on collection!", + Spree::Calculator::FlatRate.new + ) + end + + def create_card_method(enterprise) + create_payment_method( + enterprise, + "Credit card (fake)", + "We charge 1%, but won't ask for your details. ;-)", + Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 1) + ) + end + + def create_payment_method(enterprise, name, description, calculator) + card = enterprise.payment_methods.new( + name: name, + description: description, + distributor_ids: [enterprise.id] + ) + card.calculator = calculator + card.save! + end +end diff --git a/lib/tasks/sample_data/permission_factory.rb b/lib/tasks/sample_data/permission_factory.rb new file mode 100644 index 0000000000..48a62976d2 --- /dev/null +++ b/lib/tasks/sample_data/permission_factory.rb @@ -0,0 +1,33 @@ +require "tasks/sample_data/logging" + +class PermissionFactory + include Logging + + def create_samples(enterprises) + all_permissions = [ + :add_to_order_cycle, + :manage_products, + :edit_profile, + :create_variant_overrides + ] + enterprises.each do |enterprise| + log "#{enterprise.name} permits everybody to do everything." + enterprise_permits_to(enterprise, enterprises, all_permissions) + end + end + + private + + def enterprise_permits_to(enterprise, receivers, permissions) + receivers.each do |receiver| + EnterpriseRelationship.where( + parent_id: enterprise, + child_id: receiver + ).first_or_create!( + parent: enterprise, + child: receiver, + permissions_list: permissions + ) + end + end +end diff --git a/lib/tasks/sample_data/product_factory.rb b/lib/tasks/sample_data/product_factory.rb new file mode 100644 index 0000000000..b5b6544cb0 --- /dev/null +++ b/lib/tasks/sample_data/product_factory.rb @@ -0,0 +1,90 @@ +require "tasks/sample_data/logging" + +class ProductFactory + include Logging + + def create_samples(enterprises) + log "Creating products:" + product_data(enterprises).map do |hash| + create_product(hash) + end + end + + private + + # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + def product_data(enterprises) + vegetables = Spree::Taxon.find_by_name('Vegetables') + fruit = Spree::Taxon.find_by_name('Fruit') + meat = Spree::Taxon.find_by_name('Meat and Fish') + producers = enterprises.select(&:is_primary_producer) + distributors = enterprises.select(&:is_distributor) + [ + { + name: 'Garlic', + price: 20.00, + supplier: producers[0], + taxons: [vegetables], + distributor: distributors[0] + }, + { + name: 'Fuji Apple', + price: 5.00, + supplier: producers[1], + taxons: [fruit], + distributor: distributors[0] + }, + { + name: 'Beef - 5kg Trays', + price: 50.00, + supplier: producers[1], + taxons: [meat], + distributor: distributors[0] + }, + { + name: 'Carrots', + price: 3.00, + supplier: producers[2], + taxons: [vegetables], + distributor: distributors[0] + }, + { + name: 'Potatoes', + price: 2.00, + supplier: producers[2], + taxons: [vegetables], + distributor: distributors[0] + }, + { + name: 'Tomatoes', + price: 2.00, + supplier: producers[2], + taxons: [vegetables], + distributor: distributors[0] + } + ] + end + # rubocop:enable Metrics/MethodLength, Metrics/AbcSize + + def create_product(hash) + log "- #{hash[:name]}" + params = hash.merge( + supplier_id: hash[:supplier].id, + primary_taxon_id: hash[:taxons].first.id, + variant_unit: "weight", + variant_unit_scale: 1, + unit_value: 1, + on_demand: true + ) + create_product_with_distribution(params) + end + + def create_product_with_distribution(params) + product = Spree::Product.create_with(params).find_or_create_by_name(params[:name]) + ProductDistribution.create( + product: product, + distributor: params[:distributor] + ) + product + end +end diff --git a/lib/tasks/sample_data/shipping_method_factory.rb b/lib/tasks/sample_data/shipping_method_factory.rb new file mode 100644 index 0000000000..87e3747781 --- /dev/null +++ b/lib/tasks/sample_data/shipping_method_factory.rb @@ -0,0 +1,54 @@ +require "tasks/sample_data/addressing" +require "tasks/sample_data/logging" + +class ShippingMethodFactory + include Logging + include Addressing + + def create_samples(enterprises) + log "Creating shipping methods:" + distributors = enterprises.select(&:is_distributor) + distributors.each do |enterprise| + create_shipping_methods(enterprise) + end + end + + private + + def create_shipping_methods(enterprise) + return if enterprise.shipping_methods.present? + log "- #{enterprise.name}" + create_pickup(enterprise) + create_delivery(enterprise) + end + + def create_pickup(enterprise) + create_shipping_method( + enterprise, + name: "Pickup", + description: "pick-up at your awesome hub gathering place", + require_ship_address: false, + calculator_type: "Calculator::Weight" + ) + end + + def create_delivery(enterprise) + delivery = create_shipping_method( + enterprise, + name: "Home delivery", + description: "yummy food delivered at your door", + require_ship_address: true, + calculator_type: "Spree::Calculator::FlatRate" + ) + delivery.calculator.preferred_amount = 2 + delivery.calculator.save! + end + + def create_shipping_method(enterprise, params) + params[:distributor_ids] = [enterprise.id] + method = enterprise.shipping_methods.new(params) + method.zone = zone + method.save! + method + end +end diff --git a/lib/tasks/sample_data/taxon_factory.rb b/lib/tasks/sample_data/taxon_factory.rb new file mode 100644 index 0000000000..912b426114 --- /dev/null +++ b/lib/tasks/sample_data/taxon_factory.rb @@ -0,0 +1,26 @@ +require "tasks/sample_data/logging" + +class TaxonFactory + include Logging + + def create_samples + log "Creating taxonomies:" + taxonomy = Spree::Taxonomy.find_or_create_by_name!('Products') + taxons = ['Vegetables', 'Fruit', 'Oils', 'Preserves and Sauces', 'Dairy', 'Meat and Fish'] + taxons.each do |taxon_name| + create_taxon(taxonomy, taxon_name) + end + end + + private + + def create_taxon(taxonomy, taxon_name) + return if Spree::Taxon.where(name: taxon_name).exists? + log "- #{taxon_name}" + Spree::Taxon.create!( + name: taxon_name, + parent_id: taxonomy.root.id, + taxonomy_id: taxonomy.id + ) + end +end diff --git a/lib/tasks/sample_data/user_factory.rb b/lib/tasks/sample_data/user_factory.rb new file mode 100644 index 0000000000..36aae48b37 --- /dev/null +++ b/lib/tasks/sample_data/user_factory.rb @@ -0,0 +1,40 @@ +require "tasks/sample_data/logging" + +class UserFactory + include Logging + + def create_samples + log "Creating users:" + usernames.map { |name| + create_user(name) + }.to_h + end + + private + + def usernames + [ + "Manel Super Admin", + "Penny Profile", + "Fred Farmer", + "Freddy Shop Farmer", + "Fredo Hub Farmer", + "Mary Retailer", + "Maryse Private", + "Jane Customer" + ] + end + + def create_user(name) + email = "#{name.downcase.tr(' ', '.')}@example.org" + password = Spree::User.friendly_token + log "- #{email}" + user = Spree::User.create_with( + password: password, + password_confirmation: password, + confirmation_sent_at: Time.zone.now, + confirmed_at: Time.zone.now + ).find_or_create_by_email!(email) + [name, user] + end +end From 1d0732732572e84d53bf71b9362b201265229692 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 8 Jan 2019 15:24:52 +1100 Subject: [PATCH 06/16] Add descriptions to enterprises --- lib/tasks/sample_data/enterprise_factory.rb | 37 +++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/tasks/sample_data/enterprise_factory.rb b/lib/tasks/sample_data/enterprise_factory.rb index 3bf18109a0..cf5d62db22 100644 --- a/lib/tasks/sample_data/enterprise_factory.rb +++ b/lib/tasks/sample_data/enterprise_factory.rb @@ -25,35 +25,55 @@ class EnterpriseFactory owner: users["Penny Profile"], is_primary_producer: false, sells: "none", - address: address("25 Myrtle Street, Bayswater, 3153") + address: address("25 Myrtle Street, Bayswater, 3153"), + long_description: < Date: Tue, 8 Jan 2019 15:39:53 +1100 Subject: [PATCH 07/16] Fix sample product creation and use bang methods Mistakes like the missing fee when creating product distributions were hidden, because I didn't use the bang methods to create records. --- lib/tasks/sample_data/addressing.rb | 2 +- lib/tasks/sample_data/inventory_factory.rb | 4 ++-- lib/tasks/sample_data/product_factory.rb | 17 ++++++++++------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/tasks/sample_data/addressing.rb b/lib/tasks/sample_data/addressing.rb index 1a50a8df95..cfdf23c505 100644 --- a/lib/tasks/sample_data/addressing.rb +++ b/lib/tasks/sample_data/addressing.rb @@ -15,7 +15,7 @@ module Addressing def zone zone = Spree::Zone.find_or_create_by_name!("Australia") - zone.members.create(zonable: country) + zone.members.create!(zonable: country) zone end diff --git a/lib/tasks/sample_data/inventory_factory.rb b/lib/tasks/sample_data/inventory_factory.rb index d37557f67f..b4147e024d 100644 --- a/lib/tasks/sample_data/inventory_factory.rb +++ b/lib/tasks/sample_data/inventory_factory.rb @@ -18,7 +18,7 @@ class InventoryFactory enterprise: shop, variant: product.variants.first, visible: true - ).find_or_create_by_variant_id(product.variants.first.id) + ).find_or_create_by_variant_id!(product.variants.first.id) create_override(shop, product) end @@ -29,6 +29,6 @@ class InventoryFactory price: 12, on_demand: false, count_on_hand: 5 - ).find_or_create_by_variant_id(product.variants.first.id) + ).find_or_create_by_variant_id!(product.variants.first.id) end end diff --git a/lib/tasks/sample_data/product_factory.rb b/lib/tasks/sample_data/product_factory.rb index b5b6544cb0..260ddb49cc 100644 --- a/lib/tasks/sample_data/product_factory.rb +++ b/lib/tasks/sample_data/product_factory.rb @@ -76,15 +76,18 @@ class ProductFactory unit_value: 1, on_demand: true ) - create_product_with_distribution(params) + create_product_with_distribution(params, hash[:supplier]) end - def create_product_with_distribution(params) - product = Spree::Product.create_with(params).find_or_create_by_name(params[:name]) - ProductDistribution.create( - product: product, - distributor: params[:distributor] - ) + def create_product_with_distribution(params, supplier) + product = Spree::Product.create_with(params).find_or_create_by_name!(params[:name]) + + distribution_params = { + distributor_id: params[:distributor].id, + enterprise_fee_id: supplier.enterprise_fees.first.id + } + ProductDistribution.create_with(distribution_params).find_or_create_by_product_id!(product.id) + product end end From c6393b60772917faad7df7bfb595b6351b55ff67 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 10 Jan 2019 12:26:48 +1100 Subject: [PATCH 08/16] Warn about deprecation via ActiveSupport --- lib/tasks/dev.rake | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index a1ead17f5c..5fbafaa5b4 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -316,15 +316,22 @@ namespace :ofn do if EnterpriseRole.count < 1 EnterpriseRole.create!(user: Spree::User.first, enterprise: enterprise2) end - puts < Date: Thu, 10 Jan 2019 13:46:38 +1100 Subject: [PATCH 09/16] Use new sample data during setup --- script/setup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/setup b/script/setup index 53332df685..08fc3fb677 100755 --- a/script/setup +++ b/script/setup @@ -52,7 +52,7 @@ printf '\n\n' | bundle exec rake db:setup db:test:prepare printf '\n' # Load some default data for your environment -bundle exec rake ofn:dev:load_sample_data +bundle exec rake ofn:sample_data printf '\n' printf "${YELLOW}WELCOME TO OPEN FOOD NETWORK!\n" From 659aa269de0f6198d4083d822b92fef5bcb1bc9f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 22 Jan 2019 17:00:29 +1100 Subject: [PATCH 10/16] Fix creation of sample payment methods on staging Due to an unknown reason my chosen way of creating calculator and assigning it to a payment method didn't work in staging environment even though it was working in development. Without diving deeper into the cause of this, I decided to change the record assignments and fix the problem that way. --- lib/tasks/sample_data/payment_method_factory.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tasks/sample_data/payment_method_factory.rb b/lib/tasks/sample_data/payment_method_factory.rb index 43fd43348f..43f0c49a4d 100644 --- a/lib/tasks/sample_data/payment_method_factory.rb +++ b/lib/tasks/sample_data/payment_method_factory.rb @@ -46,7 +46,7 @@ class PaymentMethodFactory description: description, distributor_ids: [enterprise.id] ) - card.calculator = calculator - card.save! + calculator.calculable = card + calculator.save! end end From cc46bfd0023a405a4493b7bb195cd8eb55238177 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 5 Feb 2019 16:30:46 +1100 Subject: [PATCH 11/16] Rename namespace to ofn --- lib/tasks/dev.rake | 2 +- lib/tasks/sample_data.rake | 2 +- lib/tasks/sample_data/logging.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 5fbafaa5b4..e14ef6a535 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -327,7 +327,7 @@ namespace :ofn do This task is going to be replaced by: - $ bundle exec rake openfoodnetwork:sample_data + $ bundle exec rake ofn:sample_data It contains more sample data. WARNING diff --git a/lib/tasks/sample_data.rake b/lib/tasks/sample_data.rake index bb41d2c7d6..bf929281b6 100644 --- a/lib/tasks/sample_data.rake +++ b/lib/tasks/sample_data.rake @@ -21,7 +21,7 @@ require "tasks/sample_data/user_factory" # - https://community.openfoodnetwork.org/t/seed-data-development-provisioning-deployment/910 # - https://github.com/openfoodfoundation/openfoodnetwork/issues/2072 # -namespace :openfoodnetwork do +namespace :ofn do desc 'load sample data for development or staging' task sample_data: :environment do raise "Please run `rake db:seed` first." unless seeded? diff --git a/lib/tasks/sample_data/logging.rb b/lib/tasks/sample_data/logging.rb index ed68d7e52e..4c8ca493b6 100644 --- a/lib/tasks/sample_data/logging.rb +++ b/lib/tasks/sample_data/logging.rb @@ -3,6 +3,6 @@ module Logging def log(message) @logger ||= ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) - @logger.tagged("openfoodnetwork:sample_data:load") { @logger.info(message) } + @logger.tagged("ofn:sample_data") { @logger.info(message) } end end From 6790e8895d5cca8c6e35e039801b32c7723e0b13 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 15 Feb 2019 16:44:55 +1100 Subject: [PATCH 12/16] Create sample payment methods with current environment --- lib/tasks/sample_data/payment_method_factory.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tasks/sample_data/payment_method_factory.rb b/lib/tasks/sample_data/payment_method_factory.rb index 43f0c49a4d..512e54437f 100644 --- a/lib/tasks/sample_data/payment_method_factory.rb +++ b/lib/tasks/sample_data/payment_method_factory.rb @@ -44,6 +44,7 @@ class PaymentMethodFactory card = enterprise.payment_methods.new( name: name, description: description, + environment: Rails.env, distributor_ids: [enterprise.id] ) calculator.calculable = card From d418ca1b1f01e20b39c054411b2249525ec0205b Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Tue, 26 Feb 2019 18:13:51 +0000 Subject: [PATCH 13/16] Add mandatory payment method provider to sample data payment methods --- lib/tasks/sample_data/payment_method_factory.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/tasks/sample_data/payment_method_factory.rb b/lib/tasks/sample_data/payment_method_factory.rb index 512e54437f..640faf2c9c 100644 --- a/lib/tasks/sample_data/payment_method_factory.rb +++ b/lib/tasks/sample_data/payment_method_factory.rb @@ -24,6 +24,7 @@ class PaymentMethodFactory def create_cash_method(enterprise) create_payment_method( + "Spree::PaymentMethod::Check", enterprise, "Cash on collection", "Pay on collection!", @@ -33,6 +34,7 @@ class PaymentMethodFactory def create_card_method(enterprise) create_payment_method( + "Spree::Gateway::Bogus", enterprise, "Credit card (fake)", "We charge 1%, but won't ask for your details. ;-)", @@ -40,14 +42,14 @@ class PaymentMethodFactory ) end - def create_payment_method(enterprise, name, description, calculator) - card = enterprise.payment_methods.new( + def create_payment_method(provider_class, enterprise, name, description, calculator) + payment_method = provider_class.constantize.new( name: name, description: description, environment: Rails.env, distributor_ids: [enterprise.id] ) - calculator.calculable = card + calculator.calculable = payment_method calculator.save! end end From ad5c37977149a3e6dbbd66f64ce16644f2cdc8d1 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Wed, 27 Feb 2019 09:46:18 +0000 Subject: [PATCH 14/16] Make sample data zone read from the environment variable CHECKOUT_ZONE --- lib/tasks/sample_data/addressing.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/sample_data/addressing.rb b/lib/tasks/sample_data/addressing.rb index cfdf23c505..ea6ec7b2bb 100644 --- a/lib/tasks/sample_data/addressing.rb +++ b/lib/tasks/sample_data/addressing.rb @@ -14,7 +14,7 @@ module Addressing end def zone - zone = Spree::Zone.find_or_create_by_name!("Australia") + zone = Spree::Zone.find_or_create_by_name!(ENV.fetch('CHECKOUT_ZONE')) zone.members.create!(zonable: country) zone end From 03d242c061ca91fa5cb127279e222ecfe40286aa Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Wed, 27 Feb 2019 23:19:39 +0000 Subject: [PATCH 15/16] Fix typo in sample_data zone creation (zonEable) and added clause to avoid re-adding the same country to the zone --- lib/tasks/sample_data/addressing.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/sample_data/addressing.rb b/lib/tasks/sample_data/addressing.rb index ea6ec7b2bb..bc1471e276 100644 --- a/lib/tasks/sample_data/addressing.rb +++ b/lib/tasks/sample_data/addressing.rb @@ -15,7 +15,7 @@ module Addressing def zone zone = Spree::Zone.find_or_create_by_name!(ENV.fetch('CHECKOUT_ZONE')) - zone.members.create!(zonable: country) + zone.members.create!(zoneable: country) unless zone.zoneables.include?(country) zone end From 9644fcf67cf3134c9777da2a303049d115c18bd3 Mon Sep 17 00:00:00 2001 From: luisramos0 Date: Wed, 6 Mar 2019 10:01:17 +0000 Subject: [PATCH 16/16] Improve payment method factory in sample data by removing constantize --- lib/tasks/sample_data/payment_method_factory.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/sample_data/payment_method_factory.rb b/lib/tasks/sample_data/payment_method_factory.rb index 640faf2c9c..2b3899a662 100644 --- a/lib/tasks/sample_data/payment_method_factory.rb +++ b/lib/tasks/sample_data/payment_method_factory.rb @@ -24,7 +24,7 @@ class PaymentMethodFactory def create_cash_method(enterprise) create_payment_method( - "Spree::PaymentMethod::Check", + Spree::PaymentMethod::Check, enterprise, "Cash on collection", "Pay on collection!", @@ -34,7 +34,7 @@ class PaymentMethodFactory def create_card_method(enterprise) create_payment_method( - "Spree::Gateway::Bogus", + Spree::Gateway::Bogus, enterprise, "Credit card (fake)", "We charge 1%, but won't ask for your details. ;-)", @@ -43,7 +43,7 @@ class PaymentMethodFactory end def create_payment_method(provider_class, enterprise, name, description, calculator) - payment_method = provider_class.constantize.new( + payment_method = provider_class.new( name: name, description: description, environment: Rails.env,