include foodsoft-article-import

use filetypes for manual uploading bnn, odin, foodsoft file

use opts in .parse

adapt specs to include file format

add specs for odin, bnn, foodsoft files

adapt localize input to remove ',' separator and replace with '.'

remove depr foodsoftfile.rb and spreadsheet.rb

remove todo
This commit is contained in:
viehlieb 2023-01-31 15:25:36 +01:00 committed by Philipp Rothmann
parent 936c1ba878
commit 4b5775e107
15 changed files with 492 additions and 120 deletions

View file

@ -49,6 +49,7 @@ gem 'attribute_normalizer'
gem 'ice_cube' gem 'ice_cube'
# At time of development 01-06-2022 mmddyyyy necessary fix for config_helper.rb form builder was not in rubygems so we pull from github, see: https://github.com/gregschmit/recurring_select/pull/152 # At time of development 01-06-2022 mmddyyyy necessary fix for config_helper.rb form builder was not in rubygems so we pull from github, see: https://github.com/gregschmit/recurring_select/pull/152
gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select' gem 'recurring_select', git: 'https://github.com/gregschmit/recurring_select'
gem 'foodsoft_article_import', git: 'https://git.local-it.org/Foodsoft/foodsoft_article_import', tag: 'v1.0'
gem 'roo' gem 'roo'
gem 'roo-xls' gem 'roo-xls'
gem 'spreadsheet' gem 'spreadsheet'

View file

@ -1,3 +1,11 @@
GIT
remote: https://git.local-it.org/Foodsoft/foodsoft_article_import
revision: 49a0c1ddb3bb67a357c692c63af0cda2db7c45b0
tag: v1.0
specs:
foodsoft_article_import (1.0.0)
roo (~> 2.9.0)
GIT GIT
remote: https://github.com/gregschmit/recurring_select remote: https://github.com/gregschmit/recurring_select
revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf revision: 29febc4c4abdd6c30636c33a7d2daecb09973ecf
@ -623,6 +631,7 @@ DEPENDENCIES
exception_notification exception_notification
factory_bot_rails factory_bot_rails
faker faker
foodsoft_article_import!
foodsoft_discourse! foodsoft_discourse!
foodsoft_documents! foodsoft_documents!
foodsoft_links! foodsoft_links!

View file

@ -148,11 +148,12 @@ class ArticlesController < ApplicationController
# Update articles from a spreadsheet # Update articles from a spreadsheet
def parse_upload def parse_upload
uploaded_file = params[:articles]['file'] or raise I18n.t('articles.controller.parse_upload.no_file') uploaded_file = params[:articles]['file'] or raise I18n.t('articles.controller.parse_upload.no_file')
type = params[:articles]['type']
options = { filename: uploaded_file.original_filename } options = { filename: uploaded_file.original_filename }
options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1') options[:outlist_absent] = (params[:articles]['outlist_absent'] == '1')
options[:convert_units] = (params[:articles]['convert_units'] == '1') options[:convert_units] = (params[:articles]['convert_units'] == '1')
options[:update_category] = (params[:articles]['update_category'] == '1') options[:update_category] = (params[:articles]['update_category'] == '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, type, options
if @updated_article_pairs.empty? && @outlisted_articles.empty? && @new_articles.empty? 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 end

View file

@ -1,25 +0,0 @@
# Foodsoft-file import
class FoodsoftFile
# parses a string from a foodsoft-file
# returns two arrays with articles and outlisted_articles
# the parsed article is a simple hash
def self.parse(file, options = {})
SpreadsheetFile.parse file, options do |row, row_index|
next if row[2].blank?
article = { :order_number => row[1],
:name => row[2],
:note => row[3],
:manufacturer => row[4],
:origin => row[5],
:unit => row[6],
:price => row[7],
:tax => row[8],
:deposit => (row[9].nil? ? "0" : row[9]),
:unit_quantity => row[10],
:article_category => row[13] }
status = row[0] && row[0].strip.downcase == 'x' ? :outlisted : nil
yield status, article, row_index
end
end
end

View file

@ -1,22 +0,0 @@
require 'roo'
class SpreadsheetFile
def self.parse(file, options = {})
filepath = file.is_a?(String) ? file : file.to_path
filename = options.delete(:filename) || filepath
fileext = File.extname(filename)
options[:csv_options] = { col_sep: ';', encoding: 'utf-8' }.merge(options[:csv_options] || {})
s = Roo::Spreadsheet.open(filepath, options.merge({ extension: fileext }))
row_index = 1
s.each do |row|
if row_index == 1
# @todo try to detect headers; for now using the index is ok
else
yield row, row_index
end
row_index += 1
end
row_index
end
end

View file

@ -8,7 +8,7 @@ module LocalizeInput
separator = I18n.t("separator", scope: "number.format") separator = I18n.t("separator", scope: "number.format")
delimiter = I18n.t("delimiter", scope: "number.format") delimiter = I18n.t("delimiter", scope: "number.format")
input.gsub!(delimiter, "") if input.match(/\d+#{Regexp.escape(delimiter)}+\d+#{Regexp.escape(separator)}+\d+/) # Remove delimiter input.gsub!(delimiter, "") if input.match(/\d+#{Regexp.escape(delimiter)}+\d+#{Regexp.escape(separator)}+\d+/) # Remove delimiter
input.gsub!(separator, ".") # Replace separator with db compatible character input.gsub!(separator, ".") or input.gsub!(",", ".") # Replace separator with db compatible character
input input
rescue rescue
Rails.logger.warn "Can't localize input: #{input}" Rails.logger.warn "Can't localize input: #{input}"

View file

@ -1,3 +1,4 @@
require 'foodsoft_article_import'
class Supplier < ApplicationRecord class Supplier < ApplicationRecord
include MarkAsDeletedWithName include MarkAsDeletedWithName
include CustomFields include CustomFields
@ -73,13 +74,16 @@ class Supplier < ApplicationRecord
# Synchronise articles with spreadsheet. # Synchronise articles with spreadsheet.
# #
# @param file [File] Spreadsheet file to parse # @param file [File] Spreadsheet file to parse
# @param options [Hash] Options passed to {FoodsoftFile#parse} except when listed here. # @param options [Hash] Options passed to {FoodsoftArticleImport#parse} except when listed here.
# @option options [Boolean] :outlist_absent Set to +true+ to remove articles not in spreadsheet. # @option options [Boolean] :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. # @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 = {}) def sync_from_file(file, type, options = {})
all_order_numbers = [] all_order_numbers = []
updated_article_pairs, outlisted_articles, new_articles = [], [], [] updated_article_pairs, outlisted_articles, new_articles = [], [], []
FoodsoftFile::parse file, options do |status, new_attrs, line| custom_codes_path = File.join(Rails.root, "config", "custom_codes.yml")
opts = options.except(:convert_units, :outlist_absent)
custom_codes_file_path = custom_codes_path if File.exist?(custom_codes_path)
FoodsoftArticleImport.parse(file, custom_file_path: custom_codes_file_path, type: type, **opts) do |new_attrs, status, line|
article = articles.undeleted.where(order_number: new_attrs[:order_number]).first article = articles.undeleted.where(order_number: new_attrs[:order_number]).first
if new_attrs[:article_category].present? && options[:update_category] if new_attrs[:article_category].present? && options[:update_category]

View file

@ -71,9 +71,14 @@
= form_for :articles, :url => parse_upload_supplier_articles_path(@supplier), = form_for :articles, :url => parse_upload_supplier_articles_path(@supplier),
:html => { multipart: true, class: "form-horizontal" } do |f| :html => { multipart: true, class: "form-horizontal" } do |f|
.control-group .control-group
%label(for="articles_file")= t '.file_label' %label(for="articles_file")
%strong= t '.file_label'
= f.file_field "file" = f.file_field "file"
%label(for="articles_file")
%strong="select the file type you are about to upload"
=f.collection_select :type, ["bnn","foodsoft","odin"], :to_s, :to_s
.control-group .control-group
%label(for="articles_update_category") %label(for="articles_update_category")

View file

@ -187,8 +187,8 @@ describe ArticlesController, type: :controller do
describe '#parse_upload' do describe '#parse_upload' do
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/files/upload_test.csv'), original_filename: 'upload_test.csv') } let(:file) { Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/files/upload_test.csv'), original_filename: 'upload_test.csv') }
it 'updates particles from spreadsheet' do it 'updates articles from spreadsheet' do
post_with_supplier :parse_upload, params: { articles: { file: file, outlist_absent: '1', convert_units: '1' } } post_with_supplier :parse_upload, params: { articles: { file: file, outlist_absent: '1', convert_units: '1', type: 'foodsoft' } }
expect(response).to have_http_status(:success) expect(response).to have_http_status(:success)
end end

5
spec/fixtures/bnn_file_01.bnn vendored Normal file
View file

@ -0,0 +1,5 @@
BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1
29932;;;;4280001958081;4280001958203;Walnoten (ongeroosterd);bio;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;1 kg;1;N;930190;99260;;1,41;;;;1;;;4,49;2,34;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;;
28391;;;;4280001958081;4280001958203;Pijnboompitten;dem;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;100 g;10;N;930190;99260;;1,41;;;;1;;;5,56;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;Kg;28,571;;
1829;;;;4280001958081;4280001958203;Appelsap (verpakt);;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;4x250 ml;10;4x250 ml;10;N;930190;99260;;3,21;;;;1;;;4,49;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;ml;28,571;;
177813;;;;4280001958081;4280001958203;Tomaten;bio;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;1;500 g;20;N;930190;99260;;1,20;;;;1;;;4,49;2.89;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;g;28,571;;

2
spec/fixtures/bnn_file_02.bnn vendored Normal file
View file

@ -0,0 +1,2 @@
BNN;3;0;Naturkost Nord, Hamburg;T;Angebot Nr. 0922;EUR;20220905;20221001;20220825;837;1
1;;;;4280001958081;4280001958203;Tomatoes;organic;;;med;;GR;C%;DE-?KO-001;120;1302;10;55;;1;;20;500 g;1;N;930190;99260;;1,41;;;;1;;;4,49;1,20;J;;2;3;;;;;;;;;;;;;;;;;;;A;;;;;g;28,571;;

273
spec/fixtures/odin_file_01.xml vendored Normal file
View file

@ -0,0 +1,273 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xmlproduct>
<leverancierkop>
<leveranciersnummer>1039</leveranciersnummer>
<versienummer>1.08</versienummer>
<naam>Estafette Associatie C.V.</naam>
<plaats>Geldermalsen</plaats>
</leverancierkop>
<product>
<eancode>8719325207668</eancode>
<omschrijving>Walnoten (ongeroosterd)</omschrijving>
<kassaomschrijving>Nucli rose</kassaomschrijving>
<plucode></plucode>
<weegschaalartikel>0</weegschaalartikel>
<wichtartikel>0</wichtartikel>
<pluartikel>0</pluartikel>
<inhoud>1</inhoud>
<eenheid>kg</eenheid>
<verpakkingce>Stuk</verpakkingce>
<statiegeld>0</statiegeld>
<merk>Het warme woud</merk>
<kwaliteit>bio</kwaliteit>
<keurmerkbio></keurmerkbio>
<keurmerkoverig></keurmerkoverig>
<herkomst>NL</herkomst>
<herkomstregio></herkomstregio>
<btw>6</btw>
<cblcode>1017515</cblcode>
<bestelnummer>29932</bestelnummer>
<sve>10</sve>
<status>Actief</status>
<ingredienten>druiven*</ingredienten>
<d204>0</d204>
<d209>0</d209>
<d210>2</d210>
<d212>2</d212>
<d213>0</d213>
<d214>0</d214>
<d234>0</d234>
<d215>2</d215>
<d239>2</d239>
<d216>0</d216>
<d217>2</d217>
<d220>0</d220>
<d221>2</d221>
<d223>0</d223>
<d236>2</d236>
<d235>2</d235>
<d238>2</d238>
<d225>2</d225>
<d228>1</d228>
<d232>0</d232>
<d237>2</d237>
<d240>0</d240>
<d241>2</d241>
<d242>2</d242>
<lengteverpakking></lengteverpakking>
<breedteverpakking></breedteverpakking>
<hoogteverpakking></hoogteverpakking>
<proefdiervrij>0</proefdiervrij>
<vegetarisch>0</vegetarisch>
<veganistisch>0</veganistisch>
<rauwemelk>0</rauwemelk>
<bewaartemperatuur>1</bewaartemperatuur>
<gebruikstips></gebruikstips>
<soortleverancier>2</soortleverancier>
<geriefartikel>0</geriefartikel>
<prijs>
<prijslijn>adviesprijs</prijslijn>
<ingangsdatum>2022-08-18</ingangsdatum>
<inkoopprijs>2.34</inkoopprijs>
<consumentenprijs>7.95</consumentenprijs>
</prijs>
</product>
<product>
<eancode>8719325207668</eancode>
<omschrijving>Pijnboompitten</omschrijving>
<kassaomschrijving>Nucli rose</kassaomschrijving>
<plucode></plucode>
<weegschaalartikel>0</weegschaalartikel>
<wichtartikel>0</wichtartikel>
<pluartikel>0</pluartikel>
<inhoud>100</inhoud>
<eenheid>g</eenheid>
<verpakkingce>Stuk</verpakkingce>
<statiegeld>0</statiegeld>
<merk>NELEMAN</merk>
<kwaliteit>dem</kwaliteit>
<keurmerkbio></keurmerkbio>
<keurmerkoverig></keurmerkoverig>
<herkomst>TR</herkomst>
<herkomstregio></herkomstregio>
<btw>6</btw>
<cblcode>1017515</cblcode>
<bestelnummer>28391</bestelnummer>
<sve>10</sve>
<status>Actief</status>
<ingredienten>druiven*</ingredienten>
<d204>0</d204>
<d209>0</d209>
<d210>2</d210>
<d212>2</d212>
<d213>0</d213>
<d214>0</d214>
<d234>0</d234>
<d215>2</d215>
<d239>2</d239>
<d216>0</d216>
<d217>2</d217>
<d220>0</d220>
<d221>2</d221>
<d223>0</d223>
<d236>2</d236>
<d235>2</d235>
<d238>2</d238>
<d225>2</d225>
<d228>1</d228>
<d232>0</d232>
<d237>2</d237>
<d240>0</d240>
<d241>2</d241>
<d242>2</d242>
<lengteverpakking></lengteverpakking>
<breedteverpakking></breedteverpakking>
<hoogteverpakking></hoogteverpakking>
<proefdiervrij>0</proefdiervrij>
<vegetarisch>0</vegetarisch>
<veganistisch>0</veganistisch>
<rauwemelk>0</rauwemelk>
<bewaartemperatuur>1</bewaartemperatuur>
<gebruikstips></gebruikstips>
<soortleverancier>2</soortleverancier>
<geriefartikel>0</geriefartikel>
<prijs>
<prijslijn>adviesprijs</prijslijn>
<ingangsdatum>2022-08-18</ingangsdatum>
<inkoopprijs>5.56</inkoopprijs>
<consumentenprijs>7.95</consumentenprijs>
</prijs>
</product>
<product>
<eancode>8719325207668</eancode>
<omschrijving>Appelsap (verpakt)</omschrijving>
<kassaomschrijving>Nucli rose</kassaomschrijving>
<plucode></plucode>
<weegschaalartikel>0</weegschaalartikel>
<wichtartikel>0</wichtartikel>
<pluartikel>0</pluartikel>
<inhoud>4x250</inhoud>
<eenheid>ml</eenheid>
<verpakkingce>Stuk</verpakkingce>
<statiegeld>0.4</statiegeld>
<merk>Appelgaarde</merk>
<kwaliteit></kwaliteit>
<keurmerkbio></keurmerkbio>
<keurmerkoverig></keurmerkoverig>
<herkomst>DE</herkomst>
<herkomstregio></herkomstregio>
<btw>6</btw>
<cblcode>1017515</cblcode>
<bestelnummer>1829</bestelnummer>
<sve>10</sve>
<status>Actief</status>
<ingredienten>druiven*</ingredienten>
<d204>0</d204>
<d209>0</d209>
<d210>2</d210>
<d212>2</d212>
<d213>0</d213>
<d214>0</d214>
<d234>0</d234>
<d215>2</d215>
<d239>2</d239>
<d216>0</d216>
<d217>2</d217>
<d220>0</d220>
<d221>2</d221>
<d223>0</d223>
<d236>2</d236>
<d235>2</d235>
<d238>2</d238>
<d225>2</d225>
<d228>1</d228>
<d232>0</d232>
<d237>2</d237>
<d240>0</d240>
<d241>2</d241>
<d242>2</d242>
<lengteverpakking></lengteverpakking>
<breedteverpakking></breedteverpakking>
<hoogteverpakking></hoogteverpakking>
<proefdiervrij>0</proefdiervrij>
<vegetarisch>0</vegetarisch>
<veganistisch>0</veganistisch>
<rauwemelk>0</rauwemelk>
<bewaartemperatuur>1</bewaartemperatuur>
<gebruikstips></gebruikstips>
<soortleverancier>2</soortleverancier>
<geriefartikel>0</geriefartikel>
<prijs>
<prijslijn>adviesprijs</prijslijn>
<ingangsdatum>2022-08-18</ingangsdatum>
<inkoopprijs>3.21</inkoopprijs>
<consumentenprijs>7.95</consumentenprijs>
</prijs>
</product>
<product>
<eancode>8719325207668</eancode>
<omschrijving>Tomaten</omschrijving>
<kassaomschrijving>Nucli rose</kassaomschrijving>
<plucode></plucode>
<weegschaalartikel>0</weegschaalartikel>
<wichtartikel>0</wichtartikel>
<pluartikel>0</pluartikel>
<inhoud>500</inhoud>
<eenheid>g</eenheid>
<verpakkingce>Stuk</verpakkingce>
<statiegeld>0</statiegeld>
<merk>De röde hof</merk>
<kwaliteit>bio</kwaliteit>
<keurmerkbio></keurmerkbio>
<keurmerkoverig></keurmerkoverig>
<herkomst>DE</herkomst>
<herkomstregio></herkomstregio>
<btw>6</btw>
<cblcode>1017515</cblcode>
<bestelnummer>177813</bestelnummer>
<sve>20</sve>
<status>Actief</status>
<ingredienten>druiven*</ingredienten>
<d204>0</d204>
<d209>0</d209>
<d210>2</d210>
<d212>2</d212>
<d213>0</d213>
<d214>0</d214>
<d234>0</d234>
<d215>2</d215>
<d239>2</d239>
<d216>0</d216>
<d217>2</d217>
<d220>0</d220>
<d221>2</d221>
<d223>0</d223>
<d236>2</d236>
<d235>2</d235>
<d238>2</d238>
<d225>2</d225>
<d228>1</d228>
<d232>0</d232>
<d237>2</d237>
<d240>0</d240>
<d241>2</d241>
<d242>2</d242>
<lengteverpakking></lengteverpakking>
<breedteverpakking></breedteverpakking>
<hoogteverpakking></hoogteverpakking>
<proefdiervrij>0</proefdiervrij>
<vegetarisch>0</vegetarisch>
<veganistisch>0</veganistisch>
<rauwemelk>0</rauwemelk>
<bewaartemperatuur>1</bewaartemperatuur>
<gebruikstips></gebruikstips>
<soortleverancier>2</soortleverancier>
<geriefartikel>0</geriefartikel>
<prijs>
<prijslijn>adviesprijs</prijslijn>
<ingangsdatum>2022-08-18</ingangsdatum>
<inkoopprijs>1.2</inkoopprijs>
<consumentenprijs>7.95</consumentenprijs>
</prijs>
</product>
</xmlproduct>

75
spec/fixtures/odin_file_02.xml vendored Normal file
View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xmlproduct>
<leverancierkop>
<leveranciersnummer>1039</leveranciersnummer>
<versienummer>1.08</versienummer>
<naam>Estafette Associatie C.V.</naam>
<plaats>Geldermalsen</plaats>
</leverancierkop>
<product>
<eancode>8719325207668</eancode>
<omschrijving>Tomatoes</omschrijving>
<kassaomschrijving>Nucli rose</kassaomschrijving>
<plucode></plucode>
<weegschaalartikel>0</weegschaalartikel>
<wichtartikel>0</wichtartikel>
<pluartikel>0</pluartikel>
<inhoud>500</inhoud>
<eenheid>g</eenheid>
<verpakkingce>Stuk</verpakkingce>
<statiegeld>0</statiegeld>
<merk>De röde hof</merk>
<kwaliteit>organic</kwaliteit>
<keurmerkbio></keurmerkbio>
<keurmerkoverig></keurmerkoverig>
<herkomst>Somewhere, UK</herkomst>
<herkomstregio></herkomstregio>
<btw>6</btw>
<cblcode>1017515</cblcode>
<bestelnummer>1</bestelnummer>
<sve>20</sve>
<status>Actief</status>
<ingredienten>druiven*</ingredienten>
<d204>0</d204>
<d209>0</d209>
<d210>2</d210>
<d212>2</d212>
<d213>0</d213>
<d214>0</d214>
<d234>0</d234>
<d215>2</d215>
<d239>2</d239>
<d216>0</d216>
<d217>2</d217>
<d220>0</d220>
<d221>2</d221>
<d223>0</d223>
<d236>2</d236>
<d235>2</d235>
<d238>2</d238>
<d225>2</d225>
<d228>1</d228>
<d232>0</d232>
<d237>2</d237>
<d240>0</d240>
<d241>2</d241>
<d242>2</d242>
<lengteverpakking></lengteverpakking>
<breedteverpakking></breedteverpakking>
<hoogteverpakking></hoogteverpakking>
<proefdiervrij>0</proefdiervrij>
<vegetarisch>0</vegetarisch>
<veganistisch>0</veganistisch>
<rauwemelk>0</rauwemelk>
<bewaartemperatuur>1</bewaartemperatuur>
<gebruikstips></gebruikstips>
<soortleverancier>2</soortleverancier>
<geriefartikel>0</geriefartikel>
<prijs>
<prijslijn>adviesprijs</prijslijn>
<ingangsdatum>2022-08-18</ingangsdatum>
<inkoopprijs>1.2</inkoopprijs>
<consumentenprijs>7.95</consumentenprijs>
</prijs>
</product>
</xmlproduct>

View file

@ -1,9 +1,9 @@
require_relative '../spec_helper' require_relative '../spec_helper'
feature ArticlesController do feature ArticlesController do
let(:user) { create :user, groups: [create(:workgroup, role_article_meta: true)] } let(:user) { create(:user, groups: [create(:workgroup, role_article_meta: true)]) }
let(:supplier) { create :supplier } let(:supplier) { create(:supplier) }
let!(:article_category) { create :article_category } let!(:article_category) { create(:article_category) }
before { login user } before { login user }
@ -18,7 +18,7 @@ feature ArticlesController do
it 'can create a new article' do it 'can create a new article' do
click_on I18n.t('articles.index.new') click_on I18n.t('articles.index.new')
expect(page).to have_selector('form#new_article') expect(page).to have_selector('form#new_article')
article = build :article, supplier: supplier, article_category: article_category article = build(:article, supplier: supplier, article_category: article_category)
within('#new_article') do within('#new_article') do
fill_in 'article_name', :with => article.name fill_in 'article_name', :with => article.name
fill_in 'article_unit', :with => article.unit fill_in 'article_unit', :with => article.unit
@ -49,6 +49,7 @@ feature ArticlesController do
let(:file) { Rails.root.join(test_file) } let(:file) { Rails.root.join(test_file) }
it do it do
find("#articles_type option[value='foodsoft']").select_option
find('input[type="submit"]').click find('input[type="submit"]').click
expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio ◎" expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio ◎"
expect(find("tr:nth-child(2) #new_articles__name").value).to eq "Pijnboompitten" expect(find("tr:nth-child(2) #new_articles__name").value).to eq "Pijnboompitten"
@ -64,81 +65,124 @@ feature ArticlesController do
end end
end end
describe "can update existing article" do Dir.glob('spec/fixtures/bnn_file_01.*') do |test_file|
let!(:article) { create :article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g' } describe "can import articles from #{test_file}" do
let(:file) { Rails.root.join(test_file) }
it do it do
find('input[type="submit"]').click find("#articles_type option[value='bnn']").select_option
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes' find('input[type="submit"]').click
find('input[type="submit"]').click expect(find("tr:nth-child(1) #new_articles__note").value).to eq "bio"
article.reload expect(find("tr:nth-child(1) #new_articles__name").value).to eq "Walnoten (ongeroosterd)"
expect(article.name).to eq 'Tomatoes' # set article category
expect([article.unit, article.unit_quantity, article.price]).to eq ['500 g', 20, 1.2] 4.times do |i|
all("tr:nth-child(#{i + 1}) select > option")[1].select_option
end
find('input[type="submit"]').click
expect(page).to have_content("Pijnboompitten")
expect(supplier.articles.count).to eq 4
end
end end
end end
end
describe "handles missing data" do describe "updates" do
it do file_paths = ['spec/fixtures/foodsoft_file_02.csv', 'spec/fixtures/bnn_file_02.bnn', 'spec/fixtures/odin_file_02.xml']
find('input[type="submit"]').click # to overview let(:filename) { 'foodsoft_file_02.csv' }
find('input[type="submit"]').click # missing category, re-show form let(:file) { Rails.root.join("spec/fixtures/#{filename}") }
expect(find('tr.alert')).to be_present let(:val) { 'foodsoft' }
expect(supplier.articles.count).to eq 0 let(:type) { %w[foodsoft bnn odin] }
all("tr select > option")[1].select_option before do
find('input[type="submit"]').click # now it should succeed visit upload_supplier_articles_path(supplier_id: supplier.id)
expect(supplier.articles.count).to eq 1 attach_file 'articles_file', file
end find("#articles_type option[value='#{val}']").select_option
end end
describe "takes over category from file" do file_paths.each_with_index do |test_file, index|
it do describe "updates article for #{test_file}" do
find(:css, '#articles_update_category[value="1"]').set(true) # check take over category from file let(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g') }
expect(ArticleCategory.count).to eq 1 # new Category vegetables should be created from file let(:file) { Rails.root.join(test_file) }
find('input[type="submit"]').click # upload file let(:val) { type[index] }
find('input[type="submit"]').click # submit changes
expect(ArticleCategory.count).to eq 2 # it is it do
expect(supplier.articles.count).to eq 1 article.reload
expect(supplier.articles.first.article_category.name).to eq "Vegetables" find('input[type="submit"]').click
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes'
find('input[type="submit"]').click
article.reload
expect(article.name).to eq 'Tomatoes'
if type[index] == "odin"
expect([article.unit, article.unit_quantity, article.price]).to eq ['500gr', 20, 1.20]
else
expect([article.unit, article.unit_quantity, article.price]).to eq ['500 g', 20, 1.20]
end
end
it "handles missing data" do
find('input[type="submit"]').click # to overview
find('input[type="submit"]').click # missing category, re-show form
expect(find('tr.alert')).to be_present
expect(supplier.articles.count).to eq 0
all("tr select > option")[1].select_option
find('input[type="submit"]').click # now it should succeed
expect(supplier.articles.count).to eq 1
end
end end
end
describe "overwrites article_category from file" do describe "takes over category from file" do
let!(:article_category) { create(:article_category, name: "Fruit") } it do
let(:article) { create(:article, supplier: supplier, name: 'Tomatoes', order_number: 1, unit: '250 g', article_category: article_category) } find(:css, '#articles_update_category[value="1"]').set(true) # check take over category from file
expect(ArticleCategory.count).to eq 1 # new Category vegetables should be created from file
it do find('input[type="submit"]').click # upload file
find(:css, '#articles_update_category[value="1"]').set(true) # check take over category from file find('input[type="submit"]').click # submit changes
find('input[type="submit"]').click #upload file expect(ArticleCategory.count).to eq 2 # it is
find('input[type="submit"]').click #submit changes expect(supplier.articles.count).to eq 1
expect(supplier.articles.count).to eq 1 expect(supplier.articles.first.article_category.name).to eq "Vegetables"
expect(supplier.articles.first.article_category.name).to eq "Vegetables" end
end end
end
describe "can remove an existing article" do describe "overwrites article_category from file" do
let!(:article) { create :article, supplier: supplier, name: 'Foobar', order_number: 99999 } let!(:article_category) { create(:article_category, name: "Fruit") }
let(:article) { create(:article, supplier: supplier, name: 'Tomatoes', order_number: 1, unit: '250 g', article_category: article_category) }
it do it do
check('articles_outlist_absent') find(:css, '#articles_update_category[value="1"]').set(true) # check take over category from file
find('input[type="submit"]').click find('input[type="submit"]').click #upload file
expect(find("#outlisted_articles_#{article.id}", visible: :all)).to be_present find('input[type="submit"]').click #submit changes
expect(supplier.articles.count).to eq 1
all("tr select > option")[1].select_option expect(supplier.articles.first.article_category.name).to eq "Vegetables"
find('input[type="submit"]').click end
expect(article.reload.deleted?).to be true
end end
end
describe "can convert units when updating" do describe "can remove an existing article" do
let!(:article) { create :article, supplier: supplier, order_number: 1, unit: '250 g' } let!(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 99999) }
it do it do
check('articles_convert_units') check('articles_outlist_absent')
find('input[type="submit"]').click find('input[type="submit"]').click
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes' expect(find("#outlisted_articles_#{article.id}", visible: :all)).to be_present
find('input[type="submit"]').click
article.reload all("tr select > option")[1].select_option
expect([article.unit, article.unit_quantity, article.price]).to eq ['250 g', 40, 0.6] find('input[type="submit"]').click
expect(article.reload.deleted?).to be true
end
end
describe "can convert units when updating" do
let!(:article) { create(:article, supplier: supplier, order_number: 1, unit: '250 g') }
it do
check('articles_convert_units')
find('input[type="submit"]').click
expect(find("#articles_#{article.id}_name").value).to eq 'Tomatoes'
find('input[type="submit"]').click
article.reload
expect([article.unit, article.unit_quantity, article.price]).to eq ['250 g', 40, 0.6]
end
end end
end end
end end

View file

@ -11,7 +11,7 @@ describe Supplier do
options = { filename: 'foodsoft_file_01.csv' } options = { filename: 'foodsoft_file_01.csv' }
options[:outlist_absent] = true options[:outlist_absent] = true
options[:convert_units] = true options[:convert_units] = true
updated_article_pairs, outlisted_articles, new_articles = supplier.sync_from_file(Rails.root.join('spec/fixtures/foodsoft_file_01.csv'), options) updated_article_pairs, outlisted_articles, new_articles = supplier.sync_from_file(Rails.root.join('spec/fixtures/foodsoft_file_01.csv'), "foodsoft", options)
expect(new_articles.length).to be > 0 expect(new_articles.length).to be > 0
expect(updated_article_pairs.first[1][:name]).to eq 'Tomaten' expect(updated_article_pairs.first[1][:name]).to eq 'Tomaten'
expect(outlisted_articles.first).to eq article2 expect(outlisted_articles.first).to eq article2