diff --git a/app/models/product_import/entry_validator.rb b/app/models/product_import/entry_validator.rb index 06c1311d3a..a9aa8193a9 100644 --- a/app/models/product_import/entry_validator.rb +++ b/app/models/product_import/entry_validator.rb @@ -39,6 +39,8 @@ module ProductImport enterprise_validation(entry) unit_fields_validation(entry) variant_of_product_validation(entry) + price_validation(entry) + on_hand_on_demand_validation(entry) next if entry.enterprise_id.blank? @@ -170,6 +172,11 @@ module ProductImport error: I18n.t('admin.product_import.model.blank')) end + unless is_numeric(entry.units) && entry.units.to_i > 0 + mark_as_invalid(entry, attribute: 'units', + error: I18n.t('admin.product_import.model.incorrect_value')) + end + return if import_into_inventory? # unit_type must be valid type @@ -189,6 +196,43 @@ module ProductImport error: I18n.t('admin.product_import.model.conditional_blank')) end + def is_numeric(value) + return true unless Float(value, exception: false).nil? + end + + def price_validation(entry) + return if is_numeric(entry.price) + + if empty_or_placeholder_value(entry.price) + mark_as_invalid(entry, attribute: 'price', + error: I18n.t('admin.product_import.model.blank')) + else + mark_as_invalid(entry, attribute: 'price', + error: I18n.t('admin.product_import.model.incorrect_value')) + end + end + + def on_hand_on_demand_validation(entry) + on_hand_present_numeric = !empty_or_placeholder_value(entry.on_hand) && + is_numeric(entry.on_hand) + on_hand_value = entry.on_hand&.to_i + on_demand_present_numeric = !empty_or_placeholder_value(entry.on_demand) && + is_numeric(entry.on_demand) + on_demand_value = entry.on_demand&.to_i + + return if (on_hand_present_numeric && on_hand_value >= 0) || + (on_demand_present_numeric && on_demand_value == 1) + + mark_as_invalid(entry, attribute: 'on_hand', + error: I18n.t('admin.product_import.model.incorrect_value')) + mark_as_invalid(entry, attribute: 'on_demand', + error: I18n.t('admin.product_import.model.incorrect_value')) + end + + def empty_or_placeholder_value(value) + !value&.present? || value.nil? || value.to_s.strip == '' || value.to_s.strip == "-" + end + def variant_of_product_validation(entry) return if entry.producer.blank? || entry.name.blank? @@ -287,8 +331,7 @@ module ProductImport entry.primary_taxon_id = @spreadsheet_data.categories_index[category_name] else mark_as_invalid(entry, attribute: "category", - error: I18n.t(:error_not_found_in_database, - name: category_name)) + error: I18n.t('admin.product_import.model.category_not_found')) end end diff --git a/spec/models/product_import/entry_validator_spec.rb b/spec/models/product_import/entry_validator_spec.rb index 44b9738706..5469c47364 100644 --- a/spec/models/product_import/entry_validator_spec.rb +++ b/spec/models/product_import/entry_validator_spec.rb @@ -37,7 +37,9 @@ describe ProductImport::EntryValidator do enterprise_id: enterprise.id, producer: enterprise, producer_id: enterprise.id, - distributor: enterprise + distributor: enterprise, + price: "1.0", + on_hand: "1" ) end @@ -51,7 +53,9 @@ describe ProductImport::EntryValidator do enterprise_id: enterprise.id, producer: enterprise, producer_id: enterprise.id, - distributor: enterprise + distributor: enterprise, + price: "1.0", + on_hand: "1" ) end @@ -99,7 +103,7 @@ describe ProductImport::EntryValidator do it "validates a product" do entries = [potato_variant] entry_validator.validate_all(entries) - expect(potato_variant.errors.count).to eq 1 + expect(potato_variant.errors.count).to eq 4 end end diff --git a/spec/models/product_importer_spec.rb b/spec/models/product_importer_spec.rb index 812181e3e1..908dd9e4ae 100644 --- a/spec/models/product_importer_spec.rb +++ b/spec/models/product_importer_spec.rb @@ -651,7 +651,7 @@ describe ProductImport::ProductImporter do let(:csv_data) { CSV.generate do |csv| csv << ["name", "display_name", "distributor", "producer", "on_hand", "price", "units"] - csv << ["Oats", "Porridge Oats", enterprise2.name, enterprise.name, "900", "", "500"] + csv << ["Oats", "Porridge Oats", enterprise2.name, enterprise.name, "900", "1.0", "500"] end } let(:importer) { import_data csv_data, import_into: 'inventories' } @@ -679,7 +679,7 @@ describe ProductImport::ProductImporter do CSV.generate do |csv| csv << ["name", "distributor", "producer", "on_hand", "price", "units", "variant_unit_name"] - csv << ["Cabbage", enterprise2.name, enterprise.name, "900", "", "1", "Whole"] + csv << ["Cabbage", enterprise2.name, enterprise.name, "900", "1.0", "1", "Whole"] end } let(:importer) { import_data csv_data, import_into: 'inventories' } diff --git a/spec/system/admin/product_import_spec.rb b/spec/system/admin/product_import_spec.rb index a91968d6d4..b41c81483c 100644 --- a/spec/system/admin/product_import_spec.rb +++ b/spec/system/admin/product_import_spec.rb @@ -376,7 +376,7 @@ describe "Product Import", js: true do csv << ["name", "distributor", "producer", "category", "on_hand", "price", "unit_type", "units", "on_demand"] csv << ["Beets", "Another Enterprise", "User Enterprise", "Vegetables", nil, "3.20", "kg", - "1", "true"] + "1", "1"] end File.write('/tmp/test.csv', csv_data) @@ -415,7 +415,7 @@ describe "Product Import", js: true do csv << ["name", "distributor", "producer", "category", "on_hand", "price", "unit_type", "units", "on_demand", "variant_unit_name"] csv << ["Aubergine", "Another Enterprise", "User Enterprise", "Vegetables", "", "3.3", - "kg", "1", "true", "Bag"] + "kg", "1", "1", "Bag"] end File.write('/tmp/test.csv', csv_data) @@ -445,9 +445,9 @@ describe "Product Import", js: true do csv << ["name", "distributor", "producer", "category", "on_hand", "price", "unit_type", "units", "on_demand", "variant_unit_name"] csv << ["Aubergine", "Another Enterprise", "User Enterprise", "Vegetables", "", "3.3", - "kg", "1", "true", "Bag"] + "kg", "1", "1", "Bag"] csv << ["Aubergine", "Another Enterprise", "User Enterprise", "Vegetables", "", "6.6", - "kg", "1", "true", "Big-Bag"] + "kg", "1", "1", "Big-Bag"] end File.write('/tmp/test.csv', csv_data) @@ -466,6 +466,66 @@ describe "Product Import", js: true do expect(page).not_to have_content "Aubergine" end + + it "invalidates units value if 0 or non-numeric" do + csv_data = CSV.generate do |csv| + csv << ["name", "distributor", "producer", "category", "on_hand", "price", "unit_type", + "units", "on_demand", "variant_unit_name"] + csv << ["Aubergine", "Another Enterprise", "User Enterprise", "Vegetables", "", "3.3", + "kg", "1", "1", "Bag"] + csv << ["Beans", "Another Enterprise", "User Enterprise", "Vegetables", "3", "3.0", + "kg", "0", "1", "Bag"] + csv << ["Cabbage", "Another Enterprise", "User Enterprise", "Vegetables", "1", "4.3", + "kg", "XX", "", "Bag"] + end + + File.write('/tmp/test.csv', csv_data) + visit main_app.admin_product_import_path + select 'Inventories', from: "settings_import_into" + attach_file 'file', '/tmp/test.csv' + click_button 'Upload' + proceed_to_validation + expect(page).to have_selector '.item-count', text: "3" + expect(page).to have_selector '.invalid-count', text: "2" + expect(page).to have_selector '.inv-create-count', text: '1' + + find('div.header-description', text: 'Items contain errors').click + expect(page).to have_content "line 4: Cabbage - Units incorrect value" + expect(page).to have_content "line 3: Beans - Units incorrect value" + expect(page).to have_content "Imported file contains invalid entries" + expect(page).to have_no_selector 'input[type=submit][value="Save"]' + expect(page).not_to have_content "line 2: Aubergine" + end + + it "Price validation" do + csv_data = CSV.generate do |csv| + csv << ["name", "distributor", "producer", "category", "on_hand", "price", "unit_type", + "units", "on_demand", "variant_unit_name"] + csv << ["Aubergine", "Another Enterprise", "User Enterprise", "Vegetables", "", "3.3", + "kg", "1", "1", "Bag"] + csv << ["Beans", "Another Enterprise", "User Enterprise", "Vegetables", "3", "", + "kg", "2", "1", "Bag"] + csv << ["Cabbage", "Another Enterprise", "User Enterprise", "Vegetables", "1", "t6", + "kg", "3", "", "Bag"] + end + + File.write('/tmp/test.csv', csv_data) + visit main_app.admin_product_import_path + select 'Inventories', from: "settings_import_into" + attach_file 'file', '/tmp/test.csv' + click_button 'Upload' + proceed_to_validation + expect(page).to have_selector '.item-count', text: "3" + expect(page).to have_selector '.invalid-count', text: "2" + expect(page).to have_selector '.inv-create-count', text: '1' + + find('div.header-description', text: 'Items contain errors').click + expect(page).to have_content "line 4: Cabbage - Price incorrect value" + expect(page).to have_content "line 3: Beans - Price can't be blank" + expect(page).to have_content "Imported file contains invalid entries" + expect(page).to have_no_selector 'input[type=submit][value="Save"]' + expect(page).not_to have_content "line 2: Aubergine" + end end it "handles on_demand and on_hand validations with inventory" do @@ -473,11 +533,13 @@ describe "Product Import", js: true do csv << ["name", "distributor", "producer", "category", "on_hand", "price", "units", "on_demand"] csv << ["Beans", "Another Enterprise", "User Enterprise", "Vegetables", nil, "3.20", "500", - "true"] + "1"] csv << ["Sprouts", "Another Enterprise", "User Enterprise", "Vegetables", "6", "6.50", - "500", "false"] - csv << ["Cabbage", "Another Enterprise", "User Enterprise", "Vegetables", nil, "1.50", + "500", "0"] + csv << ["Cabbage", "Another Enterprise", "User Enterprise", "Vegetables", "", "1.50", "500", nil] + csv << ["Aubergine", "Another Enterprise", "User Enterprise", "Vegetables", nil, "1.50", + "500", "0"] end File.write('/tmp/test.csv', csv_data) @@ -488,34 +550,17 @@ describe "Product Import", js: true do proceed_to_validation - expect(page).to have_selector '.item-count', text: "3" - expect(page).to have_no_selector '.invalid-count' + expect(page).to have_selector '.item-count', text: "4" expect(page).to have_selector '.inv-create-count', text: '2' - expect(page).to have_selector '.inv-update-count', text: '1' + expect(page).to have_selector '.invalid-count', text: "2" - save_data - - expect(page).to have_selector '.inv-created-count', text: '2' - expect(page).to have_selector '.inv-updated-count', text: '1' - - beans_override = VariantOverride.where(variant_id: product2.variants.first.id, - hub_id: enterprise2.id).first - sprouts_override = VariantOverride.where(variant_id: product3.variants.first.id, - hub_id: enterprise2.id).first - cabbage_override = VariantOverride.where(variant_id: product4.variants.first.id, - hub_id: enterprise2.id).first - - expect(Float(beans_override.price)).to eq 3.20 - expect(beans_override.count_on_hand).to be_nil - expect(beans_override.on_demand).to be_truthy - - expect(Float(sprouts_override.price)).to eq 6.50 - expect(sprouts_override.count_on_hand).to eq 6 - expect(sprouts_override.on_demand).to eq false - - expect(Float(cabbage_override.price)).to eq 1.50 - expect(cabbage_override.count_on_hand).to be_nil - expect(cabbage_override.on_demand).to be_nil + find('div.header-description', text: 'Items contain errors').click + expect(page).to have_content "line 4: Cabbage - On_hand incorrect value - On_demand incorrect value" + expect(page).to have_content "line 5: Aubergine - On_hand incorrect value - On_demand incorrect value" + expect(page).to have_content "Imported file contains invalid entries" + expect(page).to have_no_selector 'input[type=submit][value="Save"]' + expect(page).not_to have_content "line 2: Beans" + expect(page).not_to have_content "line 3: Sprouts" end it "imports lines with all allowed units" do