diff --git a/lib/Coocook/Controller/Ajax/MealsDishesEditor.pm b/lib/Coocook/Controller/Ajax/MealsDishesEditor.pm index d4c3254d89683c9dde79ec8f07366193e6b1efb9..400cdccc38ca849ffe0c67f1016378377305e4d9 100644 --- a/lib/Coocook/Controller/Ajax/MealsDishesEditor.pm +++ b/lib/Coocook/Controller/Ajax/MealsDishesEditor.pm @@ -103,7 +103,26 @@ sub move_meal_or_dish : POST Chained('/project/base') PathPart('move_meal_dish') if ( $source_path->{item_type} eq 'dish' and $target_path->{item_type} eq 'dish' ) { - $moved->move_to_group( { meal_id => $target->meal->id }, $target->position ); + # Calculate positional offset because move_to_group() first removes the item, then inserts + # it at the specified position. This is only necessary if the moved item is above the target + # position. + my $pos_offset = sub { + if ( $moved->meal_id == $target->meal_id ) { + if ( $moved->position > $target->position and $direction eq "under" ) { + return +1; + } + elsif ( $moved->position < $target->position and $direction eq "over" ) { + return -1; + } + } + elsif ( $direction eq "under" ) { + return +1; + } + return 0; + } + ->(); + + $moved->move_to_group( { meal_id => $target->meal->id }, $target->position + $pos_offset ); } elsif ( $source_path->{item_type} eq 'dish' and $target_path->{item_type} eq 'meal' ) @@ -129,7 +148,25 @@ sub move_meal_or_dish : POST Chained('/project/base') PathPart('move_meal_dish') ] ); } - $moved->move_to_group( { project_id => $project->id, date => $target->date }, $target->position ); + my $pos_offset = sub { + if ( $moved->date == $target->date ) { + if ( $moved->position > $target->position and $direction eq "under" ) { + return +1; + } + elsif ( $moved->position < $target->position and $direction eq "over" ) { + return -1; + } + } + elsif ( $direction eq "under" ) { + return +1; + } + + return 0; + } + ->(); + + $moved->move_to_group( { project_id => $project->id, date => $target->date }, + $target->position + $pos_offset ); } $moved = $moved->for_meals_dishes_editor; diff --git a/share/test_data.sql b/share/test_data.sql index f6f58779f629d99a90425538f1fe228a4bae0a4d..1915ffa220587854ab7da73381ad0fd81c9911a3 100644 --- a/share/test_data.sql +++ b/share/test_data.sql @@ -51,11 +51,12 @@ INSERT INTO "articles" ( 6, 2, 9, NULL, NULL, NULL, 'other article', ''); INSERT INTO "meals" -(id, project_id, position, date, name, comment) VALUES -( 1, 1, 1, '2000-01-01', 'breakfast', 'Best meal of the day!'), -( 2, 1, 1, '2000-01-02', 'lunch', ''), -( 3, 1, 1, '2000-01-03', 'dinner', ''), -( 9, 2, 1, '2000-01-01', 'other meal', ''); +(id, project_id, position, date, name, comment) VALUES +( 1, 1, 1, '2000-01-01', 'breakfast', 'Best meal of the day!'), +( 2, 1, 1, '2000-01-02', 'lunch', ''), +( 3, 1, 1, '2000-01-03', 'dinner', ''), +( 4, 1, 2, '2000-01-03', 'midnight snack', ''), +( 9, 2, 1, '2000-01-01', 'other meal', ''); INSERT INTO "units" (id, project_id, short_name, long_name) VALUES @@ -101,23 +102,24 @@ INSERT INTO "recipe_ingredients" INSERT INTO "dishes" (id, meal_id, position, from_recipe_id, name, servings, prepare_at_meal_id, preparation, description, comment) VALUES -( 1, 1, 1, NULL, 'pancakes', 4, NULL, '', 'Make them really sweet!', 'sweet'), -( 2, 2, 2, 1, 'pizza', 2, NULL, '', '', ''), -( 3, 3, 3, NULL, 'bread', 4, 2, 'Bake bread!', '', ''); +( 1, 1, 1, NULL, 'pancakes', 2, NULL, '', 'Make them really sweet!', 'sweet'), +( 2, 1, 2, NULL, 'cerials', 2, NULL, '', '', ''), +( 3, 2, 2, 1, 'pizza', 4, NULL, '', '', ''), +( 4, 3, 3, NULL, 'bread', 4, 2, 'Bake bread!', '', ''); INSERT INTO "dish_ingredients" (id, position, dish_id, prepare, value, unit_id, article_id, comment, item_id) VALUES ( 1, 1, 1, FALSE, 500.0, 1, 1, '', NULL), ( 2, 2, 1, FALSE, 5.0, 1, 2, '', NULL), ( 3, 3, 1, FALSE, 0.5, 3, 3, '', NULL), -( 4, 1, 2, FALSE, 0.5, 2, 1, '', NULL), -( 5, 2, 2, FALSE, 0.25, 3, 3, '', NULL), -( 6, 3, 2, FALSE, 12.5, 1, 2, '', NULL), -( 7, 1, 3, TRUE, 1.0, 2, 1, '', NULL), -( 8, 2, 3, TRUE, 25.0, 1, 2, '', NULL), -( 9, 3, 3, TRUE, 1.0, 3, 3, '', NULL), -(10, 4, 3, FALSE, 500.0, 1, 4, '', NULL), -(11, 5, 3, FALSE, 12.5, 2, 1, '', NULL); +( 4, 1, 3, FALSE, 0.5, 2, 1, '', NULL), +( 5, 2, 3, FALSE, 0.25, 3, 3, '', NULL), +( 6, 3, 3, FALSE, 12.5, 1, 2, '', NULL), +( 7, 1, 4, TRUE, 1.0, 2, 1, '', NULL), +( 8, 2, 4, TRUE, 25.0, 1, 2, '', NULL), +( 9, 3, 4, TRUE, 1.0, 3, 3, '', NULL), +(10, 4, 4, FALSE, 500.0, 1, 4, '', NULL), +(11, 5, 4, FALSE, 12.5, 2, 1, '', NULL); INSERT INTO "purchase_lists" (id, project_id, name, date) VALUES diff --git a/t/controller_Ajax_IngredientsEditor.t b/t/controller_Ajax_IngredientsEditor.t index 905d4e67a331b53d88acf0cd25dd7a2840769a74..7c74b82a5b4c5b5d788d44f495dc0445da4a8210 100644 --- a/t/controller_Ajax_IngredientsEditor.t +++ b/t/controller_Ajax_IngredientsEditor.t @@ -46,7 +46,7 @@ subtest "delete ingredients", txn_do_and_rollback $t->schema, sub { "item was updated"; note "Deleting remaining ingredients of item ..."; - for ( [ 2, 6 ], [ 3, 8 ] ) { + for ( [ 3, 6 ], [ 4, 8 ] ) { my ( $dish_id, $ingredient_id ) = @$_; ok $t->post_json( "https://localhost/project/1/Test-Project/dish/$dish_id/ingredients/delete", diff --git a/t/controller_Ajax_MealsDishesEditor.t b/t/controller_Ajax_MealsDishesEditor.t index a347968357e71e91817eafd65fa61b45c6101b26..19c526fb5249ba35fdc356b3621bed3bf4079d4c 100644 --- a/t/controller_Ajax_MealsDishesEditor.t +++ b/t/controller_Ajax_MealsDishesEditor.t @@ -3,59 +3,342 @@ use Test2::V0; use DateTime; use lib 't/lib'; +use TestDB qw(txn_do_and_rollback); use Test::Coocook; -plan(17); +plan(20); -my $t = Test::Coocook->new(); +my $t = Test::Coocook->new(); +my $db = $t->schema; $t->get_ok('/'); $t->login_ok( 'john_doe', 'P@ssw0rd' ); -ok $t->post('/project/1/Test-Project/move_meal_dish'), "empty formdata request"; -$t->status_is(400); +subtest "move_meal_dish empty formdata request" => sub { + ok $t->post('/project/1/Test-Project/move_meal_dish'), "empty formdata request"; + $t->status_is(400); +}; -ok $t->post_json( '/project/1/Test-Project/move_meal_dish', {} ), "empty JSON request"; -$t->status_is(400); +subtest "move_meal_dish empty JSON request" => sub { + ok $t->post_json( '/project/1/Test-Project/move_meal_dish', {} ), "empty JSON request"; + $t->status_is(400); +}; -ok $t->post_json( - 'https://localhost/project/1/Test-Project/move_meal_dish', - { - direction => 'under', - source_path => { - date => '2000-01-01', - meal_id => 1, - dish_id => 1, - item_type => 'dish', - type => 'elem', - }, - target_path => { - date => '2000-01-02', - meal_id => 2, - dish_id => 2, - item_type => 'dish', - type => 'elem', - }, - } -); -$t->status_is(200); -$t->json_is( - { - id => 1, - meal_id => 2, - from_recipe_id => undef, - prepare_at_meal_id => undef, - position => 2, - date => '2000-01-02', - servings => 4, - name => L(), - comment => L(), - description => L(), - preparation => '', - update_url => 'https://localhost/project/1/Test-Project/dish/1/update', - delete_url => 'https://localhost/project/1/Test-Project/dish/1/delete', - } -); +subtest "move_meal_dish dish under dish in same meal" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'under', + source_path => { + date => '2000-01-01', + meal_id => 1, + dish_id => 1, + item_type => 'dish', + type => 'elem', + }, + target_path => { + date => '2000-01-01', + meal_id => 1, + dish_id => 2, + item_type => 'dish', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + id => 1, + meal_id => 1, + from_recipe_id => undef, + prepare_at_meal_id => undef, + position => 2, + date => '2000-01-01', + servings => 2, + name => L(), + comment => L(), + description => L(), + preparation => '', + update_url => 'https://localhost/project/1/Test-Project/dish/1/update', + delete_url => 'https://localhost/project/1/Test-Project/dish/1/delete', + } + ); +}; + +subtest "move_meal_dish dish over dish in same meal" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'over', + source_path => { + date => '2000-01-01', + meal_id => 1, + dish_id => 2, + item_type => 'dish', + type => 'elem', + }, + target_path => { + date => '2000-01-01', + meal_id => 1, + dish_id => 1, + item_type => 'dish', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + id => 2, + meal_id => 1, + from_recipe_id => undef, + prepare_at_meal_id => undef, + position => 1, + date => '2000-01-01', + servings => 2, + name => L(), + comment => D(), + description => D(), + preparation => '', + update_url => 'https://localhost/project/1/Test-Project/dish/2/update', + delete_url => 'https://localhost/project/1/Test-Project/dish/2/delete', + } + ); +}; + +subtest "move_meal_dish dish under dish in different meal" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'under', + source_path => { + date => '2000-01-01', + meal_id => 1, + dish_id => 1, + item_type => 'dish', + type => 'elem', + }, + target_path => { + date => '2000-01-02', + meal_id => 2, + dish_id => 3, + item_type => 'dish', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + id => 1, + meal_id => 2, + from_recipe_id => undef, + prepare_at_meal_id => undef, + position => 3, + date => '2000-01-02', + servings => 2, + name => L(), + comment => L(), + description => L(), + preparation => '', + update_url => 'https://localhost/project/1/Test-Project/dish/1/update', + delete_url => 'https://localhost/project/1/Test-Project/dish/1/delete', + } + ); +}; + +subtest "move_meal_dish dish over dish in different meal" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'over', + source_path => { + date => '2000-01-02', + meal_id => 2, + dish_id => 3, + item_type => 'dish', + type => 'elem', + }, + target_path => { + date => '2000-01-01', + meal_id => 1, + dish_id => 2, + item_type => 'dish', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + id => 3, + meal_id => 1, + from_recipe_id => 1, + prepare_at_meal_id => undef, + position => 2, + date => '2000-01-01', + servings => 4, + name => L(), + comment => D(), + description => D(), + preparation => '', + update_url => 'https://localhost/project/1/Test-Project/dish/3/update', + delete_url => 'https://localhost/project/1/Test-Project/dish/3/delete', + } + ); +}; + +subtest "move_meal_dish meal under meal on same date" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'under', + source_path => { + date => '2000-01-03', + meal_id => 3, + item_type => 'meal', + type => 'elem', + }, + target_path => { + date => '2000-01-03', + meal_id => 4, + item_type => 'meal', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + project_id => 1, + id => 3, + position => 2, + date => '2000-01-03', + name => L(), + comment => D(), + deletable => F(), + delete_dishes_url => 'https://localhost/project/1/Test-Project/meals/3/delete_dishes', + update_url => 'https://localhost/project/1/Test-Project/meals/3/update', + delete_url => 'https://localhost/project/1/Test-Project/meals/3/delete', + prepared_dishes => hash { etc() }, + dishes => hash { etc() }, + } + ); +}; + +subtest "move_meal_dish meal over meal on same date" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'over', + source_path => { + date => '2000-01-03', + meal_id => 4, + item_type => 'meal', + type => 'elem', + }, + target_path => { + date => '2000-01-03', + meal_id => 3, + item_type => 'meal', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + project_id => 1, + id => 4, + position => 1, + date => '2000-01-03', + name => L(), + comment => D(), + deletable => T(), + delete_dishes_url => 'https://localhost/project/1/Test-Project/meals/4/delete_dishes', + update_url => 'https://localhost/project/1/Test-Project/meals/4/update', + delete_url => 'https://localhost/project/1/Test-Project/meals/4/delete', + prepared_dishes => hash { etc() }, + dishes => hash { etc() }, + } + ); +}; + +subtest "move_meal_dish meal under meal on different date" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'under', + source_path => { + date => '2000-01-03', + meal_id => 3, + item_type => 'meal', + type => 'elem', + }, + target_path => { + date => '2000-01-03', + meal_id => 4, + item_type => 'meal', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + project_id => 1, + id => 3, + position => 2, + date => '2000-01-03', + name => L(), + comment => D(), + deletable => F(), + delete_dishes_url => 'https://localhost/project/1/Test-Project/meals/3/delete_dishes', + update_url => 'https://localhost/project/1/Test-Project/meals/3/update', + delete_url => 'https://localhost/project/1/Test-Project/meals/3/delete', + prepared_dishes => hash { etc() }, + dishes => hash { etc() }, + } + ); +}; + +subtest "move_meal_dish meal over meal on different date" => txn_do_and_rollback $db => sub { + ok $t->post_json( + 'https://localhost/project/1/Test-Project/move_meal_dish', + { + direction => 'over', + source_path => { + date => '2000-01-01', + meal_id => 1, + item_type => 'meal', + type => 'elem', + }, + target_path => { + date => '2000-01-03', + meal_id => 4, + item_type => 'meal', + type => 'elem', + }, + } + ); + $t->status_is(200); + $t->json_is( + { + project_id => 1, + id => 1, + position => 2, + date => '2000-01-03', + name => L(), + comment => L(), + deletable => F(), + delete_dishes_url => 'https://localhost/project/1/Test-Project/meals/1/delete_dishes', + update_url => 'https://localhost/project/1/Test-Project/meals/1/update', + delete_url => 'https://localhost/project/1/Test-Project/meals/1/delete', + prepared_dishes => hash { etc() }, + dishes => hash { etc() }, + } + ); +}; $t->post_ok( '/project/1/Test-Project/meals/create', @@ -65,12 +348,12 @@ $t->post_ok( comment => __FILE__, } ); - $t->json_is( { meal => hash { - field id => 10; - field name => 'meal from test'; + field id => 10; + field name => 'meal from test'; + field comment => __FILE__; etc(); }, } @@ -131,9 +414,9 @@ subtest "update mals" => sub { position => 1, comment => __FILE__, date => '2000-01-02', - deletable => T(), - prepared_dishes => {}, - dishes => {}, + deletable => F(), + prepared_dishes => hash { etc() }, + dishes => hash { etc() }, } ); diff --git a/t/model_Plan.t b/t/model_Plan.t index f19a447cc263866dfd6b1404dd75bc698c25d5d6..d5fc41666d03eeba78d68457035b58bbb636572c 100644 --- a/t/model_Plan.t +++ b/t/model_Plan.t @@ -28,7 +28,7 @@ is 'id' => 1, 'name' => 'pancakes', 'comment' => 'sweet', - 'servings' => 4, + 'servings' => 2, 'preparation' => '', 'description' => 'Make them really sweet!', 'has_prepared_ingredients' => F(), @@ -73,6 +73,16 @@ is 'value' => '0.5' } ], + }, + { + 'id' => 2, + 'name' => 'cerials', + 'comment' => '', + 'servings' => 2, + 'preparation' => '', + 'description' => '', + 'has_prepared_ingredients' => F(), + 'ingredients' => [], } ], } @@ -133,9 +143,24 @@ is my $project_plan = $plan->project($project) => array { field preparation => ''; field description => 'Make them really sweet!'; field comment => 'sweet'; - field servings => 4; + field servings => 2; + end(); + }; + item hash { + field id => 2; + field position => 2; + field meal_id => 1; + field meal => hash { field id => 1; etc() }; + field prepare_at_meal_id => U(); + field from_recipe_id => U(); + field name => 'cerials'; + field preparation => ''; + field description => ''; + field comment => ''; + field servings => 2; end(); }; + end(); }; field prepared_dishes => []; end(); @@ -157,7 +182,7 @@ is my $project_plan = $plan->project($project) => array { field deletable => F(); field dishes => array { item hash { - field id => 2; + field id => 3; field position => 2; field meal_id => 2; field meal => hash { field id => 2; etc() }; @@ -167,13 +192,13 @@ is my $project_plan = $plan->project($project) => array { field preparation => ''; field description => ''; field comment => ''; - field servings => 2; + field servings => 4; end(); }; }; field prepared_dishes => array { item $bread = hash { - field id => 3; + field id => 4; field meal_id => 3; field position => 3; field meal => hash { field id => 3; etc() }; @@ -206,6 +231,18 @@ is my $project_plan = $plan->project($project) => array { field comment => ''; end(); }; + item hash { + field id => 4; + field project_id => 1; + field position => 2; + field date => string '2000-01-03T00:00:00'; + field name => 'midnight snack'; + field deletable => T(); + field dishes => []; + field prepared_dishes => []; + field comment => ''; + end(); + }; }; }; }, @@ -217,7 +254,7 @@ subtest deletable => sub { ok !$plan->project($project)->[1]{meals}[0]{deletable}; $db->resultset('Meal')->find(2)->delete_dishes(); ok !$plan->project($project)->[1]{meals}[0]{deletable}; - $db->resultset('Dish')->find(3)->update( { prepare_at_meal_id => undef } ); + $db->resultset('Dish')->find(4)->update( { prepare_at_meal_id => undef } ); ok $plan->project($project)->[1]{meals}[0]{deletable}; }; @@ -260,7 +297,8 @@ subtest "order of meals from day() and project()" => sub { item hash { field date => string '2000-01-03T00:00:00'; field meals => array { - item hash { field name => "dinner"; etc() }; + item hash { field name => "dinner"; etc() }; + item hash { field name => "midnight snack"; etc() }; end() }; end(); diff --git a/t/schema.t b/t/schema.t index 3183824d763918e390470a2d8eaaa0727634b81b..3acef983fb600b9746753f40710f4d80cbc852a6 100644 --- a/t/schema.t +++ b/t/schema.t @@ -10,7 +10,7 @@ plan(9); ok my $db = TestDB->new; -is $db->count() => 89, "count()"; +is $db->count() => 91, "count()"; is $db->count(qw< Article Unit >) => 15, "count(Article Unit)"; subtest "one_row() in favor of first()" => sub { @@ -31,7 +31,7 @@ subtest statistics => sub { field users => 2; field organizations => 1; field recipes => 3; - field dishes_served => 4 + 2 + 4; + field dishes_served => 2 + 2 + 4 + 4; field dishes_planned => 0; etc(); }; diff --git a/t/schema_Dish.t b/t/schema_Dish.t index b350fcc2838c5dc184bc76f64a2503ff826de086..4a9640f8cf8935f08c13746de8037f62328a0b82 100644 --- a/t/schema_Dish.t +++ b/t/schema_Dish.t @@ -11,10 +11,10 @@ my $db = TestDB->new(); subtest recalculate => sub { my $dish = $db->resultset('Dish')->find(1); - ok $dish->recalculate(6), "recalculate()"; + ok $dish->recalculate(3), "recalculate()"; $dish->discard_changes(); - is $dish->servings => 6, "servings"; + is $dish->servings => 3, "servings"; is [ $dish->ingredients->hri->all ] => array { item hash { field value => 750; field unit_id => 1; field article_id => 1; etc }; diff --git a/t/schema_Project.t b/t/schema_Project.t index 0736e5cc0da6c6fbda516cef7fd1e5931433edfe..c4e483d9c7be5628335bea0115bacb1a2b138f67 100644 --- a/t/schema_Project.t +++ b/t/schema_Project.t @@ -15,8 +15,8 @@ subtest inventory => sub { is $inventory => { articles => 5, - dishes => 3, - meals => 3, + dishes => 4, + meals => 4, purchase_lists => 2, recipes => 1, shop_sections => 2, @@ -151,10 +151,10 @@ subtest find_or_create_tags_from_names => sub { subtest dishes => sub { isa_ok my $dishes = $project->dishes => 'Coocook::Schema::ResultSet::Dish'; - is $dishes->count => 3; + is $dishes->count => 4; }; subtest items => sub { isa_ok my $meals = $project->meals => 'Coocook::Schema::ResultSet::Meal'; - is $meals->count => 3; + is $meals->count => 4; };