diff --git a/.gitignore b/.gitignore index 41ff8f3..b7fbab8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ hypnotoad.pid *.swp cover_db/ +schema.png diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a90e471 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,57 @@ +# Changelog + +# Next Release + +# v0.9.1 + +* Change to semantic versioning +* Change database schema to use entity style model +* Added schema graphs for showing the schema layout +* **Fix:** null values on Org Graphs +* **Feature:** Org Graphs for sales and purchase data +* **Fix:** Deny organisations buying from themselves +* **Feature:** API endpoint for viewing purchases +* **Feature:** Transaction viewing in Admin interface +* **Fix:** Booleans under postgres and sqlite +* **Feature:** Organisation snippets API + +# v0.009 + +*No changes recorded* + +# v0.008.1 + +*No changes recorded* + +# v0.008 + +*No changes recorded* + +# v0.007 + +*No changes recorded* + +# v0.006 + +*No changes recorded* + +# v0.005 + +*No changes recorded* + +# v0.004 + +*No changes recorded* + +# v0.003 + +Made leaderboard cronjob scripts work correctly by using production config +instead of defaulting to development + +# v0.002 + +Release with leaderboard scripts for automatic generation of leaderboards + +# v0.001 + +First release with basic functionality. diff --git a/cpanfile b/cpanfile index 6e21422..e38cdc8 100644 --- a/cpanfile +++ b/cpanfile @@ -21,3 +21,8 @@ requires 'MooX::Options::Actions'; requires 'Module::Runtime'; requires 'DBIx::Class::DeploymentHandler'; requires 'DBIx::Class::Fixtures'; + +on 'schema-graph' => sub { + requires 'GraphViz'; + requires 'SQL::Translator'; +}; diff --git a/doc/Fixtures/Users.md b/doc/Fixtures/Users.md new file mode 100644 index 0000000..15db8d3 --- /dev/null +++ b/doc/Fixtures/Users.md @@ -0,0 +1,24 @@ +# Users Fixtures + +* Fixture Name: `users` + +## Users: + +* Test User1 + * email: test1@example.com + * password: abc123 +* Test User2 + * email: test2@example.com + * password: abc123 +* Test User3 + * email: test3@example.com + * password: abc123 +* Test User4 + * email: test4@example.com + * password: abc123 +* Test Org + * email: test5@example.com + * password: abc123 +* Test Admin + * email: admin@example.com + * password: abc123 diff --git a/lib/Pear/LocalLoop.pm b/lib/Pear/LocalLoop.pm index f06b1c0..a1763b4 100644 --- a/lib/Pear/LocalLoop.pm +++ b/lib/Pear/LocalLoop.pm @@ -74,16 +74,6 @@ sub startup { } }); - $self->helper( datetime_formatter => sub { - my $c = shift; - - return DateTime::Format::Strptime->new( - pattern => '%FT%T%z', - strict => 1, - on_error => 'undef', - ); - }); - $self->helper( get_path_from_uuid => sub { my $c = shift; my $uuid = shift; @@ -150,16 +140,17 @@ sub startup { $api->post('/search')->to('api-upload#post_search'); $api->post('/user')->to('api-user#post_account'); $api->post('/user/account')->to('api-user#post_account_update'); - $api->post('/user/day')->to('api-user#post_day'); $api->post('/user-history')->to('api-user#post_user_history'); $api->post('/stats')->to('api-stats#post_index'); $api->post('/stats/leaderboard')->to('api-stats#post_leaderboards'); + $api->post('/outgoing-transactions')->to('api-transactions#post_transaction_list_purchases'); my $api_v1 = $api->under('/v1'); my $api_v1_org = $api_v1->under('/organisation')->to('api-v1-organisation#auth'); $api_v1_org->post('/graphs')->to('api-v1-organisation-graphs#index'); + $api_v1_org->post('/snippets')->to('api-v1-organisation-snippets#index'); my $admin_routes = $r->under('/admin')->to('admin#under'); @@ -175,20 +166,21 @@ sub startup { $admin_routes->get('/users/:id')->to('admin-users#read'); $admin_routes->post('/users/:id')->to('admin-users#update'); $admin_routes->post('/users/:id/delete')->to('admin-users#delete'); - $admin_routes->post('/users/:id/edit')->to('admin-users#edit'); $admin_routes->get('/organisations')->to('admin-organisations#list'); $admin_routes->get('/organisations/add')->to('admin-organisations#add_org'); - $admin_routes->post('/organisations/add/submit')->to('admin-organisations#add_org_submit'); - $admin_routes->get('/organisations/valid/:id')->to('admin-organisations#valid_read'); - $admin_routes->post('/organisations/valid/:id/edit')->to('admin-organisations#valid_edit'); - $admin_routes->get('/organisations/pending/:id')->to('admin-organisations#pending_read'); - $admin_routes->post('/organisations/pending/:id/edit')->to('admin-organisations#pending_edit'); - $admin_routes->get('/organisations/pending/:id/approve')->to('admin-organisations#pending_approve'); + $admin_routes->post('/organisations/add')->to('admin-organisations#add_org_submit'); + $admin_routes->get('/organisations/:id')->to('admin-organisations#valid_read'); + $admin_routes->post('/organisations/:id')->to('admin-organisations#valid_edit'); $admin_routes->get('/feedback')->to('admin-feedback#index'); $admin_routes->get('/feedback/:id')->to('admin-feedback#read'); + $admin_routes->get('/transactions')->to('admin-transactions#index'); + $admin_routes->get('/transactions/:id')->to('admin-transactions#read'); + $admin_routes->get('/transactions/:id/image')->to('admin-transactions#image'); + $admin_routes->post('/transactions/:id/delete')->to('admin-transactions#delete'); + # my $user_routes = $r->under('/')->to('root#under'); # $user_routes->get('/home')->to('root#home'); diff --git a/lib/Pear/LocalLoop/Controller/Admin.pm b/lib/Pear/LocalLoop/Controller/Admin.pm index 005707b..8030d59 100644 --- a/lib/Pear/LocalLoop/Controller/Admin.pm +++ b/lib/Pear/LocalLoop/Controller/Admin.pm @@ -5,10 +5,10 @@ sub under { my $c = shift; if ( $c->is_user_authenticated ) { - return 1 if defined $c->current_user->administrator; + return 1 if $c->current_user->is_admin; } $c->redirect_to('/'); - return undef; + return 0; } sub home { @@ -16,8 +16,8 @@ sub home { my $user_rs = $c->schema->resultset('User'); my $token_rs = $c->schema->resultset('AccountToken'); - my $pending_orgs_rs = $c->schema->resultset('PendingOrganisation'); - my $pending_transaction_rs = $c->schema->resultset('PendingTransaction'); + my $pending_orgs_rs = $c->schema->resultset('Organisation')->search({ pending => 1 }); + my $pending_transaction_rs = $pending_orgs_rs->entity->sales; $c->stash( user_count => $user_rs->count, tokens => { diff --git a/lib/Pear/LocalLoop/Controller/Admin/Organisations.pm b/lib/Pear/LocalLoop/Controller/Admin/Organisations.pm index f382a5e..6921c6a 100644 --- a/lib/Pear/LocalLoop/Controller/Admin/Organisations.pm +++ b/lib/Pear/LocalLoop/Controller/Admin/Organisations.pm @@ -2,13 +2,12 @@ package Pear::LocalLoop::Controller::Admin::Organisations; use Mojo::Base 'Mojolicious::Controller'; use Try::Tiny; -use Data::Dumper; sub list { my $c = shift; - my $valid_orgs_rs = $c->schema->resultset('Organisation'); - my $pending_orgs_rs = $c->schema->resultset('PendingOrganisation'); + my $valid_orgs_rs = $c->schema->resultset('Organisation')->search({ pending => 0 }); + my $pending_orgs_rs = $c->schema->resultset('Organisation')->search({ pending => 1 }); $c->stash( valid_orgs_rs => $valid_orgs_rs, @@ -30,38 +29,44 @@ sub add_org_submit { $validation->required('town'); $validation->optional('sector'); $validation->optional('postcode')->postcode; + $validation->optional('pending'); if ( $validation->has_error ) { $c->flash( error => 'The validation has failed' ); - $c->app->log->warn(Dumper $validation); - return $c->redirect_to( '/admin/organisations/add/' ); + return $c->redirect_to( '/admin/organisations/add' ); } my $organisation; try { - $organisation = $c->schema->resultset('Organisation')->create({ - name => $validation->param('name'), - street_name => $validation->param('street_name'), - town => $validation->param('town'), - sector => $validation->param('sector'), - postcode => $validation->param('postcode'), + my $entity = $c->schema->resultset('Entity')->create({ + organisation => { + name => $validation->param('name'), + street_name => $validation->param('street_name'), + town => $validation->param('town'), + sector => $validation->param('sector'), + postcode => $validation->param('postcode'), + submitted_by_id => $c->current_user->id, + pending => defined $validation->param('pending') ? 0 : 1, + }, + type => 'organisation', }); + $organisation = $entity->organisation; } finally { if ( @_ ) { $c->flash( error => 'Something went wrong Adding the Organisation' ); - $c->app->log->warn(Dumper @_); + $c->redirect_to( '/admin/organisations/add' ); } else { $c->flash( success => 'Added Organisation' ); + $c->redirect_to( '/admin/organisations/' . $organisation->id); } }; - $c->redirect_to( '/admin/organisations/add/' ); } sub valid_read { my $c = shift; my $valid_org = $c->schema->resultset('Organisation')->find( $c->param('id') ); - my $transactions = $valid_org->transactions->search( + my $transactions = $valid_org->entity->sales->search( undef, { page => $c->param('page') || 1, rows => 10, @@ -83,11 +88,11 @@ sub valid_edit { $validation->required('town'); $validation->optional('sector'); $validation->required('postcode')->postcode; + $validation->optional('pending'); if ( $validation->has_error ) { $c->flash( error => 'The validation has failed' ); - $c->app->log->warn(Dumper $validation); - return $c->redirect_to( '/admin/organisations/valid/' . $c->param('id') ); + return $c->redirect_to( '/admin/organisations/' . $c->param('id') ); } my $valid_org = $c->schema->resultset('Organisation')->find( $c->param('id') ); @@ -100,95 +105,17 @@ sub valid_edit { town => $validation->param('town'), sector => $validation->param('sector'), postcode => $validation->param('postcode'), + pending => defined $validation->param('pending') ? 0 : 1, }); } ); } finally { if ( @_ ) { $c->flash( error => 'Something went wrong Updating the Organisation' ); - $c->app->log->warn(Dumper @_); } else { $c->flash( success => 'Updated Organisation' ); } }; - $c->redirect_to( '/admin/organisations/valid/' . $valid_org->id ); -} - -sub pending_read { - my $c = shift; - my $pending_org = $c->schema->resultset('PendingOrganisation')->find( $c->param('id') ); - my $transactions = $pending_org->transactions->search( - undef, { - page => $c->param('page') || 1, - rows => 10, - }, - ); - $c->stash( - pending_org => $pending_org, - transactions => $transactions, - ); -} - -sub pending_edit { - my $c = shift; - - my $validation = $c->validation; - $validation->required('name'); - $validation->required('street_name'); - $validation->required('town'); - $validation->required('postcode')->postcode; - - if ( $validation->has_error ) { - $c->flash( error => 'The validation has failed' ); - $c->app->log->warn(Dumper $validation); - return $c->redirect_to( '/admin/organisations/pending/' . $c->param('id') ); - } - - my $pending_org = $c->schema->resultset('PendingOrganisation')->find( $c->param('id') ); - - try { - $c->schema->storage->txn_do( sub { - $pending_org->update({ - name => $validation->param('name'), - street_name => $validation->param('street_name'), - town => $validation->param('town'), - postcode => $validation->param('postcode'), - }); - } ); - } finally { - if ( @_ ) { - $c->flash( error => 'Something went wrong Updating the Organisation' ); - $c->app->log->warn(Dumper @_); - } else { - $c->flash( success => 'Updated Organisation' ); - } - }; - $c->redirect_to( '/admin/organisations/pending/' . $pending_org->id ); -} - -sub pending_approve { - my $c = shift; - my $pending_org = $c->schema->resultset('PendingOrganisation')->find( $c->param('id') ); - - my $valid_org; - try { - $c->schema->storage->txn_do( sub { - $valid_org = $c->schema->resultset('Organisation')->create({ - name => $pending_org->name, - street_name => $pending_org->street_name, - town => $pending_org->town, - postcode => $pending_org->postcode, - }); - $c->copy_transactions_and_delete( $pending_org, $valid_org ); - } ); - } finally { - if ( @_ ) { - $c->flash( error => 'Something went wrong Validating the Organisation' ); - $c->redirect_to( '/admin/organisations/pending/' . $pending_org->id ); - } else { - $c->flash( success => 'Validated Organisation' ); - $c->redirect_to( '/admin/organisations/valid/' . $valid_org->id ); - } - } + $c->redirect_to( '/admin/organisations/' . $valid_org->id ); } 1; diff --git a/lib/Pear/LocalLoop/Controller/Admin/Transactions.pm b/lib/Pear/LocalLoop/Controller/Admin/Transactions.pm new file mode 100644 index 0000000..428fdf9 --- /dev/null +++ b/lib/Pear/LocalLoop/Controller/Admin/Transactions.pm @@ -0,0 +1,66 @@ +package Pear::LocalLoop::Controller::Admin::Transactions; +use Mojo::Base 'Mojolicious::Controller'; + +has result_set => sub { + my $c = shift; + return $c->schema->resultset('Transaction'); +}; + +sub index { + my $c = shift; + + my $transactions = $c->result_set->search( + undef, { + page => $c->param('page') || 1, + rows => 10, + order_by => { -desc => 'submitted_at' }, + }, + ); + $c->stash( + transactions => $transactions, + ); +} + +sub read { + my $c = shift; + + my $id = $c->param('id'); + + if ( my $transaction = $c->result_set->find($id) ) { + $c->stash( transaction => $transaction ); + } else { + $c->flash( error => 'No transaction found' ); + $c->redirect_to( '/admin/transactions' ); + } +} + +sub image { + my $c = shift; + + my $id = $c->param('id'); + + my $transaction = $c->result_set->find($id); + + if ( $transaction->proof_image ) { + $c->reply->asset($c->get_file_from_uuid($transaction->proof_image)); + } else { + $c->reply->static('image/no_transaction.jpg'); + } +} + +sub delete { + my $c = shift; + + my $id = $c->param('id'); + +if ( my $transaction = $c->result_set->find($id) ) { + $transaction->delete; + $c->flash( success => 'Successfully deleted transaction' ); + $c->redirect_to( '/admin/transactions' ); + } else { + $c->flash( error => 'No transaction found' ); + $c->redirect_to( '/admin/transactions' ); + } +} + +1; diff --git a/lib/Pear/LocalLoop/Controller/Admin/Users.pm b/lib/Pear/LocalLoop/Controller/Admin/Users.pm index 064ffba..13905c6 100644 --- a/lib/Pear/LocalLoop/Controller/Admin/Users.pm +++ b/lib/Pear/LocalLoop/Controller/Admin/Users.pm @@ -33,14 +33,24 @@ sub read { my $id = $c->param('id'); if ( my $user = $c->user_result_set->find($id) ) { - $c->stash( user => $user ); + my $transactions = $user->entity->purchases->search( + undef, { + page => $c->param('page') || 1, + rows => 10, + order_by => { -desc => 'submitted_at' }, + }, + ); + $c->stash( + user => $user, + transactions => $transactions, + ); } else { $c->flash( error => 'No User found' ); $c->redirect_to( '/admin/users' ); } } -sub edit { +sub update { my $c = shift; my $id = $c->param('id'); @@ -61,10 +71,10 @@ sub edit { $validation->required('postcode')->postcode; $validation->optional('new_password'); - if ( defined $user->customer_id ) { + if ( $user->type eq 'customer' ) { $validation->required('display_name'); $validation->required('full_name'); - } elsif ( defined $user->organisation_id ) { + } elsif ( $user->type eq 'organisation' ) { $validation->required('name'); $validation->required('street_name'); $validation->required('town'); @@ -73,15 +83,14 @@ sub edit { if ( $validation->has_error ) { $c->flash( error => 'The validation has failed' ); - $c->app->log->warn(Dumper $validation); return $c->redirect_to( '/admin/users/' . $id ); } - if ( defined $user->customer_id ){ + if ( $user->type eq 'customer' ){ try { $c->schema->txn_do( sub { - $user->customer->update({ + $user->entity->customer->update({ full_name => $validation->param('full_name'), display_name => $validation->param('display_name'), postcode => $validation->param('postcode'), @@ -100,11 +109,11 @@ sub edit { }; } } - elsif ( defined $user->organisation_id ) { + elsif ( $user->type eq 'organisation' ) { try { $c->schema->txn_do( sub { - $user->organisation->update({ + $user->entity->organisation->update({ name => $validation->param('name'), street_name => $validation->param('street_name'), town => $validation->param('town'), @@ -129,9 +138,4 @@ sub edit { $c->redirect_to( '/admin/users/' . $id ); } -sub update { - my $c = shift; - $c->redirect_to( '/admin/users' ); -} - 1; diff --git a/lib/Pear/LocalLoop/Controller/Api/Register.pm b/lib/Pear/LocalLoop/Controller/Api/Register.pm index dfff302..8ba4f11 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Register.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Register.pm @@ -48,7 +48,7 @@ has error_messages => sub { }; }; -sub post_register{ +sub post_register { my $c = shift; my $validation = $c->validation; @@ -87,17 +87,19 @@ sub post_register{ name => $validation->param('token'), used => 0, })->update({ used => 1 }); - # Create customer as a seperate step, so we dont leak data - my $customer = $c->schema->resultset('Customer')->create({ - full_name => $validation->param('full_name'), - display_name => $validation->param('display_name'), - year_of_birth => $validation->param('year_of_birth'), - postcode => $validation->param('postcode'), - }); - $c->schema->resultset('User')->create({ - customer => $customer, - email => $validation->param('email'), - password => $validation->param('password'), + + $c->schema->resultset('Entity')->create({ + customer => { + full_name => $validation->param('full_name'), + display_name => $validation->param('display_name'), + year_of_birth => $validation->param('year_of_birth'), + postcode => $validation->param('postcode'), + }, + user => { + email => $validation->param('email'), + password => $validation->param('password'), + }, + type => 'customer', }); }); @@ -109,17 +111,19 @@ sub post_register{ name => $validation->param('token'), used => 0, })->update({ used => 1 }); - my $organisation = $c->schema->resultset('Organisation')->create({ - name => $validation->param('name'), - street_name => $validation->param('street_name'), - town => $validation->param('town'), - sector => $validation->param('sector'), - postcode => $validation->param('postcode'), - }); - $c->schema->resultset('User')->create({ - organisation => $organisation, - email => $validation->param('email'), - password => $validation->param('password'), + $c->schema->resultset('Entity')->create({ + organisation => { + name => $validation->param('name'), + street_name => $validation->param('street_name'), + town => $validation->param('town'), + sector => $validation->param('sector'), + postcode => $validation->param('postcode'), + }, + user => { + email => $validation->param('email'), + password => $validation->param('password'), + }, + type => 'organisation', }); }); } diff --git a/lib/Pear/LocalLoop/Controller/Api/Stats.pm b/lib/Pear/LocalLoop/Controller/Api/Stats.pm index c6b1c7d..ddb5152 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Stats.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Stats.pm @@ -15,44 +15,44 @@ has error_messages => sub { sub post_index { my $c = shift; - my $user = $c->stash->{api_user}; + my $user = $c->stash->{api_user}->entity; - my $today_rs = $user->transactions->today_rs; - my $today_sum = $today_rs->get_column('value')->sum; + my $today_rs = $user->purchases->today_rs; + my $today_sum = $today_rs->get_column('value')->sum || 0; my $today_count = $today_rs->count; - my $week_rs = $user->transactions->week_rs; - my $week_sum = $week_rs->get_column('value')->sum; + my $week_rs = $user->purchases->week_rs; + my $week_sum = $week_rs->get_column('value')->sum || 0; my $week_count = $week_rs->count; - my $month_rs = $user->transactions->month_rs; - my $month_sum = $month_rs->get_column('value')->sum; + my $month_rs = $user->purchases->month_rs; + my $month_sum = $month_rs->get_column('value')->sum || 0; my $month_count = $month_rs->count; - my $user_rs = $user->transactions; - my $user_sum = $user_rs->get_column('value')->sum; + my $user_rs = $user->purchases; + my $user_sum = $user_rs->get_column('value')->sum || 0; my $user_count = $user_rs->count; my $global_rs = $c->schema->resultset('Transaction'); - my $global_sum = $global_rs->get_column('value')->sum; + my $global_sum = $global_rs->get_column('value')->sum || 0; my $global_count = $global_rs->count; my $leaderboard_rs = $c->schema->resultset('Leaderboard'); my $monthly_board = $leaderboard_rs->get_latest( 'monthly_total' ); my $monthly_values = $monthly_board->values; - my $current_user_position = $monthly_values ? $monthly_values->find({ user_id => $c->stash->{api_user}->id }) : undef; + my $current_user_position = $monthly_values ? $monthly_values->find({ entity_id => $user->id }) : undef; return $c->render( json => { success => Mojo::JSON->true, - today_sum => $today_sum || 0, + today_sum => $today_sum / 100000, today_count => $today_count, - week_sum => $week_sum || 0, + week_sum => $week_sum / 100000, week_count => $week_count, - month_sum => $month_sum || 0, + month_sum => $month_sum / 100000, month_count => $month_count, - user_sum => $user_sum || 0, + user_sum => $user_sum / 100000, user_count => $user_count, - global_sum => $global_sum || 0, + global_sum => $global_sum / 100000, global_count => $global_count, user_position => defined $current_user_position ? $current_user_position->position : 0, }); @@ -84,14 +84,18 @@ sub post_leaderboards { /, { display_name => 'customer.display_name' }, ], - join => { user => 'customer' }, + join => { entity => 'customer' }, }, ); $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); my @leaderboard_array = $today_values->all; - my $current_user_position = $today_values->find({ user_id => $c->stash->{api_user}->id }); + if ( $validation->param('type') =~ /total$/ ) { + map { $_->{value} / 100000 } @leaderboard_array; + } + + my $current_user_position = $today_values->find({ entity_id => $c->stash->{api_user}->entity->id }); return $c->render( json => { success => Mojo::JSON->true, diff --git a/lib/Pear/LocalLoop/Controller/Api/Transactions.pm b/lib/Pear/LocalLoop/Controller/Api/Transactions.pm new file mode 100644 index 0000000..79e61ad --- /dev/null +++ b/lib/Pear/LocalLoop/Controller/Api/Transactions.pm @@ -0,0 +1,49 @@ +package Pear::LocalLoop::Controller::Api::Transactions; +use Mojo::Base 'Mojolicious::Controller'; +use Mojo::JSON; + +has error_messages => sub { + return { + email => { + required => { message => 'No email sent.', status => 400 }, + email => { message => 'Email is invalid.', status => 400 }, + }, + }; +}; + +sub post_transaction_list_purchases { + my $c = shift; + + my $user = $c->stash->{api_user}; + + my $validation = $c->validation; + $validation->input( $c->stash->{api_json} ); + $validation->optional('page')->number; + + return $c->api_validation_error if $validation->has_error; + + my $transactions = $user->entity->purchases->search( + undef, { + page => $validation->param('page') || 1, + rows => 10, + order_by => { -desc => 'purchase_time' }, + }, + ); + +# purchase_time needs timezone attached to it + my @transaction_list = ( + map {{ + seller => $_->seller->name, + value => $_->value / 100000, + purchase_time => $_->purchase_time, + }} $transactions->all + ); + + return $c->render( json => { + success => Mojo::JSON->true, + transactions => \@transaction_list, + page_no => $transactions->pager->total_entries, + }); +} + +1; diff --git a/lib/Pear/LocalLoop/Controller/Api/Upload.pm b/lib/Pear/LocalLoop/Controller/Api/Upload.pm index 08db6cc..e7ae0d5 100644 --- a/lib/Pear/LocalLoop/Controller/Api/Upload.pm +++ b/lib/Pear/LocalLoop/Controller/Api/Upload.pm @@ -112,7 +112,10 @@ sub post_upload { if ( $type == 1 ) { # Validated Organisation - my $valid_org_rs = $c->schema->resultset('Organisation'); + my $valid_org_rs = $c->schema->resultset('Organisation')->search({ + pending => 0, + entity_id => { "!=" => $user->entity_id }, + }); $validation->required('organisation_id')->number->in_resultset( 'id', $valid_org_rs ); return $c->api_validation_error if $validation->has_error; @@ -121,7 +124,11 @@ sub post_upload { } elsif ( $type == 2 ) { # Unvalidated Organisation - my $valid_org_rs = $c->schema->resultset('PendingOrganisation')->search({ submitted_by_id => $user->id }); + my $valid_org_rs = $c->schema->resultset('Organisation')->search({ + submitted_by_id => $user->id, + pending => 1, + entity_id => { "!=" => $user->entity_id }, + }); $validation->required('organisation_id')->number->in_resultset( 'id', $valid_org_rs ); return $c->api_validation_error if $validation->has_error; @@ -137,14 +144,15 @@ sub post_upload { return $c->api_validation_error if $validation->has_error; - $organisation = $c->schema->resultset('PendingOrganisation')->create({ - submitted_by => $user, - submitted_at => DateTime->now, - name => $validation->param('organisation_name'), - street_name => $validation->param('street_name'), - town => $validation->param('town'), - postcode => $validation->param('postcode'), + my $entity = $c->schema->resultset('Entity')->create_org({ + submitted_by_id => $user->id, + name => $validation->param('organisation_name'), + street_name => $validation->param('street_name'), + town => $validation->param('town'), + postcode => $validation->param('postcode'), + pending => 1, }); + $organisation = $entity->organisation; } unless ( defined $organisation ) { @@ -164,12 +172,12 @@ sub post_upload { $purchase_time ||= DateTime->now(); my $file = defined $upload ? $c->store_file_from_upload( $upload ) : undef; - my $new_transaction = $organisation->create_related( - 'transactions', + my $new_transaction = $organisation->entity->create_related( + 'sales', { - buyer => $user, - value => $transaction_value, - ( defined $file ? ( proof_image => $file ) : ( proof_image => 'a' ) ), + buyer => $user->entity, + value => $transaction_value * 100000, + ( defined $file ? ( proof_image => $file ) : () ), purchase_time => $c->format_db_datetime($purchase_time), } ); @@ -197,6 +205,8 @@ sub post_search { my $c = shift; my $self = $c; + my $user = $c->stash->{api_user}; + my $validation = $c->validation; $validation->input( $c->stash->{api_json} ); @@ -209,11 +219,19 @@ sub post_search { my $search_stmt = [ 'LOWER("name") LIKE ?', '%' . lc $search_name . '%' ]; - my $valid_orgs_rs = $c->schema->resultset('Organisation')->search( + my $org_rs = $c->schema->resultset('Organisation'); + my $valid_orgs_rs = $org_rs->search({ + pending => 0, + entity_id => { "!=" => $user->entity_id }, + })->search( \$search_stmt, ); - my $pending_orgs_rs = $c->stash->{api_user}->pending_organisations->search( + my $pending_orgs_rs = $org_rs->search({ + pending => 1, + submitted_by_id => $c->stash->{api_user}->id, + entity_id => { "!=" => $user->entity_id }, + })->search( \$search_stmt, ); diff --git a/lib/Pear/LocalLoop/Controller/Api/User.pm b/lib/Pear/LocalLoop/Controller/Api/User.pm index f9b46cb..0c0a2f3 100644 --- a/lib/Pear/LocalLoop/Controller/Api/User.pm +++ b/lib/Pear/LocalLoop/Controller/Api/User.pm @@ -33,25 +33,12 @@ has error_messages => sub { town => { required => { message => 'No town sent.', status => 400 }, }, + sector => { + required => { message => 'No sector sent.', status => 400 }, + }, }; }; -sub post_day { - my $c = shift; - - my $validation = $c->validation; - - $validation->input( $c->stash->{api_json} ); - - $validation->optional('day')->is_iso_datetime; - - return $c->api_validation_error if $validation->has_error; - - $c->render( json => { - success => Mojo::JSON->true, - }); -} - sub post_account { my $c = shift; @@ -60,28 +47,43 @@ sub post_account { if ( defined $user_result ) { my $email = $user_result->email; - my $full_name; - my $display_name; - my $postcode; - #Needs elsif added for trader page for this similar relevant entry - if ( defined $user_result->customer_id ) { - $full_name = $user_result->customer->full_name; - $display_name = $user_result->customer->display_name; - $postcode = $user_result->customer->postcode; - } elsif ( defined $user_result->organisation_id ) { - $display_name = $user_result->organisation->name; + if ( $user_result->type eq 'customer' ) { + my $full_name = $user_result->entity->customer->full_name; + my $display_name = $user_result->entity->customer->display_name; + my $postcode = $user_result->entity->customer->postcode; + return $c->render( json => { + success => Mojo::JSON->true, + full_name => $full_name, + display_name => $display_name, + email => $email, + postcode => $postcode, + }); + } elsif ( $user_result->type eq 'organisation' ) { + my $name = $user_result->entity->organisation->name; + my $postcode = $user_result->entity->organisation->postcode; + my $street_name = $user_result->entity->organisation->street_name; + my $town = $user_result->entity->organisation->town; + my $sector = $user_result->entity->organisation->sector; + return $c->render( json => { + success => Mojo::JSON->true, + town => $town, + name => $name, + sector => $sector, + street_name => $street_name, + email => $email, + postcode => $postcode, + }); } else { - return; + return $c->render( + json => { + success => Mojo::JSON->false, + message => 'Invalid Server Error.', + }, + status => 500 + ); } - return $c->render( json => { - success => Mojo::JSON->true, - full_name => $full_name, - display_name => $display_name, - email => $email, - postcode => $postcode, - }); } return $c->render( json => { @@ -121,10 +123,10 @@ sub post_account_update { $validation->required('postcode')->postcode; $validation->optional('new_password'); - if ( defined $user->customer_id ) { + if ( $user->type eq 'customer' ) { $validation->required('display_name'); $validation->required('full_name'); - } elsif ( defined $user->organisation_id ) { + } elsif ( $user->type eq 'organisation' ) { $validation->required('name'); $validation->required('street_name'); $validation->required('town'); @@ -133,10 +135,10 @@ sub post_account_update { return $c->api_validation_error if $validation->has_error; - if ( defined $user->customer_id ){ + if ( $user->type eq 'customer' ){ $c->schema->txn_do( sub { - $user->customer->update({ + $user->entity->customer->update({ full_name => $validation->param('full_name'), display_name => $validation->param('display_name'), postcode => $validation->param('postcode'), @@ -148,10 +150,10 @@ sub post_account_update { }); } - elsif ( defined $user->organisation_id ) { + elsif ( $user->type eq 'organisation' ) { $c->schema->txn_do( sub { - $user->organisation->update({ + $user->entity->organisation->update({ name => $validation->param('name'), street_name => $validation->param('street_name'), town => $validation->param('town'), diff --git a/lib/Pear/LocalLoop/Controller/Api/V1/Organisation/Graphs.pm b/lib/Pear/LocalLoop/Controller/Api/V1/Organisation/Graphs.pm index bb807ba..d1eddaf 100644 --- a/lib/Pear/LocalLoop/Controller/Api/V1/Organisation/Graphs.pm +++ b/lib/Pear/LocalLoop/Controller/Api/V1/Organisation/Graphs.pm @@ -18,6 +18,11 @@ sub index { $validation->required('graph')->in( qw/ customers_last_7_days customers_last_30_days + sales_last_7_days + sales_last_30_days + purchases_last_7_days + purchases_last_30_days + customers_range / ); return $c->api_validation_error if $validation->has_error; @@ -39,6 +44,40 @@ sub index { return $c->$graph_sub; } +sub graph_customers_range { + my $c = shift; + + my $validation = $c->validation; + $validation->input( $c->stash->{api_json} ); + $validation->required('start')->is_iso_date; + $validation->required('end')->is_iso_date; + + return $c->api_validation_error if $validation->has_error; + + my $entity = $c->stash->{api_user}->entity; + + my $data = { labels => [], data => [] }; + my $start = $c->parse_iso_date( $validation->param('start') ); + my $end = $c->parse_iso_date( $validation->param('end') ); + + while ( $start <= $end ) { + my $next_end = $start->clone->add( days => 1 ); + my $transactions = $entity->sales + ->search_between( $start, $next_end ) + ->count; + push @{ $data->{ labels } }, $c->format_iso_date( $start ); + push @{ $data->{ data } }, $transactions; + $start->add( days => 1 ); + } + + return $c->render( + json => { + success => Mojo::JSON->true, + graph => $data, + } + ); +} + sub graph_customers_last_7_days { my $c = shift; @@ -56,24 +95,20 @@ sub graph_customers_last_30_days { sub _customers_last_duration { my ( $c, $duration ) = @_; - my $org = $c->stash->{api_user}->organisation; + my $entity = $c->stash->{api_user}->entity; - my $data = { day => [], count => [] }; + my $data = { labels => [], data => [] }; - my $start = DateTime->today; - my $end = $start->clone->subtract_duration( $duration ); + my ( $start, $end ) = $c->_get_start_end_duration( $duration ); - my $dtf = $c->schema->storage->datetime_parser; - - while ( $end < $start ) { - my $moving_end = $end->clone->add( days => 1 ); - my $transactions = $c->schema->resultset('Transaction')->search({ - seller_id => $org->id, - purchase_time => { '-between' => [ $dtf->format_datetime($end), $dtf->format_datetime($moving_end) ] }, - })->count; - push @{$data->{day}}, $end->day_name; - push @{$data->{count}}, $transactions; - $end->add( days => 1 ); + while ( $start < $end ) { + my $next_end = $start->clone->add( days => 1 ); + my $transactions = $entity->sales + ->search_between( $start, $next_end ) + ->count; + push @{ $data->{ labels } }, $start->day_name; + push @{ $data->{ data } }, $transactions; + $start->add( days => 1 ); } return $c->render( @@ -84,4 +119,75 @@ sub _customers_last_duration { ); } +sub graph_sales_last_7_days { return shift->_sales_last_duration( 7 ) } +sub graph_sales_last_30_days { return shift->_sales_last_duration( 30 ) } + +sub _sales_last_duration { + my ( $c, $day_duration ) = @_; + + my $duration = DateTime::Duration->new( days => $day_duration ); + my $entity = $c->stash->{api_user}->entity; + + my $data = { labels => [], data => [] }; + + my ( $start, $end ) = $c->_get_start_end_duration( $duration ); + + while ( $start < $end ) { + my $next_end = $start->clone->add( days => 1 ); + my $transactions = $entity->sales + ->search_between( $start, $next_end ) + ->get_column('value') + ->sum || 0 + 0; + push @{ $data->{ labels } }, $start->day_name; + push @{ $data->{ data } }, $transactions / 100000; + $start->add( days => 1 ); + } + + return $c->render( + json => { + success => Mojo::JSON->true, + graph => $data, + } + ); +} + +sub graph_purchases_last_7_days { return shift->_purchases_last_duration( 7 ) } +sub graph_purchases_last_30_days { return shift->_purchases_last_duration( 30 ) } + +sub _purchases_last_duration { + my ( $c, $day_duration ) = @_; + + my $duration = DateTime::Duration->new( days => $day_duration ); + my $entity = $c->stash->{api_user}->entity; + + my $data = { labels => [], data => [] }; + + my ( $start, $end ) = $c->_get_start_end_duration( $duration ); + + while ( $start < $end ) { + my $next_end = $start->clone->add( days => 1 ); + my $transactions = $entity->purchases + ->search_between( $start, $next_end ) + ->get_column('value') + ->sum || 0 + 0; + push @{ $data->{ labels } }, $start->day_name; + push @{ $data->{ data } }, $transactions / 100000; + $start->add( days => 1 ); + } + + return $c->render( + json => { + success => Mojo::JSON->true, + graph => $data, + } + ); +} + +sub _get_start_end_duration { + my ( $c, $duration ) = @_; + my $end = DateTime->today; + my $start = $end->clone->subtract_duration( $duration ); + return ( $start, $end ); +} + 1; diff --git a/lib/Pear/LocalLoop/Controller/Api/V1/Organisation/Snippets.pm b/lib/Pear/LocalLoop/Controller/Api/V1/Organisation/Snippets.pm new file mode 100644 index 0000000..07cd90a --- /dev/null +++ b/lib/Pear/LocalLoop/Controller/Api/V1/Organisation/Snippets.pm @@ -0,0 +1,67 @@ +package Pear::LocalLoop::Controller::Api::V1::Organisation::Snippets; +use Mojo::Base 'Mojolicious::Controller'; + +sub index { + my $c = shift; + + my $entity = $c->stash->{api_user}->entity; + my $data = { + this_month_sales_count => 0, + this_month_sales_total => 0, + this_month_purchases_count => 0, + this_month_purchases_total => 0, + this_week_sales_count => 0, + this_week_sales_total => 0, + this_week_purchases_count => 0, + this_week_purchases_total => 0, + today_sales_count => 0, + today_sales_total => 0, + today_purchases_count => 0, + today_purchases_total => 0, + }; + + my $now = DateTime->now; + my $today = DateTime->today; + my $week_ago = $today->clone->subtract( days => 7 ); + my $month_ago = $today->clone->subtract( days => 30 ); + + my $today_sales = $entity->sales->search_between( $today, $now ); + $data->{ today_sales_count } = $today_sales->count; + $data->{ today_sales_total } = $today_sales->get_column('value')->sum || 0; + $data->{ today_sales_total } /= 100000; + + my $week_sales = $entity->sales->search_between( $week_ago, $today ); + $data->{ this_week_sales_count } = $week_sales->count; + $data->{ this_week_sales_total } = $week_sales->get_column('value')->sum || 0; + $data->{ this_week_sales_total } /= 100000; + + my $month_sales = $entity->sales->search_between( $month_ago, $today ); + $data->{ this_month_sales_count } = $month_sales->count; + $data->{ this_month_sales_total } = $month_sales->get_column('value')->sum || 0; + $data->{ this_month_sales_total } /= 100000; + + my $today_purchases = $entity->purchases->search_between( $today, $now ); + $data->{ today_purchases_count } = $today_purchases->count; + $data->{ today_purchases_total } = $today_purchases->get_column('value')->sum || 0; + $data->{ today_purchases_total } /= 100000; + + my $week_purchases = $entity->purchases->search_between( $week_ago, $today ); + $data->{ this_week_purchases_count } = $week_purchases->count; + $data->{ this_week_purchases_total } = $week_purchases->get_column('value')->sum || 0; + $data->{ this_week_purchases_total } /= 100000; + + my $month_purchases = $entity->purchases->search_between( $month_ago, $today ); + $data->{ this_month_purchases_count } = $month_purchases->count; + $data->{ this_month_purchases_total } = $month_purchases->get_column('value')->sum || 0; + $data->{ this_month_purchases_total } /= 100000; + + return $c->render( + json => { + success => Mojo::JSON->true, + snippets => $data, + } + ); + +} + +1; diff --git a/lib/Pear/LocalLoop/Plugin/Datetime.pm b/lib/Pear/LocalLoop/Plugin/Datetime.pm index a94c292..8b7495a 100644 --- a/lib/Pear/LocalLoop/Plugin/Datetime.pm +++ b/lib/Pear/LocalLoop/Plugin/Datetime.pm @@ -7,34 +7,52 @@ sub register { my ( $plugin, $app, $conf ) = @_; $app->helper( iso_datetime_parser => sub { - return DateTime::Format::Strptime->new( pattern => '%Y-%m-%dT%H:%M:%S.%3N%z' ); -}); + return DateTime::Format::Strptime->new( pattern => '%Y-%m-%dT%H:%M:%S.%3N%z' ); + }); -$app->helper( parse_iso_datetime => sub { - my ( $c, $date_string ) = @_; - return $c->iso_datetime_parser->parse_datetime( - $date_string, - ); -}); + $app->helper( iso_date_parser => sub { + return DateTime::Format::Strptime->new( pattern => '%Y-%m-%d' ); + }); -$app->helper( format_iso_datetime => sub { - my ( $c, $datetime_obj ) = @_; - return $c->iso_datetime_parser->parse_datetime( - $datetime_obj, - ); -}); + $app->helper( parse_iso_date => sub { + my ( $c, $date_string ) = @_; + return $c->iso_date_parser->parse_datetime( + $date_string, + ); + }); -$app->helper( db_datetime_parser => sub { - return shift->schema->storage->datetime_parser; -}); + $app->helper( format_iso_date => sub { + my ( $c, $datetime_obj ) = @_; + return $c->iso_date_parser->format_datetime( + $datetime_obj, + ); + }); -$app->helper( format_db_datetime => sub { - my ( $c, $datetime_obj ) = @_; - $datetime_obj->set_time_zone('UTC'); - return $c->db_datetime_parser->format_datetime( - $datetime_obj, - ); -}); + $app->helper( parse_iso_datetime => sub { + my ( $c, $date_string ) = @_; + return $c->iso_datetime_parser->parse_datetime( + $date_string, + ); + }); + + $app->helper( format_iso_datetime => sub { + my ( $c, $datetime_obj ) = @_; + return $c->iso_datetime_parser->format_datetime( + $datetime_obj, + ); + }); + + $app->helper( db_datetime_parser => sub { + return shift->schema->storage->datetime_parser; + }); + + $app->helper( format_db_datetime => sub { + my ( $c, $datetime_obj ) = @_; + $datetime_obj->set_time_zone('UTC'); + return $c->db_datetime_parser->format_datetime( + $datetime_obj, + ); + }); } diff --git a/lib/Pear/LocalLoop/Plugin/Validators.pm b/lib/Pear/LocalLoop/Plugin/Validators.pm index 33aeca2..bab1863 100644 --- a/lib/Pear/LocalLoop/Plugin/Validators.pm +++ b/lib/Pear/LocalLoop/Plugin/Validators.pm @@ -53,9 +53,9 @@ sub register { return $app->types->type($extension) eq $filetype ? undef : 1; }); - $app->validator->add_check( is_iso_datetime => sub { + $app->validator->add_check( is_iso_date => sub { my ( $validation, $name, $value ) = @_; - $value = $app->datetime_formatter->parse_datetime( $value ); + $value = $app->iso_date_parser->parse_datetime( $value ); return defined $value ? undef : 1; }); diff --git a/lib/Pear/LocalLoop/Schema.pm b/lib/Pear/LocalLoop/Schema.pm index 739cc56..a7fc365 100644 --- a/lib/Pear/LocalLoop/Schema.pm +++ b/lib/Pear/LocalLoop/Schema.pm @@ -6,7 +6,7 @@ use warnings; use base 'DBIx::Class::Schema'; -our $VERSION = 5; +our $VERSION = 7; __PACKAGE__->load_namespaces; diff --git a/lib/Pear/LocalLoop/Schema/Result/Administrator.pm b/lib/Pear/LocalLoop/Schema/Result/Administrator.pm deleted file mode 100644 index 1fd2bfc..0000000 --- a/lib/Pear/LocalLoop/Schema/Result/Administrator.pm +++ /dev/null @@ -1,28 +0,0 @@ -package Pear::LocalLoop::Schema::Result::Administrator; - -use strict; -use warnings; - -use base 'DBIx::Class::Core'; - -__PACKAGE__->table("administrators"); - -__PACKAGE__->add_columns( - "user_id", - { - data_type => "integer", - is_foreign_key => 1, - is_nullable => 0, - }, -); - -__PACKAGE__->set_primary_key("user_id"); - -__PACKAGE__->belongs_to( - "user", - "Pear::LocalLoop::Schema::Result::User", - { id => "user_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - -1; diff --git a/lib/Pear/LocalLoop/Schema/Result/Customer.pm b/lib/Pear/LocalLoop/Schema/Result/Customer.pm index 4ce92e2..147f945 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Customer.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Customer.pm @@ -13,6 +13,11 @@ __PACKAGE__->add_columns( is_auto_increment => 1, is_nullable => 0, }, + "entity_id" => { + data_type => "integer", + is_nullable => 0, + is_foreign_key => 1, + }, "display_name" => { data_type => "varchar", size => 255, @@ -36,11 +41,10 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("id"); -__PACKAGE__->might_have( - "user", - "Pear::LocalLoop::Schema::Result::User", - { "foreign.customer_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, +__PACKAGE__->belongs_to( + "entity", + "Pear::LocalLoop::Schema::Result::Entity", + "entity_id", ); 1; diff --git a/lib/Pear/LocalLoop/Schema/Result/Entity.pm b/lib/Pear/LocalLoop/Schema/Result/Entity.pm new file mode 100644 index 0000000..0321049 --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/Result/Entity.pm @@ -0,0 +1,66 @@ +package Pear::LocalLoop::Schema::Result::Entity; + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +__PACKAGE__->table("entities"); + +__PACKAGE__->add_columns( + "id" => { + data_type => "integer", + is_auto_increment => 1, + is_nullable => 0, + }, + "type" => { + data_type => "varchar", + size => 255, + is_nullable => 0, + }, +); + +__PACKAGE__->set_primary_key("id"); + +__PACKAGE__->might_have( + "customer", + "Pear::LocalLoop::Schema::Result::Customer" => "entity_id", +); + +__PACKAGE__->might_have( + "organisation", + "Pear::LocalLoop::Schema::Result::Organisation" => "entity_id", +); + +__PACKAGE__->might_have( + "user", + "Pear::LocalLoop::Schema::Result::User" => "entity_id", +); + +__PACKAGE__->has_many( + "purchases", + "Pear::LocalLoop::Schema::Result::Transaction", + { "foreign.buyer_id" => "self.id" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + +__PACKAGE__->has_many( + "sales", + "Pear::LocalLoop::Schema::Result::Transaction", + { "foreign.seller_id" => "self.id" }, + { cascade_copy => 0, cascade_delete => 0 }, +); + +sub name { + my $self = shift; + + if ( $self->type eq 'customer' ) { + return $self->customer->display_name; + } elsif ( $self->type eq 'organisation' ) { + return $self->organisation->name; + } else { + return "Unknown Name"; + } +} + +1; diff --git a/lib/Pear/LocalLoop/Schema/Result/Feedback.pm b/lib/Pear/LocalLoop/Schema/Result/Feedback.pm index 7d990b6..3c8004d 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Feedback.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Feedback.pm @@ -14,7 +14,11 @@ __PACKAGE__->load_components(qw/ __PACKAGE__->add_columns( "id", - { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + { + data_type => "integer", + is_auto_increment => 1, + is_nullable => 0 + }, "user_id" => { data_type => "integer", is_foreign_key => 1, diff --git a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm index 480aaa4..7c122d7 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Leaderboard.pm @@ -67,8 +67,8 @@ sub create_new { sub _get_customer_rs { my $self = shift; - return $self->result_source->schema->resultset('User')->search({ - organisation_id => undef, + return $self->result_source->schema->resultset('Entity')->search({ + type => 'customer', }); } @@ -93,7 +93,7 @@ sub _set_position_and_trend { my $previous_value; if ( defined $previous_board ) { - $previous_value = $previous_board->find({ user_id => $lb_val->{user_id} }); + $previous_value = $previous_board->find({ entity_id => $lb_val->{entity_id} }); } my $trend; @@ -122,12 +122,12 @@ sub _create_total_set { my @leaderboard; while ( my $user_result = $user_rs->next ) { - my $transaction_rs = $user_result->transactions->search_between( $start, $end ); + my $transaction_rs = $user_result->purchases->search_between( $start, $end ); my $transaction_sum = $transaction_rs->get_column('value')->sum; push @leaderboard, { - user_id => $user_result->id, + entity_id => $user_result->id, value => $transaction_sum || 0, }; } @@ -153,12 +153,12 @@ sub _create_count_set { my @leaderboard; while ( my $user_result = $user_rs->next ) { - my $transaction_rs = $user_result->transactions->search_between( $start, $end ); + my $transaction_rs = $user_result->purchases->search_between( $start, $end ); my $transaction_count = $transaction_rs->count; push @leaderboard, { - user_id => $user_result->id, + entity_id => $user_result->id, value => $transaction_count || 0, }; } @@ -184,12 +184,12 @@ sub _create_total_all_time { my @leaderboard; while ( my $user_result = $user_rs->next ) { - my $transaction_rs = $user_result->transactions->search_before( $end ); + my $transaction_rs = $user_result->purchases->search_before( $end ); my $transaction_sum = $transaction_rs->get_column('value')->sum; push @leaderboard, { - user_id => $user_result->id, + entity_id => $user_result->id, value => $transaction_sum || 0, }; } @@ -215,12 +215,12 @@ sub _create_count_all_time { my @leaderboard; while ( my $user_result = $user_rs->next ) { - my $transaction_rs = $user_result->transactions->search_before( $end ); + my $transaction_rs = $user_result->purchases->search_before( $end ); my $transaction_count = $transaction_rs->count; push @leaderboard, { - user_id => $user_result->id, + entity_id => $user_result->id, value => $transaction_count || 0, }; } diff --git a/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm b/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm index 00833f4..8c08f1c 100644 --- a/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm +++ b/lib/Pear/LocalLoop/Schema/Result/LeaderboardValue.pm @@ -13,7 +13,7 @@ __PACKAGE__->add_columns( is_auto_increment => 1, is_nullable => 0, }, - "user_id" => { + "entity_id" => { data_type => "integer", is_foreign_key => 1, is_nullable => 0, @@ -28,8 +28,8 @@ __PACKAGE__->add_columns( is_nullable => 0, }, "value" => { - data_type => "decimal", - size => [ 16, 2 ], + data_type => "numeric", + size => [ 100, 0 ], is_nullable => 0, }, "trend" => { @@ -40,7 +40,7 @@ __PACKAGE__->add_columns( __PACKAGE__->set_primary_key("id"); -__PACKAGE__->add_unique_constraint([qw/ user_id set_id /]); +__PACKAGE__->add_unique_constraint([qw/ entity_id set_id /]); __PACKAGE__->belongs_to( "set", @@ -55,9 +55,9 @@ __PACKAGE__->belongs_to( ); __PACKAGE__->belongs_to( - "user", - "Pear::LocalLoop::Schema::Result::User", - { "foreign.id" => "self.user_id" }, + "entity", + "Pear::LocalLoop::Schema::Result::Entity", + { "foreign.id" => "self.entity_id" }, { is_deferrable => 0, join_type => "LEFT", diff --git a/lib/Pear/LocalLoop/Schema/Result/Organisation.pm b/lib/Pear/LocalLoop/Schema/Result/Organisation.pm index 5967644..7f6c4c8 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Organisation.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Organisation.pm @@ -5,7 +5,7 @@ use warnings; use base 'DBIx::Class::Core'; -__PACKAGE__->load_components("InflateColumn::DateTime"); +__PACKAGE__->load_components("InflateColumn::DateTime", "FilterColumn"); __PACKAGE__->table("organisations"); @@ -15,6 +15,11 @@ __PACKAGE__->add_columns( is_auto_increment => 1, is_nullable => 0, }, + entity_id => { + data_type => 'integer', + is_nullable => 0, + is_foreign_key => 1, + }, name => { data_type => 'varchar', size => 255, @@ -34,27 +39,58 @@ __PACKAGE__->add_columns( size => 16, is_nullable => 1, }, + country => { + data_type => 'varchar', + size => 255, + is_nullable => 1, + }, sector => { - data_type => "varchar", + data_type => 'varchar', size => 1, is_nullable => 1, }, + pending => { + data_type => 'boolean', + default => \"false", + is_nullable => 0, + }, + submitted_by_id => { + data_type => 'integer', + is_nullable => 1, + }, ); __PACKAGE__->set_primary_key('id'); -__PACKAGE__->has_many( - "transactions", - "Pear::LocalLoop::Schema::Result::Transaction", - { "foreign.seller_id" => 'self.id' }, - { cascade_copy => 0, cascade_delete => 0 }, +__PACKAGE__->belongs_to( + "entity", + "Pear::LocalLoop::Schema::Result::Entity", + "entity_id", ); -__PACKAGE__->might_have( - "user", - "Pear::LocalLoop::Schema::Result::User", - { "foreign.organisation_id" => 'self.id' }, - { cascade_copy => 0, cascade_delete => 0 }, -); +__PACKAGE__->filter_column( pending => { + filter_to_storage => 'to_bool', +}); + +# Only works when calling ->deploy, but atleast helps for tests +sub sqlt_deploy_hook { + my ( $source_instance, $sqlt_table ) = @_; + my $pending_field = $sqlt_table->get_field('pending'); + if ( $sqlt_table->schema->translator->producer_type =~ /SQLite$/ ) { + $pending_field->{default_value} = 0; + } else { + $pending_field->{default_value} = \"false"; + } +} + +sub to_bool { + my ( $self, $val ) = @_; + my $driver_name = $self->result_source->schema->storage->dbh->{Driver}->{Name}; + if ( $driver_name eq 'SQLite' ) { + return $val ? 1 : 0; + } else { + return $val ? 'true' : 'false'; + } +} 1; diff --git a/lib/Pear/LocalLoop/Schema/Result/PendingOrganisation.pm b/lib/Pear/LocalLoop/Schema/Result/PendingOrganisation.pm deleted file mode 100644 index 5cf1f51..0000000 --- a/lib/Pear/LocalLoop/Schema/Result/PendingOrganisation.pm +++ /dev/null @@ -1,70 +0,0 @@ -package Pear::LocalLoop::Schema::Result::PendingOrganisation; - -use strict; -use warnings; - -use base 'DBIx::Class::Core'; - -__PACKAGE__->load_components( qw/ - InflateColumn::DateTime - TimeStamp -/); - -__PACKAGE__->table("pending_organisations"); - -__PACKAGE__->add_columns( - id => { - data_type => 'integer', - is_auto_increment => 1, - is_nullable => 0, - }, - name => { - data_type => 'varchar', - size => 255, - is_nullable => 0, - }, - street_name => { - data_type => 'text', - is_nullable => 1, - }, - town => { - data_type => 'varchar', - size => 255, - is_nullable => 0, - }, - postcode => { - data_type => 'varchar', - size => 16, - is_nullable => 1, - }, - submitted_by_id => { - data_type => "integer", - is_foreign_key => 1, - is_nullable => 0, - }, - submitted_at => { - data_type => "datetime", - is_nullable => 0, - set_on_create => 1, - }, -); - -__PACKAGE__->set_primary_key('id'); - -__PACKAGE__->has_many( - "transactions", - "Pear::LocalLoop::Schema::Result::PendingTransaction", - { - "foreign.seller_id" => "self.id", - }, - { cascade_copy => 0, cascade_delete => 1 }, -); - -__PACKAGE__->belongs_to( - "submitted_by", - "Pear::LocalLoop::Schema::Result::User", - { id => "submitted_by_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - -1; diff --git a/lib/Pear/LocalLoop/Schema/Result/PendingTransaction.pm b/lib/Pear/LocalLoop/Schema/Result/PendingTransaction.pm deleted file mode 100644 index 5637181..0000000 --- a/lib/Pear/LocalLoop/Schema/Result/PendingTransaction.pm +++ /dev/null @@ -1,69 +0,0 @@ -package Pear::LocalLoop::Schema::Result::PendingTransaction; - -use strict; -use warnings; - -use base 'DBIx::Class::Core'; - -__PACKAGE__->load_components( qw/ - InflateColumn::DateTime - TimeStamp -/); - -__PACKAGE__->table("pending_transactions"); - -__PACKAGE__->add_columns( - "id" => { - data_type => "integer", - is_auto_increment => 1, - is_nullable => 0, - }, - "buyer_id" => { - data_type => "integer", - is_foreign_key => 1, - is_nullable => 0, - }, - "seller_id" => { - data_type => "integer", - is_foreign_key => 1, - is_nullable => 0, - }, - "value" => { - data_type => "decimal", - size => [ 16, 2 ], - is_nullable => 0, - }, - "proof_image" => { - data_type => "text", - is_nullable => 0, - }, - "submitted_at" => { - data_type => "datetime", - is_nullable => 0, - set_on_create => 1, - }, - "purchase_time" => { - data_type => "datetime", - timezone => "UTC", - is_nullable => 0, - set_on_create => 1, - }, -); - -__PACKAGE__->set_primary_key("id"); - -__PACKAGE__->belongs_to( - "buyer", - "Pear::LocalLoop::Schema::Result::User", - { id => "buyer_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - -__PACKAGE__->belongs_to( - "seller", - "Pear::LocalLoop::Schema::Result::PendingOrganisation", - { id => "seller_id" }, - { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, -); - -1; diff --git a/lib/Pear/LocalLoop/Schema/Result/Transaction.pm b/lib/Pear/LocalLoop/Schema/Result/Transaction.pm index 6ac0223..efdac85 100644 --- a/lib/Pear/LocalLoop/Schema/Result/Transaction.pm +++ b/lib/Pear/LocalLoop/Schema/Result/Transaction.pm @@ -29,13 +29,13 @@ __PACKAGE__->add_columns( is_nullable => 0, }, "value" => { - data_type => "decimal", - size => [ 16, 2 ], + data_type => "numeric", + size => [ 100, 0 ], is_nullable => 0, }, "proof_image" => { data_type => "text", - is_nullable => 0, + is_nullable => 1, }, "submitted_at" => { data_type => "datetime", @@ -54,14 +54,14 @@ __PACKAGE__->set_primary_key("id"); __PACKAGE__->belongs_to( "buyer", - "Pear::LocalLoop::Schema::Result::User", + "Pear::LocalLoop::Schema::Result::Entity", { id => "buyer_id" }, { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); __PACKAGE__->belongs_to( "seller", - "Pear::LocalLoop::Schema::Result::Organisation", + "Pear::LocalLoop::Schema::Result::Entity", { id => "seller_id" }, { is_deferrable => 0, on_delete => "NO ACTION", on_update => "NO ACTION" }, ); diff --git a/lib/Pear/LocalLoop/Schema/Result/User.pm b/lib/Pear/LocalLoop/Schema/Result/User.pm index 75a1dde..162fa12 100644 --- a/lib/Pear/LocalLoop/Schema/Result/User.pm +++ b/lib/Pear/LocalLoop/Schema/Result/User.pm @@ -11,6 +11,7 @@ __PACKAGE__->load_components( qw/ InflateColumn::DateTime PassphraseColumn TimeStamp + FilterColumn /); __PACKAGE__->table("users"); @@ -21,15 +22,10 @@ __PACKAGE__->add_columns( is_auto_increment => 1, is_nullable => 0, }, - "customer_id" => { + "entity_id" => { data_type => "integer", is_foreign_key => 1, - is_nullable => 1, - }, - "organisation_id" => { - data_type => "integer", - is_foreign_key => 1, - is_nullable => 1, + is_nullable => 0, }, "email" => { data_type => "text", @@ -51,59 +47,21 @@ __PACKAGE__->add_columns( }, passphrase_check_method => 'check_password', }, + "is_admin" => { + data_type => "boolean", + default_value => \"false", + is_nullable => 0, + }, ); __PACKAGE__->set_primary_key("id"); -__PACKAGE__->add_unique_constraint(["customer_id"]); - __PACKAGE__->add_unique_constraint(["email"]); -__PACKAGE__->add_unique_constraint(["organisation_id"]); - -__PACKAGE__->might_have( - "administrator", - "Pear::LocalLoop::Schema::Result::Administrator", - { "foreign.user_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, -); - __PACKAGE__->belongs_to( - "customer", - "Pear::LocalLoop::Schema::Result::Customer", - { "foreign.id" => "self.customer_id" }, - { - is_deferrable => 0, - join_type => "LEFT", - on_delete => "NO ACTION", - on_update => "NO ACTION", - }, -); - -__PACKAGE__->belongs_to( - "organisation", - "Pear::LocalLoop::Schema::Result::Organisation", - { "foreign.id" => "self.organisation_id" }, - { - is_deferrable => 0, - join_type => "LEFT", - on_delete => "NO ACTION", - on_update => "NO ACTION", - }, -); - -__PACKAGE__->has_many( - "pending_organisations", - "Pear::LocalLoop::Schema::Result::PendingOrganisation", - { "foreign.submitted_by_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, -); - -__PACKAGE__->has_many( - "pending_transactions", - "Pear::LocalLoop::Schema::Result::PendingTransaction", - { "foreign.buyer_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, + "entity", + "Pear::LocalLoop::Schema::Result::Entity", + "entity_id", ); __PACKAGE__->has_many( @@ -113,13 +71,6 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -__PACKAGE__->has_many( - "transactions", - "Pear::LocalLoop::Schema::Result::Transaction", - { "foreign.buyer_id" => "self.id" }, - { cascade_copy => 0, cascade_delete => 0 }, -); - __PACKAGE__->has_many( "feedback", "Pear::LocalLoop::Schema::Result::Feedback", @@ -127,6 +78,30 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); +sub sqlt_deploy_hook { + my ( $source_instance, $sqlt_table ) = @_; + my $pending_field = $sqlt_table->get_field('is_admin'); + if ( $sqlt_table->schema->translator->producer_type =~ /SQLite$/ ) { + $pending_field->{default_value} = 0; + } else { + $pending_field->{default_value} = \"false"; + } +} + +__PACKAGE__->filter_column( is_admin => { + filter_to_storage => 'to_bool', +}); + +sub to_bool { + my ( $self, $val ) = @_; + my $driver_name = $self->result_source->schema->storage->dbh->{Driver}->{Name}; + if ( $driver_name eq 'SQLite' ) { + return $val ? 1 : 0; + } else { + return $val ? 'true' : 'false'; + } +} + sub generate_session { my $self = shift; @@ -144,22 +119,20 @@ sub generate_session { sub name { my $self = shift; - if ( defined $self->customer_id ) { - return $self->customer->display_name; - } elsif ( defined $self->organisation_id ) { - return $self->organisation->name; + if ( defined $self->entity->customer ) { + return $self->entity->customer->display_name; + } elsif ( defined $self->entity->organisation ) { + return $self->entity->organisation->name; } else { return; } } +# TODO Deprecate this sub? sub type { my $self = shift; - if ( defined $self->customer_id ) { - return "customer"; - } - return "organisation"; + return $self->entity->type; } 1; diff --git a/lib/Pear/LocalLoop/Schema/ResultSet/Entity.pm b/lib/Pear/LocalLoop/Schema/ResultSet/Entity.pm new file mode 100644 index 0000000..59fd059 --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/ResultSet/Entity.pm @@ -0,0 +1,19 @@ +package Pear::LocalLoop::Schema::ResultSet::Entity; + +use strict; +use warnings; + +use base 'DBIx::Class::ResultSet'; + +sub sales { shift->search_related('sales', @_) } + +sub create_org { + my ( $self, $org ) = @_; + + return $self->create({ + organisation => $org, + type => 'organisation', + }); +} + +1; diff --git a/lib/Pear/LocalLoop/Schema/ResultSet/Organisation.pm b/lib/Pear/LocalLoop/Schema/ResultSet/Organisation.pm new file mode 100644 index 0000000..10560b2 --- /dev/null +++ b/lib/Pear/LocalLoop/Schema/ResultSet/Organisation.pm @@ -0,0 +1,10 @@ +package Pear::LocalLoop::Schema::ResultSet::Organisation; + +use strict; +use warnings; + +use base 'DBIx::Class::ResultSet'; + +sub entity { shift->search_related('entity', @_) } + +1; diff --git a/lib/Test/Pear/LocalLoop.pm b/lib/Test/Pear/LocalLoop.pm index 5fb6fae..961fd55 100644 --- a/lib/Test/Pear/LocalLoop.pm +++ b/lib/Test/Pear/LocalLoop.pm @@ -1,5 +1,5 @@ package Test::Pear::LocalLoop; -use Mojo::Base -base; +use Moo; use Test::More; use File::Temp; @@ -7,12 +7,48 @@ use Test::Mojo; use DateTime::Format::Strptime; use DBIx::Class::Fixtures; -has config => sub { +# Conditionally require Test::PostgreSQL +sub BUILD { + if ( $ENV{PEAR_TEST_PG} ) { + require Test::PostgreSQL + or die "you need Test::PostgreSQL to run PG testing"; + Test::PostgreSQL->import; + } +} + +sub DEMOLISH { + my ( $self, $in_global_destruction ) = @_; + + if ( $ENV{PEAR_TEST_PG} && !$in_global_destruction ) { + $self->mojo->app->schema->storage->dbh->disconnect; + $self->pg->stop; + } +} + +has pg => ( + is => 'lazy', + builder => sub { + return Test::PostgreSQL->new(); + }, +); + +has config => ( + is => 'lazy', + builder => sub { + my $self = shift; my $file = File::Temp->new; - print $file <<'END'; + my $dsn; + + if ( $ENV{PEAR_TEST_PG} ) { + $dsn = $self->pg->dsn; + } else { + $dsn = "dbi:SQLite::memory:"; + } + + print $file <<"END"; { - dsn => "dbi:SQLite::memory:", + dsn => "$dsn", user => undef, pass => undef, } @@ -20,9 +56,12 @@ END $file->seek( 0, SEEK_END ); return $file; -}; + }, +); -has mojo => sub { +has mojo => ( + is => 'lazy', + builder => sub { my $self = shift; $ENV{MOJO_CONFIG} = $self->config->filename; @@ -31,9 +70,18 @@ has mojo => sub { $t->app->schema->deploy; return $t; -}; + }, +); -has _deployed => sub { 0 }; +has etc_dir => ( + is => 'lazy', + builder => sub { die "etc dir not set" }, +); + +has _deployed => ( + is => 'rwp', + default => 0, +); sub framework { my $self = shift; @@ -56,13 +104,11 @@ sub framework { ]); } - $self->_deployed(1); + $self->_set__deployed(1); return $t; }; -has etc_dir => sub { die "etc dir not set" }; - sub dump_error { return sub { my $self = shift; @@ -142,11 +188,40 @@ sub install_fixtures { }); my $t = $self->framework(1); + my $schema = $t->app->schema; + $fixtures->populate({ directory => File::Spec->catdir( $self->etc_dir, 'fixtures', 'data', $fixture_name ), no_deploy => 1, - schema => $t->app->schema, + schema => $schema, }); + + # Reset table id sequences + if ( $ENV{PEAR_TEST_PG} ) { + $schema->storage->dbh_do( + sub { + my ( $storage, $dbh, $sets ) = @_; + for my $table ( keys %$sets ) { + my $seq = $sets->{$table}; + $dbh->do( + qq/ + SELECT setval( + '$seq', + COALESCE( + (SELECT MAX(id)+1 FROM $table), + 1 + ), + false + ); + /); + } + }, + { + entities => 'entities_id_seq', + organisations => 'organisations_id_seq', + } + ); + } } 1; diff --git a/public/image/no_transaction.jpg b/public/image/no_transaction.jpg new file mode 100644 index 0000000..8c2e148 Binary files /dev/null and b/public/image/no_transaction.jpg differ diff --git a/script/recalc_leaderboards b/script/recalc_leaderboards old mode 100644 new mode 100755 diff --git a/script/schema/graph.pl b/script/schema/graph.pl new file mode 100755 index 0000000..f51921c --- /dev/null +++ b/script/schema/graph.pl @@ -0,0 +1,36 @@ +#! /usr/bin/env perl +use strict; +use warnings; + +use feature "say"; + +use FindBin qw/ $Bin /; +use lib "$Bin/../../lib"; + +use SQL::Translator; +use Pear::LocalLoop::Schema; + +say "Setting up Translator and Schema"; + +my $schema = Pear::LocalLoop::Schema->connect; +my $tr = SQL::Translator->new( + from => "SQL::Translator::Parser::DBIx::Class", + to => 'GraphViz', + debug => 1, + trace => 1, + producer_args => { + out_file => "$Bin/../../schema.png", + output_type => 'png', + width => 0, + height => 0, + show_constraints => 1, + show_datatypes => 1, + show_sizes => 1, + }, +); + +say "Translating Schema to image"; + +$tr->translate( data => $schema ); + +say "Finished"; diff --git a/share/ddl/PostgreSQL/deploy/6/001-auto-__VERSION.sql b/share/ddl/PostgreSQL/deploy/6/001-auto-__VERSION.sql new file mode 100644 index 0000000..ab9b4e1 --- /dev/null +++ b/share/ddl/PostgreSQL/deploy/6/001-auto-__VERSION.sql @@ -0,0 +1,18 @@ +-- +-- Created by SQL::Translator::Producer::PostgreSQL +-- Created on Fri Sep 1 15:14:28 2017 +-- +; +-- +-- Table: dbix_class_deploymenthandler_versions +-- +CREATE TABLE "dbix_class_deploymenthandler_versions" ( + "id" serial NOT NULL, + "version" character varying(50) NOT NULL, + "ddl" text, + "upgrade_sql" text, + PRIMARY KEY ("id"), + CONSTRAINT "dbix_class_deploymenthandler_versions_version" UNIQUE ("version") +); + +; diff --git a/share/ddl/PostgreSQL/deploy/6/001-auto.sql b/share/ddl/PostgreSQL/deploy/6/001-auto.sql new file mode 100644 index 0000000..21223b0 --- /dev/null +++ b/share/ddl/PostgreSQL/deploy/6/001-auto.sql @@ -0,0 +1,210 @@ +-- +-- Created by SQL::Translator::Producer::PostgreSQL +-- Created on Tue Sep 5 17:22:16 2017 +-- +; +-- +-- Table: account_tokens +-- +CREATE TABLE "account_tokens" ( + "id" serial NOT NULL, + "name" text NOT NULL, + "used" integer DEFAULT 0 NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "account_tokens_name" UNIQUE ("name") +); + +; +-- +-- Table: entities +-- +CREATE TABLE "entities" ( + "id" serial NOT NULL, + "type" character varying(255) NOT NULL, + PRIMARY KEY ("id") +); + +; +-- +-- Table: leaderboards +-- +CREATE TABLE "leaderboards" ( + "id" serial NOT NULL, + "name" character varying(255) NOT NULL, + "type" character varying(255) NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "leaderboards_type" UNIQUE ("type") +); + +; +-- +-- Table: customers +-- +CREATE TABLE "customers" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "display_name" character varying(255) NOT NULL, + "full_name" character varying(255) NOT NULL, + "year_of_birth" integer NOT NULL, + "postcode" character varying(16) NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "customers_idx_entity_id" on "customers" ("entity_id"); + +; +-- +-- Table: leaderboard_sets +-- +CREATE TABLE "leaderboard_sets" ( + "id" serial NOT NULL, + "leaderboard_id" integer NOT NULL, + "date" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "leaderboard_sets_idx_leaderboard_id" on "leaderboard_sets" ("leaderboard_id"); + +; +-- +-- Table: organisations +-- +CREATE TABLE "organisations" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "name" character varying(255) NOT NULL, + "street_name" text, + "town" character varying(255) NOT NULL, + "postcode" character varying(16), + "country" character varying(255), + "sector" character varying(1), + "pending" boolean DEFAULT false NOT NULL, + "submitted_by_id" integer, + PRIMARY KEY ("id") +); +CREATE INDEX "organisations_idx_entity_id" on "organisations" ("entity_id"); + +; +-- +-- Table: transactions +-- +CREATE TABLE "transactions" ( + "id" serial NOT NULL, + "buyer_id" integer NOT NULL, + "seller_id" integer NOT NULL, + "value" numeric(16,2) NOT NULL, + "proof_image" text, + "submitted_at" timestamp NOT NULL, + "purchase_time" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "transactions_idx_buyer_id" on "transactions" ("buyer_id"); +CREATE INDEX "transactions_idx_seller_id" on "transactions" ("seller_id"); + +; +-- +-- Table: users +-- +CREATE TABLE "users" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "email" text NOT NULL, + "join_date" timestamp NOT NULL, + "password" character varying(100) NOT NULL, + "is_admin" boolean DEFAULT false NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "users_email" UNIQUE ("email") +); +CREATE INDEX "users_idx_entity_id" on "users" ("entity_id"); + +; +-- +-- Table: feedback +-- +CREATE TABLE "feedback" ( + "id" serial NOT NULL, + "user_id" integer NOT NULL, + "submitted_at" timestamp NOT NULL, + "feedbacktext" text NOT NULL, + "app_name" character varying(255) NOT NULL, + "package_name" character varying(255) NOT NULL, + "version_code" character varying(255) NOT NULL, + "version_number" character varying(255) NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "feedback_idx_user_id" on "feedback" ("user_id"); + +; +-- +-- Table: session_tokens +-- +CREATE TABLE "session_tokens" ( + "id" serial NOT NULL, + "token" character varying(255) NOT NULL, + "user_id" integer NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "session_tokens_token" UNIQUE ("token") +); +CREATE INDEX "session_tokens_idx_user_id" on "session_tokens" ("user_id"); + +; +-- +-- Table: leaderboard_values +-- +CREATE TABLE "leaderboard_values" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "set_id" integer NOT NULL, + "position" integer NOT NULL, + "value" numeric(16,2) NOT NULL, + "trend" integer DEFAULT 0 NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "leaderboard_values_entity_id_set_id" UNIQUE ("entity_id", "set_id") +); +CREATE INDEX "leaderboard_values_idx_entity_id" on "leaderboard_values" ("entity_id"); +CREATE INDEX "leaderboard_values_idx_set_id" on "leaderboard_values" ("set_id"); + +; +-- +-- Foreign Key Definitions +-- + +; +ALTER TABLE "customers" ADD CONSTRAINT "customers_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE CASCADE DEFERRABLE; + +; +ALTER TABLE "leaderboard_sets" ADD CONSTRAINT "leaderboard_sets_fk_leaderboard_id" FOREIGN KEY ("leaderboard_id") + REFERENCES "leaderboards" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "organisations" ADD CONSTRAINT "organisations_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE CASCADE DEFERRABLE; + +; +ALTER TABLE "transactions" ADD CONSTRAINT "transactions_fk_buyer_id" FOREIGN KEY ("buyer_id") + REFERENCES "entities" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "transactions" ADD CONSTRAINT "transactions_fk_seller_id" FOREIGN KEY ("seller_id") + REFERENCES "entities" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "users" ADD CONSTRAINT "users_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE CASCADE DEFERRABLE; + +; +ALTER TABLE "feedback" ADD CONSTRAINT "feedback_fk_user_id" FOREIGN KEY ("user_id") + REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "session_tokens" ADD CONSTRAINT "session_tokens_fk_user_id" FOREIGN KEY ("user_id") + REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "leaderboard_values" ADD CONSTRAINT "leaderboard_values_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "leaderboard_values" ADD CONSTRAINT "leaderboard_values_fk_set_id" FOREIGN KEY ("set_id") + REFERENCES "leaderboard_sets" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; diff --git a/share/ddl/PostgreSQL/deploy/7/001-auto-__VERSION.sql b/share/ddl/PostgreSQL/deploy/7/001-auto-__VERSION.sql new file mode 100644 index 0000000..3a15182 --- /dev/null +++ b/share/ddl/PostgreSQL/deploy/7/001-auto-__VERSION.sql @@ -0,0 +1,18 @@ +-- +-- Created by SQL::Translator::Producer::PostgreSQL +-- Created on Wed Sep 13 15:24:20 2017 +-- +; +-- +-- Table: dbix_class_deploymenthandler_versions +-- +CREATE TABLE "dbix_class_deploymenthandler_versions" ( + "id" serial NOT NULL, + "version" character varying(50) NOT NULL, + "ddl" text, + "upgrade_sql" text, + PRIMARY KEY ("id"), + CONSTRAINT "dbix_class_deploymenthandler_versions_version" UNIQUE ("version") +); + +; diff --git a/share/ddl/PostgreSQL/deploy/7/001-auto.sql b/share/ddl/PostgreSQL/deploy/7/001-auto.sql new file mode 100644 index 0000000..46d7045 --- /dev/null +++ b/share/ddl/PostgreSQL/deploy/7/001-auto.sql @@ -0,0 +1,210 @@ +-- +-- Created by SQL::Translator::Producer::PostgreSQL +-- Created on Wed Sep 13 15:24:20 2017 +-- +; +-- +-- Table: account_tokens +-- +CREATE TABLE "account_tokens" ( + "id" serial NOT NULL, + "name" text NOT NULL, + "used" integer DEFAULT 0 NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "account_tokens_name" UNIQUE ("name") +); + +; +-- +-- Table: entities +-- +CREATE TABLE "entities" ( + "id" serial NOT NULL, + "type" character varying(255) NOT NULL, + PRIMARY KEY ("id") +); + +; +-- +-- Table: leaderboards +-- +CREATE TABLE "leaderboards" ( + "id" serial NOT NULL, + "name" character varying(255) NOT NULL, + "type" character varying(255) NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "leaderboards_type" UNIQUE ("type") +); + +; +-- +-- Table: customers +-- +CREATE TABLE "customers" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "display_name" character varying(255) NOT NULL, + "full_name" character varying(255) NOT NULL, + "year_of_birth" integer NOT NULL, + "postcode" character varying(16) NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "customers_idx_entity_id" on "customers" ("entity_id"); + +; +-- +-- Table: leaderboard_sets +-- +CREATE TABLE "leaderboard_sets" ( + "id" serial NOT NULL, + "leaderboard_id" integer NOT NULL, + "date" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "leaderboard_sets_idx_leaderboard_id" on "leaderboard_sets" ("leaderboard_id"); + +; +-- +-- Table: organisations +-- +CREATE TABLE "organisations" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "name" character varying(255) NOT NULL, + "street_name" text, + "town" character varying(255) NOT NULL, + "postcode" character varying(16), + "country" character varying(255), + "sector" character varying(1), + "pending" boolean DEFAULT false NOT NULL, + "submitted_by_id" integer, + PRIMARY KEY ("id") +); +CREATE INDEX "organisations_idx_entity_id" on "organisations" ("entity_id"); + +; +-- +-- Table: transactions +-- +CREATE TABLE "transactions" ( + "id" serial NOT NULL, + "buyer_id" integer NOT NULL, + "seller_id" integer NOT NULL, + "value" numeric(100,0) NOT NULL, + "proof_image" text, + "submitted_at" timestamp NOT NULL, + "purchase_time" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "transactions_idx_buyer_id" on "transactions" ("buyer_id"); +CREATE INDEX "transactions_idx_seller_id" on "transactions" ("seller_id"); + +; +-- +-- Table: users +-- +CREATE TABLE "users" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "email" text NOT NULL, + "join_date" timestamp NOT NULL, + "password" character varying(100) NOT NULL, + "is_admin" boolean DEFAULT false NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "users_email" UNIQUE ("email") +); +CREATE INDEX "users_idx_entity_id" on "users" ("entity_id"); + +; +-- +-- Table: feedback +-- +CREATE TABLE "feedback" ( + "id" serial NOT NULL, + "user_id" integer NOT NULL, + "submitted_at" timestamp NOT NULL, + "feedbacktext" text NOT NULL, + "app_name" character varying(255) NOT NULL, + "package_name" character varying(255) NOT NULL, + "version_code" character varying(255) NOT NULL, + "version_number" character varying(255) NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "feedback_idx_user_id" on "feedback" ("user_id"); + +; +-- +-- Table: session_tokens +-- +CREATE TABLE "session_tokens" ( + "id" serial NOT NULL, + "token" character varying(255) NOT NULL, + "user_id" integer NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "session_tokens_token" UNIQUE ("token") +); +CREATE INDEX "session_tokens_idx_user_id" on "session_tokens" ("user_id"); + +; +-- +-- Table: leaderboard_values +-- +CREATE TABLE "leaderboard_values" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "set_id" integer NOT NULL, + "position" integer NOT NULL, + "value" numeric(100,0) NOT NULL, + "trend" integer DEFAULT 0 NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "leaderboard_values_entity_id_set_id" UNIQUE ("entity_id", "set_id") +); +CREATE INDEX "leaderboard_values_idx_entity_id" on "leaderboard_values" ("entity_id"); +CREATE INDEX "leaderboard_values_idx_set_id" on "leaderboard_values" ("set_id"); + +; +-- +-- Foreign Key Definitions +-- + +; +ALTER TABLE "customers" ADD CONSTRAINT "customers_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE CASCADE DEFERRABLE; + +; +ALTER TABLE "leaderboard_sets" ADD CONSTRAINT "leaderboard_sets_fk_leaderboard_id" FOREIGN KEY ("leaderboard_id") + REFERENCES "leaderboards" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "organisations" ADD CONSTRAINT "organisations_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE CASCADE DEFERRABLE; + +; +ALTER TABLE "transactions" ADD CONSTRAINT "transactions_fk_buyer_id" FOREIGN KEY ("buyer_id") + REFERENCES "entities" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "transactions" ADD CONSTRAINT "transactions_fk_seller_id" FOREIGN KEY ("seller_id") + REFERENCES "entities" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "users" ADD CONSTRAINT "users_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE CASCADE DEFERRABLE; + +; +ALTER TABLE "feedback" ADD CONSTRAINT "feedback_fk_user_id" FOREIGN KEY ("user_id") + REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "session_tokens" ADD CONSTRAINT "session_tokens_fk_user_id" FOREIGN KEY ("user_id") + REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "leaderboard_values" ADD CONSTRAINT "leaderboard_values_fk_entity_id" FOREIGN KEY ("entity_id") + REFERENCES "entities" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; +ALTER TABLE "leaderboard_values" ADD CONSTRAINT "leaderboard_values_fk_set_id" FOREIGN KEY ("set_id") + REFERENCES "leaderboard_sets" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +; diff --git a/share/ddl/PostgreSQL/upgrade/5-6/001-auto.sql b/share/ddl/PostgreSQL/upgrade/5-6/001-auto.sql new file mode 100644 index 0000000..7b6d991 --- /dev/null +++ b/share/ddl/PostgreSQL/upgrade/5-6/001-auto.sql @@ -0,0 +1,120 @@ +-- Convert schema 'share/ddl/_source/deploy/5/001-auto.yml' to 'share/ddl/_source/deploy/6/001-auto.yml':; +-- Customised for proper migration + +BEGIN; + +CREATE TABLE "entities" ( + "id" serial NOT NULL, + "type" character varying(255) NOT NULL, + PRIMARY KEY ("id") +); + +ALTER TABLE customers RENAME TO customers_temp; +ALTER TABLE organisations RENAME TO organisations_temp; +ALTER TABLE transactions RENAME TO transactions_temp; +ALTER TABLE users RENAME TO users_temp; +ALTER TABLE session_tokens RENAME TO session_tokens_temp; +ALTER TABLE feedback RENAME TO feedback_temp; + +ALTER INDEX transactions_idx_buyer_id RENAME TO transactions_temp_idx_buyer_id; +ALTER INDEX transactions_idx_seller_id RENAME TO transactions_temp_idx_seller_id; +ALTER INDEX session_tokens_idx_user_id RENAME TO session_tokens_temp_idx_user_id; +ALTER INDEX feedback_idx_user_id RENAME TO feedback_temp_idx_user_id; + +ALTER TABLE session_tokens_temp DROP CONSTRAINT session_tokens_token; +ALTER TABLE users_temp DROP CONSTRAINT users_email; + +CREATE TABLE "customers" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "display_name" character varying(255) NOT NULL, + "full_name" character varying(255) NOT NULL, + "year_of_birth" integer NOT NULL, + "postcode" character varying(16) NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "customers_idx_entity_id" on "customers" ("entity_id"); + +CREATE TABLE "organisations" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "name" character varying(255) NOT NULL, + "street_name" text, + "town" character varying(255) NOT NULL, + "postcode" character varying(16), + "country" character varying(255), + "sector" character varying(1), + "pending" boolean DEFAULT false NOT NULL, + "submitted_by_id" integer, + PRIMARY KEY ("id") +); +CREATE INDEX "organisations_idx_entity_id" on "organisations" ("entity_id"); + +CREATE TABLE "transactions" ( + "id" serial NOT NULL, + "buyer_id" integer NOT NULL, + "seller_id" integer NOT NULL, + "value" numeric(16,2) NOT NULL, + "proof_image" text, + "submitted_at" timestamp NOT NULL, + "purchase_time" timestamp NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "transactions_idx_buyer_id" on "transactions" ("buyer_id"); +CREATE INDEX "transactions_idx_seller_id" on "transactions" ("seller_id"); + +CREATE TABLE "users" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "email" text NOT NULL, + "join_date" timestamp NOT NULL, + "password" character varying(100) NOT NULL, + "is_admin" boolean DEFAULT false NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "users_email" UNIQUE ("email") +); +CREATE INDEX "users_idx_entity_id" on "users" ("entity_id"); + +-- Recreate session table as users is changing completely +CREATE TABLE "session_tokens" ( + "id" serial NOT NULL, + "token" character varying(255) NOT NULL, + "user_id" integer NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "session_tokens_token" UNIQUE ("token") +); +CREATE INDEX "session_tokens_idx_user_id" on "session_tokens" ("user_id"); + +-- Also recreate feedback as this also gets broken by the user_id changes +CREATE TABLE "feedback" ( + "id" serial NOT NULL, + "user_id" integer NOT NULL, + "submitted_at" timestamp NOT NULL, + "feedbacktext" text NOT NULL, + "app_name" character varying(255) NOT NULL, + "package_name" character varying(255) NOT NULL, + "version_code" character varying(255) NOT NULL, + "version_number" character varying(255) NOT NULL, + PRIMARY KEY ("id") +); +CREATE INDEX "feedback_idx_user_id" on "feedback" ("user_id"); + +DROP TABLE leaderboard_values; +TRUNCATE TABLE leaderboard_sets; + +CREATE TABLE "leaderboard_values" ( + "id" serial NOT NULL, + "entity_id" integer NOT NULL, + "set_id" integer NOT NULL, + "position" integer NOT NULL, + "value" numeric(16,2) NOT NULL, + "trend" integer DEFAULT 0 NOT NULL, + PRIMARY KEY ("id"), + CONSTRAINT "leaderboard_values_entity_id_set_id" UNIQUE ("entity_id", "set_id") +); +CREATE INDEX "leaderboard_values_idx_entity_id" on "leaderboard_values" ("entity_id"); +CREATE INDEX "leaderboard_values_idx_set_id" on "leaderboard_values" ("set_id"); + + +COMMIT; + diff --git a/share/ddl/PostgreSQL/upgrade/5-6/003-remove-temps.sql b/share/ddl/PostgreSQL/upgrade/5-6/003-remove-temps.sql new file mode 100644 index 0000000..f86be1c --- /dev/null +++ b/share/ddl/PostgreSQL/upgrade/5-6/003-remove-temps.sql @@ -0,0 +1,20 @@ +-- Remove temporary tables created during migration + +BEGIN; + +DROP INDEX transactions_temp_idx_buyer_id; +DROP INDEX transactions_temp_idx_seller_id; +DROP INDEX session_tokens_temp_idx_user_id; +DROP INDEX feedback_temp_idx_user_id; + +DROP TABLE transactions_temp; +DROP TABLE session_tokens_temp; +DROP TABLE feedback_temp; +DROP TABLE pending_transactions; +DROP TABLE administrators; +DROP TABLE pending_organisations; +DROP TABLE users_temp; +DROP TABLE customers_temp; +DROP TABLE organisations_temp; + +COMMIT; diff --git a/share/ddl/PostgreSQL/upgrade/6-7/001-auto.sql b/share/ddl/PostgreSQL/upgrade/6-7/001-auto.sql new file mode 100644 index 0000000..4b30c31 --- /dev/null +++ b/share/ddl/PostgreSQL/upgrade/6-7/001-auto.sql @@ -0,0 +1,15 @@ +-- Convert schema 'share/ddl/_source/deploy/6/001-auto.yml' to 'share/ddl/_source/deploy/7/001-auto.yml':; + +; +BEGIN; + +; +ALTER TABLE leaderboard_values ALTER COLUMN value TYPE numeric(100,0) USING value * 100000; + +; +ALTER TABLE transactions ALTER COLUMN value TYPE numeric(100,0) USING value * 100000; + +; + +COMMIT; + diff --git a/share/ddl/SQLite/deploy/6/001-auto-__VERSION.sql b/share/ddl/SQLite/deploy/6/001-auto-__VERSION.sql new file mode 100644 index 0000000..d3f4167 --- /dev/null +++ b/share/ddl/SQLite/deploy/6/001-auto-__VERSION.sql @@ -0,0 +1,18 @@ +-- +-- Created by SQL::Translator::Producer::SQLite +-- Created on Fri Sep 1 15:14:28 2017 +-- + +; +BEGIN TRANSACTION; +-- +-- Table: dbix_class_deploymenthandler_versions +-- +CREATE TABLE dbix_class_deploymenthandler_versions ( + id INTEGER PRIMARY KEY NOT NULL, + version varchar(50) NOT NULL, + ddl text, + upgrade_sql text +); +CREATE UNIQUE INDEX dbix_class_deploymenthandler_versions_version ON dbix_class_deploymenthandler_versions (version); +COMMIT; diff --git a/share/ddl/SQLite/deploy/6/001-auto.sql b/share/ddl/SQLite/deploy/6/001-auto.sql new file mode 100644 index 0000000..e178109 --- /dev/null +++ b/share/ddl/SQLite/deploy/6/001-auto.sql @@ -0,0 +1,145 @@ +-- +-- Created by SQL::Translator::Producer::SQLite +-- Created on Fri Sep 1 15:14:28 2017 +-- + +; +BEGIN TRANSACTION; +-- +-- Table: account_tokens +-- +CREATE TABLE account_tokens ( + id INTEGER PRIMARY KEY NOT NULL, + name text NOT NULL, + used integer NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX account_tokens_name ON account_tokens (name); +-- +-- Table: entities +-- +CREATE TABLE entities ( + id INTEGER PRIMARY KEY NOT NULL, + type varchar(255) NOT NULL +); +-- +-- Table: leaderboards +-- +CREATE TABLE leaderboards ( + id INTEGER PRIMARY KEY NOT NULL, + name varchar(255) NOT NULL, + type varchar(255) NOT NULL +); +CREATE UNIQUE INDEX leaderboards_type ON leaderboards (type); +-- +-- Table: customers +-- +CREATE TABLE customers ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + display_name varchar(255) NOT NULL, + full_name varchar(255) NOT NULL, + year_of_birth integer NOT NULL, + postcode varchar(16) NOT NULL, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX customers_idx_entity_id ON customers (entity_id); +-- +-- Table: leaderboard_sets +-- +CREATE TABLE leaderboard_sets ( + id INTEGER PRIMARY KEY NOT NULL, + leaderboard_id integer NOT NULL, + date datetime NOT NULL, + FOREIGN KEY (leaderboard_id) REFERENCES leaderboards(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX leaderboard_sets_idx_leaderboard_id ON leaderboard_sets (leaderboard_id); +-- +-- Table: organisations +-- +CREATE TABLE organisations ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + name varchar(255) NOT NULL, + street_name text, + town varchar(255) NOT NULL, + postcode varchar(16), + country varchar(255), + sector varchar(1), + pending boolean NOT NULL DEFAULT 0, + submitted_by_id integer, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX organisations_idx_entity_id ON organisations (entity_id); +-- +-- Table: transactions +-- +CREATE TABLE transactions ( + id INTEGER PRIMARY KEY NOT NULL, + buyer_id integer NOT NULL, + seller_id integer NOT NULL, + value decimal(16,2) NOT NULL, + proof_image text, + submitted_at datetime NOT NULL, + purchase_time datetime NOT NULL, + FOREIGN KEY (buyer_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (seller_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX transactions_idx_buyer_id ON transactions (buyer_id); +CREATE INDEX transactions_idx_seller_id ON transactions (seller_id); +-- +-- Table: users +-- +CREATE TABLE users ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + email text NOT NULL, + join_date datetime NOT NULL, + password varchar(100) NOT NULL, + is_admin boolean NOT NULL DEFAULT 0, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX users_idx_entity_id ON users (entity_id); +CREATE UNIQUE INDEX users_email ON users (email); +-- +-- Table: feedback +-- +CREATE TABLE feedback ( + id INTEGER PRIMARY KEY NOT NULL, + user_id integer NOT NULL, + submitted_at datetime NOT NULL, + feedbacktext text NOT NULL, + app_name varchar(255) NOT NULL, + package_name varchar(255) NOT NULL, + version_code varchar(255) NOT NULL, + version_number varchar(255) NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX feedback_idx_user_id ON feedback (user_id); +-- +-- Table: session_tokens +-- +CREATE TABLE session_tokens ( + id INTEGER PRIMARY KEY NOT NULL, + token varchar(255) NOT NULL, + user_id integer NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX session_tokens_idx_user_id ON session_tokens (user_id); +CREATE UNIQUE INDEX session_tokens_token ON session_tokens (token); +-- +-- Table: leaderboard_values +-- +CREATE TABLE leaderboard_values ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + set_id integer NOT NULL, + position integer NOT NULL, + value decimal(16,2) NOT NULL, + trend integer NOT NULL DEFAULT 0, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (set_id) REFERENCES leaderboard_sets(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX leaderboard_values_idx_entity_id ON leaderboard_values (entity_id); +CREATE INDEX leaderboard_values_idx_set_id ON leaderboard_values (set_id); +CREATE UNIQUE INDEX leaderboard_values_entity_id_set_id ON leaderboard_values (entity_id, set_id); +COMMIT; diff --git a/share/ddl/SQLite/deploy/7/001-auto-__VERSION.sql b/share/ddl/SQLite/deploy/7/001-auto-__VERSION.sql new file mode 100644 index 0000000..4602862 --- /dev/null +++ b/share/ddl/SQLite/deploy/7/001-auto-__VERSION.sql @@ -0,0 +1,18 @@ +-- +-- Created by SQL::Translator::Producer::SQLite +-- Created on Wed Sep 13 15:24:20 2017 +-- + +; +BEGIN TRANSACTION; +-- +-- Table: dbix_class_deploymenthandler_versions +-- +CREATE TABLE dbix_class_deploymenthandler_versions ( + id INTEGER PRIMARY KEY NOT NULL, + version varchar(50) NOT NULL, + ddl text, + upgrade_sql text +); +CREATE UNIQUE INDEX dbix_class_deploymenthandler_versions_version ON dbix_class_deploymenthandler_versions (version); +COMMIT; diff --git a/share/ddl/SQLite/deploy/7/001-auto.sql b/share/ddl/SQLite/deploy/7/001-auto.sql new file mode 100644 index 0000000..4c8b658 --- /dev/null +++ b/share/ddl/SQLite/deploy/7/001-auto.sql @@ -0,0 +1,145 @@ +-- +-- Created by SQL::Translator::Producer::SQLite +-- Created on Wed Sep 13 15:24:20 2017 +-- + +; +BEGIN TRANSACTION; +-- +-- Table: account_tokens +-- +CREATE TABLE account_tokens ( + id INTEGER PRIMARY KEY NOT NULL, + name text NOT NULL, + used integer NOT NULL DEFAULT 0 +); +CREATE UNIQUE INDEX account_tokens_name ON account_tokens (name); +-- +-- Table: entities +-- +CREATE TABLE entities ( + id INTEGER PRIMARY KEY NOT NULL, + type varchar(255) NOT NULL +); +-- +-- Table: leaderboards +-- +CREATE TABLE leaderboards ( + id INTEGER PRIMARY KEY NOT NULL, + name varchar(255) NOT NULL, + type varchar(255) NOT NULL +); +CREATE UNIQUE INDEX leaderboards_type ON leaderboards (type); +-- +-- Table: customers +-- +CREATE TABLE customers ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + display_name varchar(255) NOT NULL, + full_name varchar(255) NOT NULL, + year_of_birth integer NOT NULL, + postcode varchar(16) NOT NULL, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX customers_idx_entity_id ON customers (entity_id); +-- +-- Table: leaderboard_sets +-- +CREATE TABLE leaderboard_sets ( + id INTEGER PRIMARY KEY NOT NULL, + leaderboard_id integer NOT NULL, + date datetime NOT NULL, + FOREIGN KEY (leaderboard_id) REFERENCES leaderboards(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX leaderboard_sets_idx_leaderboard_id ON leaderboard_sets (leaderboard_id); +-- +-- Table: organisations +-- +CREATE TABLE organisations ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + name varchar(255) NOT NULL, + street_name text, + town varchar(255) NOT NULL, + postcode varchar(16), + country varchar(255), + sector varchar(1), + pending boolean NOT NULL DEFAULT false, + submitted_by_id integer, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX organisations_idx_entity_id ON organisations (entity_id); +-- +-- Table: transactions +-- +CREATE TABLE transactions ( + id INTEGER PRIMARY KEY NOT NULL, + buyer_id integer NOT NULL, + seller_id integer NOT NULL, + value numeric(100,0) NOT NULL, + proof_image text, + submitted_at datetime NOT NULL, + purchase_time datetime NOT NULL, + FOREIGN KEY (buyer_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (seller_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX transactions_idx_buyer_id ON transactions (buyer_id); +CREATE INDEX transactions_idx_seller_id ON transactions (seller_id); +-- +-- Table: users +-- +CREATE TABLE users ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + email text NOT NULL, + join_date datetime NOT NULL, + password varchar(100) NOT NULL, + is_admin boolean NOT NULL DEFAULT false, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX users_idx_entity_id ON users (entity_id); +CREATE UNIQUE INDEX users_email ON users (email); +-- +-- Table: feedback +-- +CREATE TABLE feedback ( + id INTEGER PRIMARY KEY NOT NULL, + user_id integer NOT NULL, + submitted_at datetime NOT NULL, + feedbacktext text NOT NULL, + app_name varchar(255) NOT NULL, + package_name varchar(255) NOT NULL, + version_code varchar(255) NOT NULL, + version_number varchar(255) NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX feedback_idx_user_id ON feedback (user_id); +-- +-- Table: session_tokens +-- +CREATE TABLE session_tokens ( + id INTEGER PRIMARY KEY NOT NULL, + token varchar(255) NOT NULL, + user_id integer NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX session_tokens_idx_user_id ON session_tokens (user_id); +CREATE UNIQUE INDEX session_tokens_token ON session_tokens (token); +-- +-- Table: leaderboard_values +-- +CREATE TABLE leaderboard_values ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + set_id integer NOT NULL, + position integer NOT NULL, + value numeric(100,0) NOT NULL, + trend integer NOT NULL DEFAULT 0, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (set_id) REFERENCES leaderboard_sets(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX leaderboard_values_idx_entity_id ON leaderboard_values (entity_id); +CREATE INDEX leaderboard_values_idx_set_id ON leaderboard_values (set_id); +CREATE UNIQUE INDEX leaderboard_values_entity_id_set_id ON leaderboard_values (entity_id, set_id); +COMMIT; diff --git a/share/ddl/SQLite/upgrade/5-6/001-auto.sql b/share/ddl/SQLite/upgrade/5-6/001-auto.sql new file mode 100644 index 0000000..7fa7791 --- /dev/null +++ b/share/ddl/SQLite/upgrade/5-6/001-auto.sql @@ -0,0 +1,116 @@ +-- Convert schema 'share/ddl/_source/deploy/5/001-auto.yml' to 'share/ddl/_source/deploy/6/001-auto.yml':; +-- Customised for proper migration + +BEGIN; + +CREATE TABLE entities ( + id INTEGER PRIMARY KEY NOT NULL, + type varchar(255) NOT NULL +); + +ALTER TABLE customers RENAME TO customers_temp; + +CREATE TABLE customers ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + display_name varchar(255) NOT NULL, + full_name varchar(255) NOT NULL, + year_of_birth integer NOT NULL, + postcode varchar(16) NOT NULL, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX customers_idx_entity_id ON customers (entity_id); + +-- Leaderboards must be regenerated, this saves trying to do this the hard way +DROP TABLE leaderboard_values; +DELETE FROM leaderboard_sets; + +CREATE TABLE leaderboard_values ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + set_id integer NOT NULL, + position integer NOT NULL, + value decimal(16,2) NOT NULL, + trend integer NOT NULL DEFAULT 0, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (set_id) REFERENCES leaderboard_sets(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX leaderboard_values_idx_entity_id ON leaderboard_values (entity_id); +CREATE INDEX leaderboard_values_idx_set_id ON leaderboard_values (set_id); +CREATE UNIQUE INDEX leaderboard_values_entity_id_set_id ON leaderboard_values (entity_id, set_id); + +ALTER TABLE organisations RENAME TO organisations_temp; + +CREATE TABLE organisations ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + name varchar(255) NOT NULL, + street_name text, + town varchar(255) NOT NULL, + postcode varchar(16), + country varchar(255), + sector varchar(1), + pending boolean NOT NULL DEFAULT 0, + submitted_by_id integer, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX organisations_idx_entity_id ON organisations (entity_id); + +ALTER TABLE transactions RENAME TO transactions_temp; + +CREATE TABLE transactions ( + id INTEGER PRIMARY KEY NOT NULL, + buyer_id integer NOT NULL, + seller_id integer NOT NULL, + value decimal(16,2) NOT NULL, + proof_image text, + submitted_at datetime NOT NULL, + purchase_time datetime NOT NULL, + FOREIGN KEY (buyer_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (seller_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX transactions_idx_buyer_id02 ON transactions (buyer_id); +CREATE INDEX transactions_idx_seller_id02 ON transactions (seller_id); + +ALTER TABLE users RENAME TO users_temp; + +CREATE TABLE users ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + email text NOT NULL, + join_date datetime NOT NULL, + password varchar(100) NOT NULL, + is_admin boolean NOT NULL DEFAULT 0, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE +); +CREATE INDEX users_idx_entity_id02 ON users (entity_id); +CREATE UNIQUE INDEX users_email02 ON users (email); + +ALTER TABLE session_tokens RENAME TO session_tokens_temp; + +CREATE TABLE session_tokens ( + id INTEGER PRIMARY KEY NOT NULL, + token varchar(255) NOT NULL, + user_id integer NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX session_tokens_idx_user_id02 ON session_tokens (user_id); +CREATE UNIQUE INDEX session_tokens_token02 ON session_tokens (token); + +ALTER TABLE feedback RENAME TO feedback_temp; + +CREATE TABLE feedback ( + id INTEGER PRIMARY KEY NOT NULL, + user_id integer NOT NULL, + submitted_at datetime NOT NULL, + feedbacktext text NOT NULL, + app_name varchar(255) NOT NULL, + package_name varchar(255) NOT NULL, + version_code varchar(255) NOT NULL, + version_number varchar(255) NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); +CREATE INDEX feedback_idx_user_id02 ON feedback (user_id); + +COMMIT; + diff --git a/share/ddl/SQLite/upgrade/5-6/003-remove-temps.sql b/share/ddl/SQLite/upgrade/5-6/003-remove-temps.sql new file mode 100644 index 0000000..0beb101 --- /dev/null +++ b/share/ddl/SQLite/upgrade/5-6/003-remove-temps.sql @@ -0,0 +1,15 @@ +-- Remove temporary tables created during migration + +BEGIN; + +DROP TABLE customers_temp; +DROP TABLE organisations_temp; +DROP TABLE transactions_temp; +DROP TABLE users_temp; +DROP TABLE feedback_temp; +DROP TABLE session_tokens_temp; +DROP TABLE pending_organisations; +DROP TABLE pending_transactions; +DROP TABLE administrators; + +COMMIT; diff --git a/share/ddl/SQLite/upgrade/6-7/001-auto.sql b/share/ddl/SQLite/upgrade/6-7/001-auto.sql new file mode 100644 index 0000000..4b6298a --- /dev/null +++ b/share/ddl/SQLite/upgrade/6-7/001-auto.sql @@ -0,0 +1,98 @@ +-- Convert schema 'share/ddl/_source/deploy/6/001-auto.yml' to 'share/ddl/_source/deploy/7/001-auto.yml':; + +; +BEGIN; + +; +CREATE TEMPORARY TABLE leaderboard_values_temp_alter ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + set_id integer NOT NULL, + position integer NOT NULL, + value decimal(16,2) NOT NULL, + trend integer NOT NULL DEFAULT 0, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (set_id) REFERENCES leaderboard_sets(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); + +; +INSERT INTO leaderboard_values_temp_alter( id, entity_id, set_id, position, value, trend) SELECT id, entity_id, set_id, position, value, trend FROM leaderboard_values; + +; +DROP TABLE leaderboard_values; + +; +CREATE TABLE leaderboard_values ( + id INTEGER PRIMARY KEY NOT NULL, + entity_id integer NOT NULL, + set_id integer NOT NULL, + position integer NOT NULL, + value numeric(100,0) NOT NULL, + trend integer NOT NULL DEFAULT 0, + FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (set_id) REFERENCES leaderboard_sets(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); + +; +CREATE INDEX leaderboard_values_idx_enti00 ON leaderboard_values (entity_id); + +; +CREATE INDEX leaderboard_values_idx_set_00 ON leaderboard_values (set_id); + +; +CREATE UNIQUE INDEX leaderboard_values_entity_i00 ON leaderboard_values (entity_id, set_id); + +; +INSERT INTO leaderboard_values SELECT id, entity_id, set_id, position, value, trend FROM leaderboard_values_temp_alter; + +; +DROP TABLE leaderboard_values_temp_alter; + +; +CREATE TEMPORARY TABLE transactions_temp_alter ( + id INTEGER PRIMARY KEY NOT NULL, + buyer_id integer NOT NULL, + seller_id integer NOT NULL, + value decimal(16,2) NOT NULL, + proof_image text, + submitted_at datetime NOT NULL, + purchase_time datetime NOT NULL, + FOREIGN KEY (buyer_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (seller_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); + +; +INSERT INTO transactions_temp_alter( id, buyer_id, seller_id, value, proof_image, submitted_at, purchase_time) SELECT id, buyer_id, seller_id, value, proof_image, submitted_at, purchase_time FROM transactions; + +; +DROP TABLE transactions; + +; +CREATE TABLE transactions ( + id INTEGER PRIMARY KEY NOT NULL, + buyer_id integer NOT NULL, + seller_id integer NOT NULL, + value numeric(100,0) NOT NULL, + proof_image text, + submitted_at datetime NOT NULL, + purchase_time datetime NOT NULL, + FOREIGN KEY (buyer_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION, + FOREIGN KEY (seller_id) REFERENCES entities(id) ON DELETE NO ACTION ON UPDATE NO ACTION +); + +; +CREATE INDEX transactions_idx_buyer_id02 ON transactions (buyer_id); + +; +CREATE INDEX transactions_idx_seller_id02 ON transactions (seller_id); + +; +INSERT INTO transactions SELECT id, buyer_id, seller_id, value * 100000, proof_image, submitted_at, purchase_time FROM transactions_temp_alter; + +; +DROP TABLE transactions_temp_alter; + +; + +COMMIT; + diff --git a/share/ddl/_common/upgrade/5-6/002-entity-upgrade.pl b/share/ddl/_common/upgrade/5-6/002-entity-upgrade.pl new file mode 100644 index 0000000..b776661 --- /dev/null +++ b/share/ddl/_common/upgrade/5-6/002-entity-upgrade.pl @@ -0,0 +1,132 @@ +#! perl + +use strict; +use warnings; + +use DBIx::Class::DeploymentHandler::DeployMethod::SQL::Translator::ScriptHelpers + 'schema_from_schema_loader'; + +schema_from_schema_loader({ naming => 'v7' }, sub { + my $schema = shift; + + my $user_rs = $schema->resultset('UsersTemp'); + my $customer_rs = $schema->resultset('CustomersTemp'); + my $organisation_rs = $schema->resultset('OrganisationsTemp'); + my $transaction_rs = $schema->resultset('TransactionsTemp'); + my $pending_org_rs = $schema->resultset('PendingOrganisation'); + my $pending_trans_rs = $schema->resultset('PendingTransaction'); + my $feedback_rs = $schema->resultset('FeedbackTemp'); + my $session_token_rs = $schema->resultset('SessionTokensTemp'); + + # Lookups used for converting transactions + my $org_lookup = {}; + my $user_lookup = {}; + + # First migrate all customers, organisations, and pending organisations across to the entity table. + for my $customer_result ( $customer_rs->all ) { + my $user_result = $user_rs->find({ customer_id => $customer_result->id }); + my $administrator = $schema->resultset('Administrator')->find({ user_id => $user_result->id }); + my $new_entity = $schema->resultset('Entity')->create({ type => 'customer' }); + + my $new_customer = $schema->resultset('Customer')->create({ + entity_id => $new_entity->id, + display_name => $customer_result->display_name, + full_name => $customer_result->full_name, + year_of_birth => $customer_result->year_of_birth, + postcode => $customer_result->postcode, + }); + + # In the old system, all customers were users + my $new_user = $schema->resultset('User')->create({ + entity_id => $new_entity->id, + email => $user_result->email, + join_date => $user_result->join_date, + password => $user_result->password, + is_admin => defined $administrator ? 1 : 0, + }); + $user_lookup->{$user_result->id} = $new_entity->id; + } + + for my $organisation_result ( $organisation_rs->all ) { + my $user_result = $user_rs->find({ organisation_id => $organisation_result->id }); + my $new_entity = $schema->resultset('Entity')->create({ type => 'organisation' }); + + my $org = $schema->resultset('Organisation')->create({ + entity_id => $new_entity->id, + name => $organisation_result->name, + street_name => $organisation_result->street_name, + town => $organisation_result->town, + postcode => $organisation_result->postcode, + }); + + # In the old system, not all organisations were users - but could have still been an admin? + if ( defined $user_result ) { + my $administrator = $schema->resultset('Administrator')->find({ user_id => $user_result->id }); + my $new_user = $schema->resultset('User')->create({ + entity_id => $new_entity->id, + email => $user_result->email, + join_date => $user_result->join_date, + password => $user_result->password, + is_admin => defined $administrator ? 1 : 0, + }); + $user_lookup->{$user_result->id} = $new_entity->id; + } + $org_lookup->{$organisation_result->id} = $new_entity->id; + } + + for my $transaction_result ( $transaction_rs->all ) { + my $new_transaction = $schema->resultset('Transaction')->create({ + buyer_id => $user_lookup->{ $transaction_result->buyer_id }, + seller_id => $org_lookup->{ $transaction_result->seller_id }, + value => $transaction_result->value, + proof_image => $transaction_result->proof_image, + submitted_at => $transaction_result->submitted_at, + purchase_time => $transaction_result->purchase_time, + }); + } + + for my $pending_result ( $pending_org_rs->all ) { + my $entity = $schema->resultset('Entity')->create({ type => 'organisation' }); + my $org = $schema->resultset('Organisation')->create({ + entity_id => $entity->id, + name => $pending_result->name, + street_name => $pending_result->street_name, + town => $pending_result->town, + postcode => $pending_result->postcode, + submitted_by_id => $user_lookup->{ $pending_result->submitted_by_id }, + pending => 1, + }); + my $pending_trans_set_rs = $pending_trans_rs->search({ + seller_id => $pending_result->id, + }); + for my $trans_result ( $pending_trans_set_rs->all ) { + $schema->resultset('Transaction')->create({ + buyer_id => $user_lookup->{ $trans_result->buyer_id }, + seller_id => $entity->id, + value => $trans_result->value, + proof_image => $trans_result->proof_image, + submitted_at => $trans_result->submitted_at, + purchase_time => $trans_result->purchase_time, + }); + } + } + + for my $session_token ( $session_token_rs->all ) { + $schema->resultset('SessionToken')->create({ + token => $session_token->token, + user_id => $user_lookup->{ $session_token->user_id }, + }); + } + + for my $feedback_result ( $feedback_rs->all ) { + $schema->resultset('Feedback')->create({ + user_id => $user_lookup->{ $feedback_result->user_id }, + submitted_at => $feedback_result->submitted_at, + feedbacktext => $feedback_result->feedbacktext, + app_name => $feedback_result->app_name, + package_name => $feedback_result->package_name, + version_code => $feedback_result->version_code, + version_number => $feedback_result->version_number, + }); + } +}); diff --git a/share/ddl/_source/deploy/6/001-auto-__VERSION.yml b/share/ddl/_source/deploy/6/001-auto-__VERSION.yml new file mode 100644 index 0000000..907f443 --- /dev/null +++ b/share/ddl/_source/deploy/6/001-auto-__VERSION.yml @@ -0,0 +1,91 @@ +--- +schema: + procedures: {} + tables: + dbix_class_deploymenthandler_versions: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - version + match_type: '' + name: dbix_class_deploymenthandler_versions_version + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + ddl: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: ddl + order: 3 + size: + - 0 + id: + data_type: int + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + upgrade_sql: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: upgrade_sql + order: 4 + size: + - 0 + version: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: version + order: 2 + size: + - 50 + indices: [] + name: dbix_class_deploymenthandler_versions + options: [] + order: 1 + triggers: {} + views: {} +translator: + add_drop_table: 0 + filename: ~ + no_comments: 0 + parser_args: + sources: + - __VERSION + parser_type: SQL::Translator::Parser::DBIx::Class + producer_args: {} + producer_type: SQL::Translator::Producer::YAML + show_warnings: 0 + trace: 0 + version: 0.11021 diff --git a/share/ddl/_source/deploy/6/001-auto.yml b/share/ddl/_source/deploy/6/001-auto.yml new file mode 100644 index 0000000..6d9e363 --- /dev/null +++ b/share/ddl/_source/deploy/6/001-auto.yml @@ -0,0 +1,1064 @@ +--- +schema: + procedures: {} + tables: + account_tokens: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - name + match_type: '' + name: account_tokens_name + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + name: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: name + order: 2 + size: + - 0 + used: + data_type: integer + default_value: 0 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: used + order: 3 + size: + - 0 + indices: [] + name: account_tokens + options: [] + order: 1 + customers: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - entity_id + match_type: '' + name: customers_fk_entity_id + on_delete: CASCADE + on_update: '' + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + display_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: display_name + order: 3 + size: + - 255 + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: entity_id + order: 2 + size: + - 0 + full_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: full_name + order: 4 + size: + - 255 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + postcode: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: postcode + order: 6 + size: + - 16 + year_of_birth: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: year_of_birth + order: 5 + size: + - 0 + indices: + - fields: + - entity_id + name: customers_idx_entity_id + options: [] + type: NORMAL + name: customers + options: [] + order: 4 + entities: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + type: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: type + order: 2 + size: + - 255 + indices: [] + name: entities + options: [] + order: 2 + feedback: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 0 + expression: '' + fields: + - user_id + match_type: '' + name: feedback_fk_user_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: users + type: FOREIGN KEY + fields: + app_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: app_name + order: 5 + size: + - 255 + feedbacktext: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: feedbacktext + order: 4 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + package_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: package_name + order: 6 + size: + - 255 + submitted_at: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: submitted_at + order: 3 + size: + - 0 + user_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: user_id + order: 2 + size: + - 0 + version_code: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: version_code + order: 7 + size: + - 255 + version_number: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: version_number + order: 8 + size: + - 255 + indices: + - fields: + - user_id + name: feedback_idx_user_id + options: [] + type: NORMAL + name: feedback + options: [] + order: 9 + leaderboard_sets: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 0 + expression: '' + fields: + - leaderboard_id + match_type: '' + name: leaderboard_sets_fk_leaderboard_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: leaderboards + type: FOREIGN KEY + fields: + date: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: date + order: 3 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + leaderboard_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: leaderboard_id + order: 2 + size: + - 0 + indices: + - fields: + - leaderboard_id + name: leaderboard_sets_idx_leaderboard_id + options: [] + type: NORMAL + name: leaderboard_sets + options: [] + order: 5 + leaderboard_values: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - entity_id + - set_id + match_type: '' + name: leaderboard_values_entity_id_set_id + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 0 + expression: '' + fields: + - entity_id + match_type: '' + name: leaderboard_values_fk_entity_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + - deferrable: 0 + expression: '' + fields: + - set_id + match_type: '' + name: leaderboard_values_fk_set_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: leaderboard_sets + type: FOREIGN KEY + fields: + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: entity_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + position: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: position + order: 4 + size: + - 0 + set_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: set_id + order: 3 + size: + - 0 + trend: + data_type: integer + default_value: 0 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: trend + order: 6 + size: + - 0 + value: + data_type: decimal + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: value + order: 5 + size: + - 16 + - 2 + indices: + - fields: + - entity_id + name: leaderboard_values_idx_entity_id + options: [] + type: NORMAL + - fields: + - set_id + name: leaderboard_values_idx_set_id + options: [] + type: NORMAL + name: leaderboard_values + options: [] + order: 11 + leaderboards: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - type + match_type: '' + name: leaderboards_type + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: name + order: 2 + size: + - 255 + type: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: type + order: 3 + size: + - 255 + indices: [] + name: leaderboards + options: [] + order: 3 + organisations: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - entity_id + match_type: '' + name: organisations_fk_entity_id + on_delete: CASCADE + on_update: '' + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + country: + data_type: varchar + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: country + order: 7 + size: + - 255 + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: entity_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: name + order: 3 + size: + - 255 + pending: + data_type: boolean + default_value: !!perl/ref + =: false + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: pending + order: 9 + size: + - 0 + postcode: + data_type: varchar + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: postcode + order: 6 + size: + - 16 + sector: + data_type: varchar + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: sector + order: 8 + size: + - 1 + street_name: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: street_name + order: 4 + size: + - 0 + submitted_by_id: + data_type: integer + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: submitted_by_id + order: 10 + size: + - 0 + town: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: town + order: 5 + size: + - 255 + indices: + - fields: + - entity_id + name: organisations_idx_entity_id + options: [] + type: NORMAL + name: organisations + options: [] + order: 6 + session_tokens: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - token + match_type: '' + name: session_tokens_token + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 0 + expression: '' + fields: + - user_id + match_type: '' + name: session_tokens_fk_user_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: users + type: FOREIGN KEY + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + token: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: token + order: 2 + size: + - 255 + user_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: user_id + order: 3 + size: + - 0 + indices: + - fields: + - user_id + name: session_tokens_idx_user_id + options: [] + type: NORMAL + name: session_tokens + options: [] + order: 10 + transactions: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 0 + expression: '' + fields: + - buyer_id + match_type: '' + name: transactions_fk_buyer_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + - deferrable: 0 + expression: '' + fields: + - seller_id + match_type: '' + name: transactions_fk_seller_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + buyer_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: buyer_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + proof_image: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: proof_image + order: 5 + size: + - 0 + purchase_time: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: purchase_time + order: 7 + size: + - 0 + seller_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: seller_id + order: 3 + size: + - 0 + submitted_at: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: submitted_at + order: 6 + size: + - 0 + value: + data_type: decimal + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: value + order: 4 + size: + - 16 + - 2 + indices: + - fields: + - buyer_id + name: transactions_idx_buyer_id + options: [] + type: NORMAL + - fields: + - seller_id + name: transactions_idx_seller_id + options: [] + type: NORMAL + name: transactions + options: [] + order: 7 + users: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - email + match_type: '' + name: users_email + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 1 + expression: '' + fields: + - entity_id + match_type: '' + name: users_fk_entity_id + on_delete: CASCADE + on_update: '' + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + email: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: email + order: 3 + size: + - 0 + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: entity_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + is_admin: + data_type: boolean + default_value: !!perl/ref + =: false + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: is_admin + order: 6 + size: + - 0 + join_date: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: join_date + order: 4 + size: + - 0 + password: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: password + order: 5 + size: + - 100 + indices: + - fields: + - entity_id + name: users_idx_entity_id + options: [] + type: NORMAL + name: users + options: [] + order: 8 + triggers: {} + views: {} +translator: + add_drop_table: 0 + filename: ~ + no_comments: 0 + parser_args: + sources: + - AccountToken + - Customer + - Entity + - Feedback + - Leaderboard + - LeaderboardSet + - LeaderboardValue + - Organisation + - SessionToken + - Transaction + - User + parser_type: SQL::Translator::Parser::DBIx::Class + producer_args: {} + producer_type: SQL::Translator::Producer::YAML + show_warnings: 0 + trace: 0 + version: 0.11021 diff --git a/share/ddl/_source/deploy/7/001-auto-__VERSION.yml b/share/ddl/_source/deploy/7/001-auto-__VERSION.yml new file mode 100644 index 0000000..907f443 --- /dev/null +++ b/share/ddl/_source/deploy/7/001-auto-__VERSION.yml @@ -0,0 +1,91 @@ +--- +schema: + procedures: {} + tables: + dbix_class_deploymenthandler_versions: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - version + match_type: '' + name: dbix_class_deploymenthandler_versions_version + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + ddl: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: ddl + order: 3 + size: + - 0 + id: + data_type: int + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + upgrade_sql: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: upgrade_sql + order: 4 + size: + - 0 + version: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: version + order: 2 + size: + - 50 + indices: [] + name: dbix_class_deploymenthandler_versions + options: [] + order: 1 + triggers: {} + views: {} +translator: + add_drop_table: 0 + filename: ~ + no_comments: 0 + parser_args: + sources: + - __VERSION + parser_type: SQL::Translator::Parser::DBIx::Class + producer_args: {} + producer_type: SQL::Translator::Producer::YAML + show_warnings: 0 + trace: 0 + version: 0.11021 diff --git a/share/ddl/_source/deploy/7/001-auto.yml b/share/ddl/_source/deploy/7/001-auto.yml new file mode 100644 index 0000000..e5c5ed6 --- /dev/null +++ b/share/ddl/_source/deploy/7/001-auto.yml @@ -0,0 +1,1064 @@ +--- +schema: + procedures: {} + tables: + account_tokens: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - name + match_type: '' + name: account_tokens_name + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + name: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: name + order: 2 + size: + - 0 + used: + data_type: integer + default_value: 0 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: used + order: 3 + size: + - 0 + indices: [] + name: account_tokens + options: [] + order: 1 + customers: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - entity_id + match_type: '' + name: customers_fk_entity_id + on_delete: CASCADE + on_update: '' + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + display_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: display_name + order: 3 + size: + - 255 + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: entity_id + order: 2 + size: + - 0 + full_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: full_name + order: 4 + size: + - 255 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + postcode: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: postcode + order: 6 + size: + - 16 + year_of_birth: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: year_of_birth + order: 5 + size: + - 0 + indices: + - fields: + - entity_id + name: customers_idx_entity_id + options: [] + type: NORMAL + name: customers + options: [] + order: 4 + entities: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + type: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: type + order: 2 + size: + - 255 + indices: [] + name: entities + options: [] + order: 2 + feedback: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 0 + expression: '' + fields: + - user_id + match_type: '' + name: feedback_fk_user_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: users + type: FOREIGN KEY + fields: + app_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: app_name + order: 5 + size: + - 255 + feedbacktext: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: feedbacktext + order: 4 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + package_name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: package_name + order: 6 + size: + - 255 + submitted_at: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: submitted_at + order: 3 + size: + - 0 + user_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: user_id + order: 2 + size: + - 0 + version_code: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: version_code + order: 7 + size: + - 255 + version_number: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: version_number + order: 8 + size: + - 255 + indices: + - fields: + - user_id + name: feedback_idx_user_id + options: [] + type: NORMAL + name: feedback + options: [] + order: 9 + leaderboard_sets: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 0 + expression: '' + fields: + - leaderboard_id + match_type: '' + name: leaderboard_sets_fk_leaderboard_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: leaderboards + type: FOREIGN KEY + fields: + date: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: date + order: 3 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + leaderboard_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: leaderboard_id + order: 2 + size: + - 0 + indices: + - fields: + - leaderboard_id + name: leaderboard_sets_idx_leaderboard_id + options: [] + type: NORMAL + name: leaderboard_sets + options: [] + order: 5 + leaderboard_values: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - entity_id + - set_id + match_type: '' + name: leaderboard_values_entity_id_set_id + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 0 + expression: '' + fields: + - entity_id + match_type: '' + name: leaderboard_values_fk_entity_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + - deferrable: 0 + expression: '' + fields: + - set_id + match_type: '' + name: leaderboard_values_fk_set_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: leaderboard_sets + type: FOREIGN KEY + fields: + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: entity_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + position: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: position + order: 4 + size: + - 0 + set_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: set_id + order: 3 + size: + - 0 + trend: + data_type: integer + default_value: 0 + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: trend + order: 6 + size: + - 0 + value: + data_type: numeric + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: value + order: 5 + size: + - 100 + - 0 + indices: + - fields: + - entity_id + name: leaderboard_values_idx_entity_id + options: [] + type: NORMAL + - fields: + - set_id + name: leaderboard_values_idx_set_id + options: [] + type: NORMAL + name: leaderboard_values + options: [] + order: 11 + leaderboards: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - type + match_type: '' + name: leaderboards_type + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: name + order: 2 + size: + - 255 + type: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: type + order: 3 + size: + - 255 + indices: [] + name: leaderboards + options: [] + order: 3 + organisations: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - entity_id + match_type: '' + name: organisations_fk_entity_id + on_delete: CASCADE + on_update: '' + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + country: + data_type: varchar + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: country + order: 7 + size: + - 255 + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: entity_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + name: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: name + order: 3 + size: + - 255 + pending: + data_type: boolean + default_value: !!perl/ref + =: false + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: pending + order: 9 + size: + - 0 + postcode: + data_type: varchar + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: postcode + order: 6 + size: + - 16 + sector: + data_type: varchar + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: sector + order: 8 + size: + - 1 + street_name: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: street_name + order: 4 + size: + - 0 + submitted_by_id: + data_type: integer + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: submitted_by_id + order: 10 + size: + - 0 + town: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: town + order: 5 + size: + - 255 + indices: + - fields: + - entity_id + name: organisations_idx_entity_id + options: [] + type: NORMAL + name: organisations + options: [] + order: 6 + session_tokens: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - token + match_type: '' + name: session_tokens_token + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 0 + expression: '' + fields: + - user_id + match_type: '' + name: session_tokens_fk_user_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: users + type: FOREIGN KEY + fields: + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + token: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: token + order: 2 + size: + - 255 + user_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: user_id + order: 3 + size: + - 0 + indices: + - fields: + - user_id + name: session_tokens_idx_user_id + options: [] + type: NORMAL + name: session_tokens + options: [] + order: 10 + transactions: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 0 + expression: '' + fields: + - buyer_id + match_type: '' + name: transactions_fk_buyer_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + - deferrable: 0 + expression: '' + fields: + - seller_id + match_type: '' + name: transactions_fk_seller_id + on_delete: NO ACTION + on_update: NO ACTION + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + buyer_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: buyer_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + proof_image: + data_type: text + default_value: ~ + is_nullable: 1 + is_primary_key: 0 + is_unique: 0 + name: proof_image + order: 5 + size: + - 0 + purchase_time: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: purchase_time + order: 7 + size: + - 0 + seller_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: seller_id + order: 3 + size: + - 0 + submitted_at: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: submitted_at + order: 6 + size: + - 0 + value: + data_type: numeric + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: value + order: 4 + size: + - 100 + - 0 + indices: + - fields: + - buyer_id + name: transactions_idx_buyer_id + options: [] + type: NORMAL + - fields: + - seller_id + name: transactions_idx_seller_id + options: [] + type: NORMAL + name: transactions + options: [] + order: 7 + users: + constraints: + - deferrable: 1 + expression: '' + fields: + - id + match_type: '' + name: '' + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: PRIMARY KEY + - deferrable: 1 + expression: '' + fields: + - email + match_type: '' + name: users_email + on_delete: '' + on_update: '' + options: [] + reference_fields: [] + reference_table: '' + type: UNIQUE + - deferrable: 1 + expression: '' + fields: + - entity_id + match_type: '' + name: users_fk_entity_id + on_delete: CASCADE + on_update: '' + options: [] + reference_fields: + - id + reference_table: entities + type: FOREIGN KEY + fields: + email: + data_type: text + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 1 + name: email + order: 3 + size: + - 0 + entity_id: + data_type: integer + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: entity_id + order: 2 + size: + - 0 + id: + data_type: integer + default_value: ~ + is_auto_increment: 1 + is_nullable: 0 + is_primary_key: 1 + is_unique: 0 + name: id + order: 1 + size: + - 0 + is_admin: + data_type: boolean + default_value: !!perl/ref + =: false + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: is_admin + order: 6 + size: + - 0 + join_date: + data_type: datetime + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: join_date + order: 4 + size: + - 0 + password: + data_type: varchar + default_value: ~ + is_nullable: 0 + is_primary_key: 0 + is_unique: 0 + name: password + order: 5 + size: + - 100 + indices: + - fields: + - entity_id + name: users_idx_entity_id + options: [] + type: NORMAL + name: users + options: [] + order: 8 + triggers: {} + views: {} +translator: + add_drop_table: 0 + filename: ~ + no_comments: 0 + parser_args: + sources: + - AccountToken + - Customer + - Entity + - Feedback + - Leaderboard + - LeaderboardSet + - LeaderboardValue + - Organisation + - SessionToken + - Transaction + - User + parser_type: SQL::Translator::Parser::DBIx::Class + producer_args: {} + producer_type: SQL::Translator::Producer::YAML + show_warnings: 0 + trace: 0 + version: 0.11021 diff --git a/share/schema_graphs/schema-v005.png b/share/schema_graphs/schema-v005.png new file mode 100644 index 0000000..585d9f8 Binary files /dev/null and b/share/schema_graphs/schema-v005.png differ diff --git a/share/schema_graphs/schema-v006.png b/share/schema_graphs/schema-v006.png new file mode 100644 index 0000000..1e9bc88 Binary files /dev/null and b/share/schema_graphs/schema-v006.png differ diff --git a/t/admin/login.t b/t/admin/login.t index 1f7211a..abaea7e 100644 --- a/t/admin/login.t +++ b/t/admin/login.t @@ -1,27 +1,18 @@ use Mojo::Base -strict; +use FindBin qw/ $Bin /; + use Test::More; -use Mojo::JSON; use Test::Pear::LocalLoop; -my $framework = Test::Pear::LocalLoop->new; +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../etc", +); +$framework->install_fixtures('users'); + my $t = $framework->framework; my $schema = $t->app->schema; -$schema->resultset('User')->create({ - email => 'admin@example.com', - password => 'abc123', - administrator => {}, -}); - -$schema->resultset('User')->create({ - email => 'user@example.com', - password => 'abc123', -}); - -is $schema->resultset('User')->count, 2, 'Users Created'; -is $schema->resultset('Administrator')->count, 1, 'Admin Created'; - my $location_is = sub { my ($t, $value, $desc) = @_; $desc ||= "Location: $value"; @@ -34,7 +25,7 @@ $t->get_ok('/admin') $t->ua->max_redirects(10); $t->post_ok('/admin', form => { - email => 'user@example.com', + email => 'test1@example.com', password => 'abc123', })->status_is(200); diff --git a/t/admin/organisation.t b/t/admin/organisation.t index 8ac7af6..32f066d 100644 --- a/t/admin/organisation.t +++ b/t/admin/organisation.t @@ -1,38 +1,42 @@ use Mojo::Base -strict; +use FindBin qw/ $Bin /; + use Test::More; -use Mojo::JSON; use Test::Pear::LocalLoop; -my $framework = Test::Pear::LocalLoop->new; +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../etc", +); +$framework->install_fixtures('users'); my $t = $framework->framework; my $schema = $t->app->schema; -my $user = $schema->resultset('User')->create({ - email => 'admin@example.com', - password => 'abc123', - administrator => {}, +my $valid_entity = $schema->resultset('Entity')->create({ + organisation => { + name => 'Shinra Electric Power Company', + street_name => 'Sector 0, Midgar, Eastern Continent', + town => 'Gaia', + sector => 'A', + postcode => 'WC1E 6AD', + }, + type => "organisation", }); -is $schema->resultset('Administrator')->count, 1, 'Admin Created'; - -$schema->resultset('Organisation')->create({ - id => 1, - name => 'Shinra Electric Power Company', - street_name => 'Sector 0, Midgar, Eastern Continent', - town => 'Gaia', - sector => 'A', - postcode => 'WC1E 6AD', +my $pending_entity = $schema->resultset('Entity')->create({ + organisation => { + name => '7th Heaven', + street_name => 'Slums, Sector 7', + town => 'Midgar', + sector => 'A', + postcode => 'WC1E 6AD', + pending => 1, + }, + type => "organisation", }); -$schema->resultset('PendingOrganisation')->create({ - id => 2, - name => '7th Heaven', - street_name => 'Slums, Sector 7', - town => 'Midgar', - postcode => 'WC1E 6AD', - submitted_by_id => $user->id, -}); +my $valid_id = $valid_entity->organisation->id; +my $pending_id = $pending_entity->organisation->id; #login to admin $t->ua->max_redirects(10); @@ -42,15 +46,15 @@ $t->post_ok('/admin', form => { })->status_is(200); #Read approved organisation -$t->get_ok('/admin/organisations/valid/1/') - ->status_is(200); +$t->get_ok("/admin/organisations/$valid_id") + ->status_is(200)->or($framework->dump_error); #Read pending organisation -$t->get_ok('/admin/organisations/pending/2/') - ->status_is(200); +$t->get_ok("/admin/organisations/$pending_id") + ->status_is(200)->or($framework->dump_error); #Valid approved organisation update -$t->post_ok('/admin/organisations/valid/1/edit', form => { +$t->post_ok("/admin/organisations/$valid_id", form => { name => 'Shinra Electric Power Company', street_name => 'Sector 0, Midgar, Eastern Continent', town => 'Gaia', @@ -59,7 +63,7 @@ $t->post_ok('/admin/organisations/valid/1/edit', form => { })->status_is(200)->content_like(qr/Updated Organisation/); #Failed validation on approved organisation -$t->post_ok('/admin/organisations/valid/1/edit', form => { +$t->post_ok("/admin/organisations/$valid_id", form => { name => 'Shinra Electric Power Company', street_name => 'Sector 0, Midgar, Eastern Continent', sector => 'A', @@ -67,7 +71,7 @@ $t->post_ok('/admin/organisations/valid/1/edit', form => { })->content_like(qr/The validation has failed/); #Valid pending organisation update -$t->post_ok('/admin/organisations/pending/2/edit', form => { +$t->post_ok("/admin/organisations/$pending_id", form => { name => '7th Heaven', street_name => 'Slums, Sector 7', town => 'Midgar', @@ -75,14 +79,14 @@ $t->post_ok('/admin/organisations/pending/2/edit', form => { })->status_is(200)->content_like(qr/Updated Organisation/); #Failed validation on pending organisation -$t->post_ok('/admin/organisations/pending/2/edit', form => { +$t->post_ok("/admin/organisations/$pending_id", form => { name => '7th Heaven', street_name => 'Slums, Sector 7', postcode => 'WC1E 6AD', })->content_like(qr/The validation has failed/); #Valid adding organisation -$t->post_ok('/admin/organisations/add/submit', form => { +$t->post_ok('/admin/organisations/add', form => { name => 'Wall Market', street_name => 'Slums, Sector 6', town => 'Midgar', @@ -91,7 +95,7 @@ $t->post_ok('/admin/organisations/add/submit', form => { })->status_is(200)->content_like(qr/Added Organisation/); #Failed validation on adding organisation -$t->post_ok('/admin/organisations/add/submit', form => { +$t->post_ok('/admin/organisations/add', form => { name => 'Wall Market', street_name => 'Slums, Sector 6', postcode => 'TN35 5AQ', diff --git a/t/admin/transaction.t b/t/admin/transaction.t new file mode 100644 index 0000000..d3a3980 --- /dev/null +++ b/t/admin/transaction.t @@ -0,0 +1,78 @@ +use Mojo::Base -strict; + +use FindBin qw/ $Bin /; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../etc", +); +$framework->install_fixtures('users'); + +my $t = $framework->framework; +my $schema = $t->app->schema; + +my $start = DateTime->today->subtract( hours => 12 ); + +# create 30 days worth of data +for my $count ( 0 .. 29 ) { + my $trans_day = $start->clone->subtract( days => $count ); + + create_random_transaction( 'test1@example.com', $trans_day ); + if ( $count % 2 ) { + create_random_transaction( 'test2@example.com', $trans_day ); + } + if ( $count % 3 ) { + create_random_transaction( 'test3@example.com', $trans_day ); + } + if ( $count % 4 ) { + create_random_transaction( 'test4@example.com', $trans_day ); + } +} + +is $schema->resultset('Transaction')->count, 87; + +#login to admin +$t->ua->max_redirects(10); +$t->post_ok('/admin', form => { + email => 'admin@example.com', + password => 'abc123', +})->status_is(200); + +#Read valid transaction +$t->get_ok("/admin/transactions/1") + ->status_is(200)->or($framework->dump_error); + +#get stock image for valid transaction +$t->get_ok("/admin/transactions/1/image") + ->status_is(200)->or($framework->dump_error); + +#Delete valid transaction +$t->post_ok("/admin/transactions/1/delete") + ->status_is(200)->or($framework->dump_error) + ->content_like(qr/Successfully deleted transaction/); + +is $schema->resultset('Transaction')->count, 86; + +#Read deleted transaction +$t->get_ok("/admin/transactions/1") + ->content_like(qr/No transaction found/); + +sub create_random_transaction { + my $buyer = shift; + my $time = shift; + + my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity; + my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity; + $schema->resultset('Transaction')->create({ + buyer => $buyer_result, + seller => $seller_result, + value => 10, + purchase_time => $time, + }); +} + +done_testing; diff --git a/t/admin/user.t b/t/admin/user.t index 129dce6..58ba1e0 100644 --- a/t/admin/user.t +++ b/t/admin/user.t @@ -1,49 +1,17 @@ use Mojo::Base -strict; +use FindBin qw/ $Bin /; + use Test::More; -use Mojo::JSON; use Test::Pear::LocalLoop; -my $framework = Test::Pear::LocalLoop->new; +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../etc", +); +$framework->install_fixtures('users'); my $t = $framework->framework; my $schema = $t->app->schema; -my $user = $schema->resultset('User')->create({ - email => 'admin@example.com', - password => 'abc123', - administrator => {}, -}); - -is $schema->resultset('Administrator')->count, 1, 'Admin Created'; - -my $user1 = { - token => 'a', - full_name => 'Test User1', - display_name => 'Test User1', - email => 'test1@example.com', - postcode => 'LA1 1AA', - password => 'abc123', - year_of_birth => 2006, -}; - -my $org = { - token => 'e', - email => 'test50@example.com', - name => '7th Heaven', - street_name => 'Slums, Sector 7', - town => 'Midgar', - sector => 'A', - postcode => 'WC1E 6AD', - password => 'abc123', -}; - -$schema->resultset('AccountToken')->create({ name => $_->{token} }) - for ( $user1, $org ); - -$framework->register_customer($user1); - -$framework->register_organisation($org); - #login to admin $t->ua->max_redirects(10); $t->post_ok('/admin', form => { @@ -51,27 +19,36 @@ $t->post_ok('/admin', form => { password => 'abc123', })->status_is(200); +$t->get_ok('/admin/users') + ->status_is(200) + ->or($framework->dump_error); + #Read customer user -$t->get_ok('/admin/users/2/') +$t->get_ok('/admin/users/1') ->status_is(200); #Read organisation user -$t->get_ok('/admin/users/3/') +$t->get_ok('/admin/users/5') ->status_is(200); #Valid customer user update -$t->post_ok('/admin/users/2/edit', form => { - email => 'test12@example.com', - new_password => 'abc123', - full_name => 'Test User1', - display_name => 'Test User1', - town => 'Midgar', - sector => 'A', - postcode => 'WC1E 6AD', -})->status_is(200)->content_like(qr/Updated User/); +$t->post_ok( + '/admin/users/1', + form => { + email => 'test12@example.com', + new_password => 'abc123', + full_name => 'Test User1', + display_name => 'Test User1', + town => 'Midgar', + sector => 'A', + postcode => 'WC1E 6AD', + }) + ->status_is(200) + ->or($framework->dump_error) + ->content_like(qr/Updated User/); #Failed validation on customer user from no postcode -$t->post_ok('/admin/users/2/edit', form => { +$t->post_ok('/admin/users/2', form => { email => 'test12@example.com', new_password => 'abc123', full_name => 'Test User1', @@ -81,7 +58,7 @@ $t->post_ok('/admin/users/2/edit', form => { })->content_like(qr/The validation has failed/); #Failed validation on customer user from no display name -$t->post_ok('/admin/users/2/edit', form => { +$t->post_ok('/admin/users/2', form => { email => 'test12@example.com', new_password => 'abc123', full_name => 'Test User1', @@ -91,7 +68,7 @@ $t->post_ok('/admin/users/2/edit', form => { })->content_like(qr/The validation has failed/); #Valid organisation user update -$t->post_ok('/admin/users/3/edit', form => { +$t->post_ok('/admin/users/5', form => { email => 'test51@example.com', new_password => 'abc123', name => '7th Heaven', @@ -102,7 +79,7 @@ $t->post_ok('/admin/users/3/edit', form => { })->status_is(200)->content_like(qr/Updated User/); #Failed validation on organisation user from no postcode -$t->post_ok('/admin/users/3/edit', form => { +$t->post_ok('/admin/users/5', form => { email => 'test50@example.com', new_password => 'abc123', name => '7th Heaven', @@ -112,7 +89,7 @@ $t->post_ok('/admin/users/3/edit', form => { })->content_like(qr/The validation has failed/); #Failed validation on organisation user from no street name -$t->post_ok('/admin/users/3/edit', form => { +$t->post_ok('/admin/users/5', form => { email => 'test50@example.com', new_password => 'abc123', name => '7th Heaven', diff --git a/t/api/search.t b/t/api/search.t index dbc688d..561e437 100644 --- a/t/api/search.t +++ b/t/api/search.t @@ -1,122 +1,46 @@ use Mojo::Base -strict; +use FindBin qw/ $Bin /; + use Test::More; use Mojo::JSON; use Test::Pear::LocalLoop; -my $framework = Test::Pear::LocalLoop->new; +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../etc", +); +$framework->install_fixtures('search'); + my $t = $framework->framework; my $schema = $t->app->schema; -my $dump_error = sub { diag $t->tx->res->to_string }; -my @account_tokens = ('a', 'b'); -$schema->resultset('AccountToken')->populate([ - [ qw/ name / ], - map { [ $_ ] } @account_tokens, -]); +#Login as customer +my $session_key = $framework->login({ + 'email' => 'test1@example.com', + 'password' => 'abc123', +}); -$schema->resultset('Organisation')->populate([ - [ qw/ name street_name town postcode / ], - [ "Avanti Bar & Restaurant", "57 Main St", "Kirkby Lonsdale", "LA6 2AH" ], - [ "Full House Noodle Bar", "21 Common Garden St", "Lancaster", "LA1 1XD" ], - [ "The Quay's Fishbar", "1 Adcliffe Rd", "Lancaster", "LA1 1SS" ], - [ "Dan's Fishop", "56 North Rd", "Lancaster", "LA1 1LT" ], - [ "Hodgeson's Chippy", "96 Prospect St", "Lancaster", "LA1 3BH" ], -]); +my $json; +my $upload; -#test with a customer. -print "test 1 - Create customer user account (Rufus)\n"; -my $emailRufus = 'rufus@shinra.energy'; -my $passwordRufus = 'MakoGold'; -my $testJson = { - 'usertype' => 'customer', - 'token' => shift(@account_tokens), - 'full_name' => 'RufusShinra', - 'display_name' => 'RufusShinra', - 'email' => $emailRufus, - 'postcode' => 'RG26 5NU', - 'password' => $passwordRufus, - 'year_of_birth' => 2006 -}; -$t->post_ok('/api/register' => json => $testJson) - ->status_is(200)->or($framework->dump_error) - ->json_is('/success', Mojo::JSON->true); - -#test with an organisation. -print "test 2 - Create organisation user account (Choco Billy)\n"; -my $emailBilly = 'choco.billy@chocofarm.org'; -my $passwordBilly = 'Choco'; -$testJson = { - 'usertype' => 'organisation', - 'token' => shift(@account_tokens), - 'name' => 'ChocoBillysGreens', - 'email' => $emailBilly, - 'postcode' => 'LA1 1HT', - 'password' => $passwordBilly, - 'street_name' => 'Market St', - 'town' => 'Lancaster', - 'sector' => 'A', -}; -$t->post_ok('/api/register' => json => $testJson) +$t->post_ok( '/api/upload', form => { + json => Mojo::JSON::encode_json({ + transaction_value => 10, + transaction_type => 3, + organisation_name => 'Shoreway Fisheries', + street_name => "2 James St", + town => "Lancaster", + postcode => "LA1 1UP", + purchase_time => "2017-08-14T11:29:07.965+01:00", + session_key => $session_key, + }), + file => { file => './t/test.jpg' }, + }) ->status_is(200) + ->or($framework->dump_error) ->json_is('/success', Mojo::JSON->true); -my $session_key; - -sub login_rufus { - $testJson = { - 'email' => $emailRufus, - 'password' => $passwordRufus, - }; - $t->post_ok('/api/login' => json => $testJson) - ->status_is(200) - ->json_is('/success', Mojo::JSON->true); - $session_key = $t->tx->res->json('/session_key'); -}; - -sub login_billy { - $testJson = { - 'email' => $emailBilly, - 'password' => $passwordBilly, - }; - $t->post_ok('/api/login' => json => $testJson) - ->status_is(200) - ->json_is('/success', Mojo::JSON->true); - $session_key = $t->tx->res->json('/session_key'); -}; - -sub log_out{ - $t->post_ok('/api/logout', json => { session_key => $session_key }) - ->status_is(200) - ->json_is('/success', Mojo::JSON->true); -} - - -###################################################### - -#Login as Rufus (customer) - -print "test 3 - Login - Rufus (cookies, customer)\n"; -login_rufus(); - -print "test 4 - Added something containing 'fish'\n"; -my $json = { - transaction_value => 10, - transaction_type => 3, - organisation_name => 'Shoreway Fisheries', - street_name => "2 James St", - town => "Lancaster", - postcode => "LA1 1UP", - purchase_time => "2017-08-14T11:29:07.965+01:00", - session_key => $session_key, -}; -my $upload = {json => Mojo::JSON::encode_json($json), file => {file => './t/test.jpg'}}; -$t->post_ok('/api/upload' => form => $upload ) - ->status_is(200) - ->json_is('/success', Mojo::JSON->true); - -print "test 5 - Logout Rufus \n"; -log_out(); +$framework->logout( $session_key ); #End of Rufus (customer) @@ -125,7 +49,10 @@ log_out(); #Login as Choco billy (organisation) print "test 6 - Login - Choco billy (cookies, organisation)\n"; -login_billy(); +$session_key = $framework->login({ + 'email' => 'org@example.com', + 'password' => 'abc123', +}); print "test 7 - Added something containing 'bar'\n"; $json = { @@ -160,16 +87,12 @@ $t->post_ok('/api/upload' => form => $upload ) ->json_is('/success', Mojo::JSON->true); print "test 9 - Logout Choco billy \n"; -log_out(); +$framework->logout( $session_key ); -#End of Choco billy (organisation) - -###################################################### - -#Login as Rufus (customer) - -print "test 10 - Login - Rufus (cookies, customer)\n"; -login_rufus(); +$session_key = $framework->login({ + 'email' => 'test1@example.com', + 'password' => 'abc123', +}); sub check_vars{ my ($searchTerm, $numValidated, $numUnvalidated) = @_; @@ -179,6 +102,7 @@ sub check_vars{ session_key => $session_key, }) ->status_is(200) + ->or($framework->dump_error) ->json_is('/success', Mojo::JSON->true) ->json_has("unvalidated") ->json_has("validated"); @@ -196,7 +120,7 @@ sub check_vars{ }; print "test 11 - search blank\n"; -check_vars(" ", 5, 1); +check_vars(" ", 6, 1); print "test 12 - Testing expected values with 'booths'\n"; #Expect 0 validated and 0 unvalidated with "booths". @@ -215,7 +139,7 @@ print "test 15 - Testing expected values with 'bar'\n"; check_vars("bar", 3, 0); print "test 16 - Logout Rufus \n"; -log_out(); +$framework->logout( $session_key ); #End of Rufus (customer) @@ -224,7 +148,10 @@ log_out(); #Login as Choco billy (organisation) print "test 17 - Login - Choco billy (cookies, organisation)\n"; -login_billy(); +$session_key = $framework->login({ + 'email' => 'org@example.com', + 'password' => 'abc123', +}); print "test 18 - Testing expected values with 'booths'\n"; #Expect 0 validated and 0 unvalidated with "booths". @@ -243,7 +170,6 @@ print "test 21 - Testing expected values with 'bar', with two unvalidated organi check_vars("bar", 3, 2); print "test 22 - Logout Choco billy \n"; -log_out(); - +$framework->logout( $session_key ); done_testing(); diff --git a/t/api/stats.t b/t/api/stats.t index 2a6d889..0484664 100644 --- a/t/api/stats.t +++ b/t/api/stats.t @@ -1,54 +1,33 @@ use Mojo::Base -strict; +use FindBin qw/ $Bin /; + use Test::More; use Mojo::JSON; use Test::Pear::LocalLoop; use DateTime; -my $framework = Test::Pear::LocalLoop->new; +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../etc", +); +$framework->install_fixtures('users'); + my $t = $framework->framework; my $schema = $t->app->schema; my $dtf = $schema->storage->datetime_parser; -my $user = { - token => 'a', - full_name => 'Test User', - display_name => 'Test User', - email => 'test@example.com', - postcode => 'LA1 1AA', - password => 'abc123', - year_of_birth => 2006, -}; - -my $org = { - token => 'b', - email => 'test2@example.com', - name => 'Test Org', - street_name => 'Test Street', - town => 'Lancaster', - postcode => 'LA1 1AA', - password => 'abc123', - sector => 'A', -}; - -$schema->resultset('AccountToken')->create({ name => $user->{token} }); -$schema->resultset('AccountToken')->create({ name => $org->{token} }); - -$framework->register_customer($user); -$framework->register_organisation($org); - -my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); -my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); +my $org_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity; +my $user_result = $schema->resultset('User')->find({ email => 'test1@example.com' })->entity; my $session_key = $framework->login({ - email => $user->{email}, - password => $user->{password}, + email => 'test1@example.com', + password => 'abc123', }); $t->app->schema->resultset('Leaderboard')->create_new( 'monthly_total', DateTime->now->truncate(to => 'month' )->subtract( months => 1) ); $t->post_ok('/api/stats' => json => { session_key => $session_key } ) - ->status_is(200) + ->status_is(200)->or($framework->dump_error) ->json_is('/success', Mojo::JSON->true) ->json_is('/today_sum', 0) ->json_is('/today_count', 0) @@ -62,58 +41,58 @@ $t->post_ok('/api/stats' => json => { session_key => $session_key } ) ->json_is('/global_count', 0); for ( 1 .. 10 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, - value => $_, + value => $_ * 100000, proof_image => 'a', }); } for ( 11 .. 20 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, - value => $_, + value => $_ * 100000, proof_image => 'a', purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 5 )), }); } for ( 21 .. 30 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, - value => $_, + value => $_ * 100000, proof_image => 'a', purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 25 )), }); } for ( 31 .. 40 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, - value => $_, + value => $_ * 100000, proof_image => 'a', purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )), }); } for ( 41 .. 50 ) { - $org_result->user->create_related( 'transactions', { + $org_result->create_related( 'purchases', { seller_id => $org_result->id, - value => $_, + value => $_ * 100000, proof_image => 'a', purchase_time => $dtf->format_datetime(DateTime->today()->subtract( days => 50 )), }); } -is $user_result->transactions->search({ +is $user_result->purchases->search({ purchase_time => { -between => [ $dtf->format_datetime(DateTime->today()), $dtf->format_datetime(DateTime->today()->add( days => 1 )), ], }, -})->get_column('value')->sum, 55, 'Got correct sum'; -is $user_result->transactions->today_rs->get_column('value')->sum, 55, 'Got correct sum through rs'; +})->get_column('value')->sum, 5500000, 'Got correct sum'; +is $user_result->purchases->today_rs->get_column('value')->sum, 5500000, 'Got correct sum through rs'; $t->post_ok('/api/stats' => json => { session_key => $session_key } ) ->status_is(200) diff --git a/t/api/stats_leaderboards.t b/t/api/stats_leaderboards.t index 11003c2..84eb331 100644 --- a/t/api/stats_leaderboards.t +++ b/t/api/stats_leaderboards.t @@ -69,22 +69,22 @@ $framework->register_customer($_) $framework->register_organisation($org); -my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); +my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} })->entity; my $tweak = 0; my $now = DateTime->today(); { - my $user_result = $schema->resultset('User')->find({ email => $user1->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user1->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 1, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 9, proof_image => 'a', @@ -93,15 +93,15 @@ my $now = DateTime->today(); } { - my $user_result = $schema->resultset('User')->find({ email => $user2->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user2->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 3, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 1, proof_image => 'a', @@ -110,15 +110,15 @@ my $now = DateTime->today(); } { - my $user_result = $schema->resultset('User')->find({ email => $user3->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user3->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 5, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 5, proof_image => 'a', @@ -127,15 +127,15 @@ my $now = DateTime->today(); } { - my $user_result = $schema->resultset('User')->find({ email => $user4->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user4->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 9, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 3, proof_image => 'a', diff --git a/t/api/transactions.t b/t/api/transactions.t new file mode 100644 index 0000000..849a2a8 --- /dev/null +++ b/t/api/transactions.t @@ -0,0 +1,69 @@ +use Mojo::Base -strict; + +use FindBin qw/ $Bin /; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../etc", +); +$framework->install_fixtures('users'); + +my $t = $framework->framework; +my $schema = $t->app->schema; + +my $start = DateTime->today->subtract( hours => 12 ); + +# create 30 days worth of data +for my $count ( 0 .. 29 ) { + my $trans_day = $start->clone->subtract( days => $count ); + + create_random_transaction( 'test1@example.com', $trans_day ); + if ( $count % 2 ) { + create_random_transaction( 'test2@example.com', $trans_day ); + } + if ( $count % 3 ) { + create_random_transaction( 'test3@example.com', $trans_day ); + } + if ( $count % 4 ) { + create_random_transaction( 'test4@example.com', $trans_day ); + } +} + +my $session_key = $framework->login({ + email => 'test1@example.com', + password => 'abc123', +}); + +use Data::Dumper; + +$t->post_ok('/api/outgoing-transactions' => json => { + session_key => $session_key, + }) + ->status_is(200)->or($framework->dump_error) + ->json_is('/success', Mojo::JSON->true) + ->json_has('/transactions') + ->json_has('/transactions/1/seller') + ->json_has('/transactions/1/value') + ->json_has('/transactions/1/purchase_time'); + + +sub create_random_transaction { + my $buyer = shift; + my $time = shift; + + my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity; + my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity; + $schema->resultset('Transaction')->create({ + buyer => $buyer_result, + seller => $seller_result, + value => 10, + proof_image => 'a', + purchase_time => $time, + }); +} + +done_testing; diff --git a/t/api/upload.t b/t/api/upload.t index ca925cf..74b3409 100644 --- a/t/api/upload.t +++ b/t/api/upload.t @@ -20,24 +20,25 @@ $schema->resultset('AccountToken')->populate([ my $test_purchase_time = "2017-08-14T11:29:07.965+01:00"; #Add one company that we've apparently authenticated but does not have an account. -my $org_id_shinra = 1; my $org_rs = $schema->resultset('Organisation'); is $org_rs->count, 0, "No organisations"; -$org_rs->create({ - id => $org_id_shinra, +my $shinra_entity = $schema->resultset('Entity')->create_org({ name => 'Shinra Electric Power Company', street_name => 'Sector 0, Midgar, Eastern Continent', town => 'Gaia', postcode => 'E1 M00', + submitted_by_id => 1, }); is $org_rs->count, 1, "1 testing organisation"; +my $org_id_shinra = $shinra_entity->organisation->id; + #Valid customer, this also tests that redirects are disabled for register. print "test 1 - Create customer user account (Rufus)\n"; -my $emailRufus = 'rufus@shinra.energy'; -my $passwordRufus = 'MakoGold'; +my $emailRufus = 'test1@example.com'; +my $passwordRufus = 'abc123'; my $testJson = { 'usertype' => 'customer', 'token' => shift(@account_tokens), @@ -200,11 +201,11 @@ $json = { organisation_id => 1, session_key => $session_key, }; -$upload = {json => Mojo::JSON::encode_json($json)}; -$t->post_ok('/api/upload' => form => $upload ) -->status_is(200) -->json_is('/success', Mojo::JSON->true) -->json_like('/message', qr/Upload Successful/); +$t->post_ok('/api/upload' => json => $json ) + ->status_is(200) + ->or($framework->dump_error) + ->json_is('/success', Mojo::JSON->true) + ->json_like('/message', qr/Upload Successful/); is $schema->resultset('Transaction')->count, 1, "1 transaction"; print "test 13 - organisation_id missing (type 1: already validated)\n"; @@ -270,8 +271,8 @@ $t->post_ok('/api/upload' => form => $upload ) ->content_like(qr/organisation_name is missing/i); print "test 17 - add valid transaction (type 3: new organisation)\n"; -is $schema->resultset('PendingOrganisation')->count, 0, "No pending organisations"; -is $schema->resultset('PendingTransaction')->count, 0, "No pending transactions"; +is $schema->resultset('Organisation')->search({ pending => 1 })->count, 0, "No pending organisations"; +is $schema->resultset('Organisation')->search({ pending => 1 })->entity->sales->count, 0, "No pending transactions"; $json = { transaction_value => 10, @@ -288,8 +289,8 @@ $t->post_ok('/api/upload' => form => $upload ) ->status_is(200) ->json_is('/success', Mojo::JSON->true) ->json_like('/message', qr/Upload Successful/); -is $schema->resultset('PendingOrganisation')->count, 1, "1 pending organisations"; -is $schema->resultset('PendingTransaction')->count, 1, "1 pending transactions"; +is $schema->resultset('Organisation')->search({ pending => 1 })->count, 1, "1 pending organisations"; +is $schema->resultset('Organisation')->search({ pending => 1 })->entity->sales->count, 1, "1 pending transactions"; # Add type 2 (unverified organisation) checking. @@ -303,6 +304,7 @@ $json = { $upload = {json => Mojo::JSON::encode_json($json), file => {file => './t/test.jpg'}}; $t->post_ok('/api/upload' => form => $upload ) ->status_is(400) + ->or($framework->dump_error) ->json_is('/success', Mojo::JSON->false) ->content_like(qr/organisation_id is missing/i); @@ -335,10 +337,10 @@ $t->post_ok('/api/upload' => form => $upload ) ->content_like(qr/organisation_id does not exist in the database/i); print "test 21 - purchase_time is missing\n"; -is $schema->resultset('PendingTransaction')->count, 1, "1 pending transactions"; +is $schema->resultset('Organisation')->search({ pending => 1 })->entity->sales->count, 1, "1 pending transactions"; $json = { transaction_value => 10, - transaction_type => 2, + transaction_type => 1, organisation_id => $org_id_shinra, session_key => $session_key, }; @@ -369,9 +371,9 @@ $t->post_ok('/api/login' => json => $testJson) $session_key = $t->tx->res->json('/session_key'); print "test 24 - add valid transaction but for with account (type 2: existing organisation)\n"; -my $org_result = $schema->resultset('PendingOrganisation')->find({ name => '7th Heaven' }); +my $org_result = $schema->resultset('Organisation')->find({ name => '7th Heaven' }); my $unvalidatedOrganisationId = $org_result->id; -is $schema->resultset('PendingTransaction')->count, 2, "2 pending transactions"; +is $schema->resultset('Organisation')->search({ pending => 1 })->entity->sales->count, 1, "1 pending transactions"; $json = { transaction_value => 10, transaction_type => 2, @@ -384,7 +386,7 @@ $t->post_ok('/api/upload' => form => $upload ) ->status_is(400) ->json_is('/success', Mojo::JSON->false) ->content_like(qr/organisation_id does not exist in the database/i); -is $schema->resultset('PendingTransaction')->count, 2, "2 pending transactions"; +is $schema->resultset('Organisation')->search({ pending => 1 })->entity->sales->count, 1, "1 pending transactions"; print "test 25 - Logout Hojo\n"; $t->post_ok('/api/logout', json => { session_key => $session_key } ) @@ -408,7 +410,7 @@ $t->post_ok('/api/login' => json => $testJson) $session_key = $t->tx->res->json('/session_key'); print "test 27 - add valid transaction (type 2: existing organisation)\n"; -is $schema->resultset('PendingTransaction')->count, 2, "2 pending transactions"; +is $schema->resultset('Organisation')->search({ pending => 1 })->entity->sales->count, 1, "1 pending transactions"; $json = { transaction_value => 10, transaction_type => 2, @@ -421,7 +423,7 @@ $t->post_ok('/api/upload' => form => $upload ) ->status_is(200) ->json_is('/success', Mojo::JSON->true) ->json_like('/message', qr/Upload Successful/); -is $schema->resultset('PendingTransaction')->count, 3, "3 pending transactions"; +is $schema->resultset('Organisation')->search({ pending => 1 })->entity->sales->count, 2, "2 pending transactions"; print "test 28 - Logout Rufus\n"; @@ -446,7 +448,7 @@ $t->post_ok('/api/login' => json => $testJson) $session_key = $t->tx->res->json('/session_key'); print "test 30 - organisation buy from another organisation\n"; -is $schema->resultset('Transaction')->count, 2, "2 transaction"; +is $schema->resultset('Transaction')->count, 5, "5 transaction"; $json = { transaction_value => 100000, transaction_type => 1, @@ -459,6 +461,21 @@ $t->post_ok('/api/upload' => form => $upload ) ->status_is(200) ->json_is('/success', Mojo::JSON->true) ->json_like('/message', qr/Upload Successful/); -is $schema->resultset('Transaction')->count, 3, "3 transaction"; +is $schema->resultset('Transaction')->count, 6, "6 transaction"; + +print "test 31 - organisation buy from same organisation\n"; +$json = { + transaction_value => 100000, + transaction_type => 1, + purchase_time => $test_purchase_time, + organisation_id => 2, + session_key => $session_key, +}; +$upload = {json => Mojo::JSON::encode_json($json), file => {file => './t/test.jpg'}}; +$t->post_ok('/api/upload' => form => $upload ) + ->status_is(400) + ->json_is('/success', Mojo::JSON->false) + ->json_like('/message', qr/organisation_id does not exist in the database/); +is $schema->resultset('Transaction')->count, 6, "6 transaction"; done_testing(); diff --git a/t/api/user.t b/t/api/user.t index a848e95..643165a 100644 --- a/t/api/user.t +++ b/t/api/user.t @@ -32,28 +32,6 @@ my $session_key = $framework->login({ password => $password, }); -my $json_no_date = { session_key => $session_key }; -$t->post_ok('/api/user/day', json => $json_no_date) - ->status_is(200)->or($framework->dump_error) - ->json_is('/success', Mojo::JSON->true); - -my $json_invalid_date = { - session_key => $session_key, - day => 'invalid', -}; -$t->post_ok('/api/user/day', json => $json_invalid_date) - ->status_is(400)->or($framework->dump_error) - ->json_is('/success', Mojo::JSON->false) - ->json_like('/message', qr/Invalid ISO8601 Datetime/); - -my $json_valid_date = { - session_key => $session_key, - day => $t->app->datetime_formatter->format_datetime(DateTime->now), -}; -$t->post_ok('/api/user/day', json => $json_valid_date) - ->status_is(200)->or($framework->dump_error) - ->json_is('/success', Mojo::JSON->true); - $t->post_ok('/api/user', json => { session_key => $session_key }) ->status_is(200)->or($framework->dump_error) ->json_is({ diff --git a/t/api/v1/organisation/graphs.t b/t/api/v1/organisation/graphs.t index d6885f3..57b6cbb 100644 --- a/t/api/v1/organisation/graphs.t +++ b/t/api/v1/organisation/graphs.t @@ -44,8 +44,8 @@ $t->post_ok('/api/v1/organisation/graphs' => json => { }) ->status_is(200)->or($framework->dump_error) ->json_is('/graph', { - day => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 6 ) ], - count => [ 2, 4, 2, 3, 3, 4, 1 ], + labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 6 ) ], + data => [ 2, 4, 2, 3, 3, 4, 1 ], }); $t->post_ok('/api/v1/organisation/graphs' => json => { @@ -54,8 +54,40 @@ $t->post_ok('/api/v1/organisation/graphs' => json => { }) ->status_is(200)->or($framework->dump_error) ->json_is('/graph', { - day => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 29 ) ], - count => [ 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1 ], + labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 29 ) ], + data => [ 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 2, 4, 2, 3, 3, 4, 1 ], + }); + +$t->post_ok('/api/v1/organisation/graphs' => json => { + session_key => $session_key, + graph => 'sales_last_7_days', + }) + ->status_is(200)->or($framework->dump_error) + ->json_is('/graph', { + labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 6 ) ], + data => [ 20, 40, 20, 30, 30, 40, 10 ], + }); + +$t->post_ok('/api/v1/organisation/graphs' => json => { + session_key => $session_key, + graph => 'sales_last_30_days', + }) + ->status_is(200)->or($framework->dump_error) + ->json_is('/graph', { + labels => [ map { $start->clone->subtract( days => $_ )->day_name } reverse ( 0 .. 29 ) ], + data => [ 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10, 40, 30, 30, 20, 40, 20, 40, 20, 30, 30, 40, 10 ], + }); + +$t->post_ok('/api/v1/organisation/graphs' => json => { + session_key => $session_key, + graph => 'customers_range', + start => $start->clone->subtract( days => 8 )->ymd, + end => $start->clone->ymd, + }) + ->status_is(200)->or($framework->dump_error) + ->json_is('/graph', { + labels => [ map { $start->clone->subtract( days => $_ )->ymd } reverse ( 0 .. 8 ) ], + data => [ 2, 4, 2, 4, 2, 3, 3, 4, 1 ], }); $framework->logout( $session_key ); @@ -73,14 +105,17 @@ $t->post_ok('/api/v1/organisation/graphs' => json => { ->json_is('/success', Mojo::JSON->false) ->json_is('/error', 'user_not_org'); + sub create_random_transaction { my $buyer = shift; my $time = shift; + my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity; + my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity; $schema->resultset('Transaction')->create({ - buyer => { email => $buyer }, - seller => { name => 'Test Org' }, - value => ( int( rand( 10000 ) ) / 100 ), + buyer => $buyer_result, + seller => $seller_result, + value => 10 * 100000, proof_image => 'a', purchase_time => $time, }); diff --git a/t/api/v1/organisation/snippets.t b/t/api/v1/organisation/snippets.t new file mode 100644 index 0000000..f4ee79c --- /dev/null +++ b/t/api/v1/organisation/snippets.t @@ -0,0 +1,82 @@ +use Mojo::Base -strict; + +use FindBin qw/ $Bin /; + +use Test::More; +use Mojo::JSON; +use Test::Pear::LocalLoop; +use DateTime; + +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/../../../etc", +); +$framework->install_fixtures('users'); + +my $t = $framework->framework; +my $schema = $t->app->schema; + +my $start = DateTime->today->subtract( hours => 12 ); + +# create 30 days worth of data +for my $count ( 0 .. 29 ) { + my $trans_day = $start->clone->subtract( days => $count ); + + create_random_transaction( 'test1@example.com', $trans_day ); + if ( $count % 2 ) { + create_random_transaction( 'test2@example.com', $trans_day ); + } + if ( $count % 3 ) { + create_random_transaction( 'test3@example.com', $trans_day ); + } + if ( $count % 4 ) { + create_random_transaction( 'test4@example.com', $trans_day ); + } +} + +my $session_key = $framework->login({ + email => 'org@example.com', + password => 'abc123', +}); + +$t->post_ok('/api/v1/organisation/snippets' => json => { + session_key => $session_key, + }) + ->status_is(200)->or($framework->dump_error) + ->json_is('/snippets', { + this_month_sales_count => 87, + this_month_sales_total => 870, + this_week_sales_count => 19, + this_week_sales_total => 190, + today_sales_count => 0, + today_sales_total => 0, + this_month_purchases_count => 0, + this_month_purchases_total => 0, + this_week_purchases_count => 0, + this_week_purchases_total => 0, + today_purchases_count => 0, + today_purchases_total => 0, + }); + +$framework->logout( $session_key ); + +$session_key = $framework->login({ + email => 'test1@example.com', + password => 'abc123', +}); + +sub create_random_transaction { + my $buyer = shift; + my $time = shift; + + my $buyer_result = $schema->resultset('User')->find({ email => $buyer })->entity; + my $seller_result = $schema->resultset('Organisation')->find({ name => 'Test Org' })->entity; + $schema->resultset('Transaction')->create({ + buyer => $buyer_result, + seller => $seller_result, + value => 10 * 100000, + proof_image => 'a', + purchase_time => $time, + }); +} + +done_testing; diff --git a/t/basic.t b/t/basic.t index 9a14ba3..666e762 100644 --- a/t/basic.t +++ b/t/basic.t @@ -1,15 +1,16 @@ use Mojo::Base -strict; + +use FindBin qw/ $Bin /; + use Test::More; -use Test::Mojo; +use Test::Pear::LocalLoop; -use FindBin; +my $framework = Test::Pear::LocalLoop->new( + etc_dir => "$Bin/etc", +); +$framework->install_fixtures('users'); -BEGIN { - $ENV{MOJO_MODE} = 'testing'; - $ENV{MOJO_LOG_LEVEL} = 'debug'; -} - -my $t = Test::Mojo->new("Pear::LocalLoop"); +my $t = $framework->framework; $t->get_ok('/')->status_is(200); done_testing(); diff --git a/t/etc/fixtures/config/search.pl b/t/etc/fixtures/config/search.pl new file mode 100644 index 0000000..78c869c --- /dev/null +++ b/t/etc/fixtures/config/search.pl @@ -0,0 +1,88 @@ +#! /usr/bin/env perl + +use strict; +use warnings; + +use DBIx::Class::Fixtures; +use FindBin qw/ $Bin /; +use lib "$Bin/../../../../lib"; +use Pear::LocalLoop::Schema; +use DateTime; + +my $fixtures = DBIx::Class::Fixtures->new({ + config_dir => "$Bin", +}); + +my $schema = Pear::LocalLoop::Schema->connect('dbi:SQLite::memory:'); + +$schema->deploy; + +$fixtures->populate({ + directory => "$Bin/../data/users", + no_deploy => 1, + schema => $schema, +}); + +my @orgs = ( + { + organisation => { + name => 'Avanti Bar & Restaurant', + street_name => '57 Main St', + town => 'Kirkby Lonsdale', + postcode => 'LA6 2AH', + sector => 'I', + }, + type => "organisation", + }, + { + organisation => { + name => 'Full House Noodle Bar', + street_name => '21 Common Garden St', + town => 'Lancaster', + postcode => 'LA1 1XD', + sector => 'I', + }, + type => "organisation", + }, + { + organisation => { + name => 'The Quay\'s Fishbar', + street_name => '1 Adcliffe Rd', + town => 'Lancaster', + postcode => 'LA1 1SS', + sector => 'I', + }, + type => "organisation", + }, + { + organisation => { + name => 'Dan\'s Fishop', + street_name => '56 North Rd', + town => 'Lancaster', + postcode => 'LA1 1LT', + sector => 'I', + }, + type => "organisation", + }, + { + organisation => { + name => 'Hodgeson\'s Chippy', + street_name => '96 Prospect St', + town => 'Lancaster', + postcode => 'LA1 3BH', + sector => 'I', + }, + type => "organisation", + }, +); + +$schema->resultset('Entity')->create( $_ ) for @orgs; + +my $data_set = 'search'; + +$fixtures->dump({ + all => 1, + schema => $schema, + directory => "$Bin/../data/" . $data_set, +}); + diff --git a/t/etc/fixtures/config/users.pl b/t/etc/fixtures/config/users.pl index 676592b..9a7f5ca 100644 --- a/t/etc/fixtures/config/users.pl +++ b/t/etc/fixtures/config/users.pl @@ -29,63 +29,93 @@ $schema->resultset('Leaderboard')->populate([ [ 'All Time Count', 'all_time_count' ], ]); -my $user1 = { +my $entity1 = { customer => { full_name => 'Test User1', display_name => 'Test User1', postcode => 'LA1 1AA', year_of_birth => 2006, }, - email => 'test1@example.com', - password => 'abc123', + user => { + email => 'test1@example.com', + password => 'abc123', + }, + type => "customer", }; -my $user2 = { +my $entity2 = { customer => { - full_name => 'Test User2', - display_name => 'Test User2', - postcode => 'LA1 1AA', + full_name => 'Test User2', + display_name => 'Test User2', + postcode => 'LA1 1AA', year_of_birth => 2006, }, - email => 'test2@example.com', - password => 'abc123', + user => { + email => 'test2@example.com', + password => 'abc123', + }, + type => "customer", }; -my $user3 = { +my $entity3 = { customer => { - full_name => 'Test User3', - display_name => 'Test User3', - postcode => 'LA1 1AA', + full_name => 'Test User3', + display_name => 'Test User3', + postcode => 'LA1 1AA', year_of_birth => 2006, }, - email => 'test3@example.com', - password => 'abc123', + user => { + email => 'test3@example.com', + password => 'abc123', + }, + type => "customer", }; -my $user4 = { +my $entity4 = { customer => { - full_name => 'Test User4', - display_name => 'Test User4', - postcode => 'LA1 1AA', + full_name => 'Test User4', + display_name => 'Test User4', + postcode => 'LA1 1AA', year_of_birth => 2006, }, - email => 'test4@example.com', - password => 'abc123', + user => { + email => 'test4@example.com', + password => 'abc123', + }, + type => "customer", }; -my $org = { +my $entity5 = { organisation => { name => 'Test Org', street_name => 'Test Street', town => 'Lancaster', postcode => 'LA1 1AA', }, - email => 'org@example.com', - password => 'abc123', + user => { + email => 'org@example.com', + password => 'abc123', + }, + type => "organisation", }; -$schema->resultset('User')->create( $_ ) - for ( $user1, $user2, $user3, $user4, $org ); +my $entity6 = { + customer => { + full_name => 'Test Admin', + display_name => 'Test Admin', + postcode => 'LA1 1AA', + year_of_birth => 2006, + }, + user => { + email => 'admin@example.com', + password => 'abc123', + is_admin => \"1", + }, + type => "customer", +}; + +$schema->resultset('Entity')->create( $_ ) + for ( $entity1, $entity2, $entity3, $entity4, $entity5, $entity6 ); my $data_set = 'users'; diff --git a/t/etc/fixtures/data/search/_config_set b/t/etc/fixtures/data/search/_config_set new file mode 100644 index 0000000..5f8269a --- /dev/null +++ b/t/etc/fixtures/data/search/_config_set @@ -0,0 +1,57 @@ +$VAR1 = { + 'has_many' => { + 'fetch' => 0 + }, + 'sets' => [ + { + 'class' => 'Feedback', + 'quantity' => 'all' + }, + { + 'quantity' => 'all', + 'class' => 'Transaction' + }, + { + 'quantity' => 'all', + 'class' => 'User' + }, + { + 'quantity' => 'all', + 'class' => 'LeaderboardSet' + }, + { + 'class' => 'Customer', + 'quantity' => 'all' + }, + { + 'quantity' => 'all', + 'class' => 'Organisation' + }, + { + 'quantity' => 'all', + 'class' => 'LeaderboardValue' + }, + { + 'class' => 'Entity', + 'quantity' => 'all' + }, + { + 'quantity' => 'all', + 'class' => 'Leaderboard' + }, + { + 'class' => 'SessionToken', + 'quantity' => 'all' + }, + { + 'quantity' => 'all', + 'class' => 'AccountToken' + } + ], + 'might_have' => { + 'fetch' => 0 + }, + 'belongs_to' => { + 'fetch' => 0 + } + }; diff --git a/t/etc/fixtures/data/search/_dumper_version b/t/etc/fixtures/data/search/_dumper_version new file mode 100644 index 0000000..8d0321e --- /dev/null +++ b/t/etc/fixtures/data/search/_dumper_version @@ -0,0 +1 @@ +1.001036 \ No newline at end of file diff --git a/t/etc/fixtures/data/search/customers/1.fix b/t/etc/fixtures/data/search/customers/1.fix new file mode 100644 index 0000000..705c22d --- /dev/null +++ b/t/etc/fixtures/data/search/customers/1.fix @@ -0,0 +1,8 @@ +$HASH1 = { + display_name => 'Test User1', + entity_id => 1, + full_name => 'Test User1', + id => 1, + postcode => 'LA1 1AA', + year_of_birth => 2006 + }; diff --git a/t/etc/fixtures/data/search/customers/2.fix b/t/etc/fixtures/data/search/customers/2.fix new file mode 100644 index 0000000..0fee4aa --- /dev/null +++ b/t/etc/fixtures/data/search/customers/2.fix @@ -0,0 +1,8 @@ +$HASH1 = { + display_name => 'Test User2', + entity_id => 2, + full_name => 'Test User2', + id => 2, + postcode => 'LA1 1AA', + year_of_birth => 2006 + }; diff --git a/t/etc/fixtures/data/search/customers/3.fix b/t/etc/fixtures/data/search/customers/3.fix new file mode 100644 index 0000000..a64e402 --- /dev/null +++ b/t/etc/fixtures/data/search/customers/3.fix @@ -0,0 +1,8 @@ +$HASH1 = { + display_name => 'Test User3', + entity_id => 3, + full_name => 'Test User3', + id => 3, + postcode => 'LA1 1AA', + year_of_birth => 2006 + }; diff --git a/t/etc/fixtures/data/search/customers/4.fix b/t/etc/fixtures/data/search/customers/4.fix new file mode 100644 index 0000000..49d0530 --- /dev/null +++ b/t/etc/fixtures/data/search/customers/4.fix @@ -0,0 +1,8 @@ +$HASH1 = { + display_name => 'Test User4', + entity_id => 4, + full_name => 'Test User4', + id => 4, + postcode => 'LA1 1AA', + year_of_birth => 2006 + }; diff --git a/t/etc/fixtures/data/search/customers/5.fix b/t/etc/fixtures/data/search/customers/5.fix new file mode 100644 index 0000000..50ef1c2 --- /dev/null +++ b/t/etc/fixtures/data/search/customers/5.fix @@ -0,0 +1,8 @@ +$HASH1 = { + display_name => 'Test Admin', + entity_id => 6, + full_name => 'Test Admin', + id => 5, + postcode => 'LA1 1AA', + year_of_birth => 2006 + }; diff --git a/t/etc/fixtures/data/search/entities/1.fix b/t/etc/fixtures/data/search/entities/1.fix new file mode 100644 index 0000000..a45e065 --- /dev/null +++ b/t/etc/fixtures/data/search/entities/1.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 1, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/search/entities/10.fix b/t/etc/fixtures/data/search/entities/10.fix new file mode 100644 index 0000000..918c437 --- /dev/null +++ b/t/etc/fixtures/data/search/entities/10.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 10, + type => 'organisation' + }; diff --git a/t/etc/fixtures/data/search/entities/11.fix b/t/etc/fixtures/data/search/entities/11.fix new file mode 100644 index 0000000..4f5536d --- /dev/null +++ b/t/etc/fixtures/data/search/entities/11.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 11, + type => 'organisation' + }; diff --git a/t/etc/fixtures/data/search/entities/2.fix b/t/etc/fixtures/data/search/entities/2.fix new file mode 100644 index 0000000..20849e3 --- /dev/null +++ b/t/etc/fixtures/data/search/entities/2.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 2, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/search/entities/3.fix b/t/etc/fixtures/data/search/entities/3.fix new file mode 100644 index 0000000..3fd6a9a --- /dev/null +++ b/t/etc/fixtures/data/search/entities/3.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 3, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/search/entities/4.fix b/t/etc/fixtures/data/search/entities/4.fix new file mode 100644 index 0000000..bacb15b --- /dev/null +++ b/t/etc/fixtures/data/search/entities/4.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 4, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/search/entities/5.fix b/t/etc/fixtures/data/search/entities/5.fix new file mode 100644 index 0000000..9aa374d --- /dev/null +++ b/t/etc/fixtures/data/search/entities/5.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 5, + type => 'organisation' + }; diff --git a/t/etc/fixtures/data/search/entities/6.fix b/t/etc/fixtures/data/search/entities/6.fix new file mode 100644 index 0000000..ee9f57f --- /dev/null +++ b/t/etc/fixtures/data/search/entities/6.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 6, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/search/entities/7.fix b/t/etc/fixtures/data/search/entities/7.fix new file mode 100644 index 0000000..beeb562 --- /dev/null +++ b/t/etc/fixtures/data/search/entities/7.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 7, + type => 'organisation' + }; diff --git a/t/etc/fixtures/data/search/entities/8.fix b/t/etc/fixtures/data/search/entities/8.fix new file mode 100644 index 0000000..da99144 --- /dev/null +++ b/t/etc/fixtures/data/search/entities/8.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 8, + type => 'organisation' + }; diff --git a/t/etc/fixtures/data/search/entities/9.fix b/t/etc/fixtures/data/search/entities/9.fix new file mode 100644 index 0000000..fe3b1f4 --- /dev/null +++ b/t/etc/fixtures/data/search/entities/9.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 9, + type => 'organisation' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/1.fix b/t/etc/fixtures/data/search/leaderboards/1.fix new file mode 100644 index 0000000..597a843 --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/1.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 1, + name => 'Daily Total', + type => 'daily_total' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/2.fix b/t/etc/fixtures/data/search/leaderboards/2.fix new file mode 100644 index 0000000..08fef2c --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/2.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 2, + name => 'Daily Count', + type => 'daily_count' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/3.fix b/t/etc/fixtures/data/search/leaderboards/3.fix new file mode 100644 index 0000000..4166f18 --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/3.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 3, + name => 'Weekly Total', + type => 'weekly_total' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/4.fix b/t/etc/fixtures/data/search/leaderboards/4.fix new file mode 100644 index 0000000..feb773c --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/4.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 4, + name => 'Weekly Count', + type => 'weekly_count' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/5.fix b/t/etc/fixtures/data/search/leaderboards/5.fix new file mode 100644 index 0000000..d0522b2 --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/5.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 5, + name => 'Monthly Total', + type => 'monthly_total' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/6.fix b/t/etc/fixtures/data/search/leaderboards/6.fix new file mode 100644 index 0000000..f7bb145 --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/6.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 6, + name => 'Monthly Count', + type => 'monthly_count' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/7.fix b/t/etc/fixtures/data/search/leaderboards/7.fix new file mode 100644 index 0000000..b2aadcd --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/7.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 7, + name => 'All Time Total', + type => 'all_time_total' + }; diff --git a/t/etc/fixtures/data/search/leaderboards/8.fix b/t/etc/fixtures/data/search/leaderboards/8.fix new file mode 100644 index 0000000..df58698 --- /dev/null +++ b/t/etc/fixtures/data/search/leaderboards/8.fix @@ -0,0 +1,5 @@ +$HASH1 = { + id => 8, + name => 'All Time Count', + type => 'all_time_count' + }; diff --git a/t/etc/fixtures/data/search/organisations/1.fix b/t/etc/fixtures/data/search/organisations/1.fix new file mode 100644 index 0000000..b761450 --- /dev/null +++ b/t/etc/fixtures/data/search/organisations/1.fix @@ -0,0 +1,16 @@ +$HASH1 = { + country => undef, + entity_id + => 5, + id => 1, + name => 'Test Org', + pending => 0, + postcode + => 'LA1 1AA', + sector => undef, + street_name + => 'Test Street', + submitted_by_id + => undef, + town => 'Lancaster' + }; diff --git a/t/etc/fixtures/data/search/organisations/2.fix b/t/etc/fixtures/data/search/organisations/2.fix new file mode 100644 index 0000000..5da65f1 --- /dev/null +++ b/t/etc/fixtures/data/search/organisations/2.fix @@ -0,0 +1,16 @@ +$HASH1 = { + country => undef, + entity_id + => 7, + id => 2, + name => 'Avanti Bar & Restaurant', + pending => 0, + postcode + => 'LA6 2AH', + sector => 'I', + street_name + => '57 Main St', + submitted_by_id + => undef, + town => 'Kirkby Lonsdale' + }; diff --git a/t/etc/fixtures/data/search/organisations/3.fix b/t/etc/fixtures/data/search/organisations/3.fix new file mode 100644 index 0000000..000cb56 --- /dev/null +++ b/t/etc/fixtures/data/search/organisations/3.fix @@ -0,0 +1,16 @@ +$HASH1 = { + country => undef, + entity_id + => 8, + id => 3, + name => 'Full House Noodle Bar', + pending => 0, + postcode + => 'LA1 1XD', + sector => 'I', + street_name + => '21 Common Garden St', + submitted_by_id + => undef, + town => 'Lancaster' + }; diff --git a/t/etc/fixtures/data/search/organisations/4.fix b/t/etc/fixtures/data/search/organisations/4.fix new file mode 100644 index 0000000..565b447 --- /dev/null +++ b/t/etc/fixtures/data/search/organisations/4.fix @@ -0,0 +1,16 @@ +$HASH1 = { + country => undef, + entity_id + => 9, + id => 4, + name => 'The Quay\'s Fishbar', + pending => 0, + postcode + => 'LA1 1SS', + sector => 'I', + street_name + => '1 Adcliffe Rd', + submitted_by_id + => undef, + town => 'Lancaster' + }; diff --git a/t/etc/fixtures/data/search/organisations/5.fix b/t/etc/fixtures/data/search/organisations/5.fix new file mode 100644 index 0000000..9e9cbe5 --- /dev/null +++ b/t/etc/fixtures/data/search/organisations/5.fix @@ -0,0 +1,16 @@ +$HASH1 = { + country => undef, + entity_id + => 10, + id => 5, + name => 'Dan\'s Fishop', + pending => 0, + postcode + => 'LA1 1LT', + sector => 'I', + street_name + => '56 North Rd', + submitted_by_id + => undef, + town => 'Lancaster' + }; diff --git a/t/etc/fixtures/data/search/organisations/6.fix b/t/etc/fixtures/data/search/organisations/6.fix new file mode 100644 index 0000000..2d941bf --- /dev/null +++ b/t/etc/fixtures/data/search/organisations/6.fix @@ -0,0 +1,16 @@ +$HASH1 = { + country => undef, + entity_id + => 11, + id => 6, + name => 'Hodgeson\'s Chippy', + pending => 0, + postcode + => 'LA1 3BH', + sector => 'I', + street_name + => '96 Prospect St', + submitted_by_id + => undef, + town => 'Lancaster' + }; diff --git a/t/etc/fixtures/data/search/users/1.fix b/t/etc/fixtures/data/search/users/1.fix new file mode 100644 index 0000000..9507717 --- /dev/null +++ b/t/etc/fixtures/data/search/users/1.fix @@ -0,0 +1,8 @@ +$HASH1 = { + email => 'test1@example.com', + entity_id => 1, + id => 1, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$HSuznDeSU1fuONwKhp2/S.TX/X4p4g0dHtz20kVXxprm8hIg5QQma' + }; diff --git a/t/etc/fixtures/data/search/users/2.fix b/t/etc/fixtures/data/search/users/2.fix new file mode 100644 index 0000000..9654ada --- /dev/null +++ b/t/etc/fixtures/data/search/users/2.fix @@ -0,0 +1,8 @@ +$HASH1 = { + email => 'test2@example.com', + entity_id => 2, + id => 2, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$5XYLWPJvVGuWUvfSWj9cIOg4/tyB4fZ3knzwgw5UnBSKFBFIKOiFC' + }; diff --git a/t/etc/fixtures/data/search/users/3.fix b/t/etc/fixtures/data/search/users/3.fix new file mode 100644 index 0000000..fd07648 --- /dev/null +++ b/t/etc/fixtures/data/search/users/3.fix @@ -0,0 +1,8 @@ +$HASH1 = { + email => 'test3@example.com', + entity_id => 3, + id => 3, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$p5VU4leHetEvuz2P3uMfYuuPTkDGg8wsM7QuSVKkXR.s3B9T2JaVW' + }; diff --git a/t/etc/fixtures/data/search/users/4.fix b/t/etc/fixtures/data/search/users/4.fix new file mode 100644 index 0000000..e8300ec --- /dev/null +++ b/t/etc/fixtures/data/search/users/4.fix @@ -0,0 +1,8 @@ +$HASH1 = { + email => 'test4@example.com', + entity_id => 4, + id => 4, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$fkkTUYA9zvt32iBbN7aOcOnmiHzOkbMEjN31PB9CsitGrE.KF7cAG' + }; diff --git a/t/etc/fixtures/data/search/users/5.fix b/t/etc/fixtures/data/search/users/5.fix new file mode 100644 index 0000000..97859de --- /dev/null +++ b/t/etc/fixtures/data/search/users/5.fix @@ -0,0 +1,8 @@ +$HASH1 = { + email => 'org@example.com', + entity_id => 5, + id => 5, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$MWC45.w1AnLPNNHiS96ICOHfNlTrIqB0.7OPzy5qB.z0pZB0theo.' + }; diff --git a/t/etc/fixtures/data/search/users/6.fix b/t/etc/fixtures/data/search/users/6.fix new file mode 100644 index 0000000..d1b52dc --- /dev/null +++ b/t/etc/fixtures/data/search/users/6.fix @@ -0,0 +1,8 @@ +$HASH1 = { + email => 'admin@example.com', + entity_id => 6, + id => 6, + is_admin => 1, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$1sPcPB9GnoNgzhBAk2bHSOqEmv6.Y6YLrAa2DJ6TR2WefzTYZQ92G' + }; diff --git a/t/etc/fixtures/data/users/_config_set b/t/etc/fixtures/data/users/_config_set index f2242db..debc83c 100644 --- a/t/etc/fixtures/data/users/_config_set +++ b/t/etc/fixtures/data/users/_config_set @@ -5,20 +5,21 @@ $VAR1 = { 'belongs_to' => { 'fetch' => 0 }, - 'might_have' => { - 'fetch' => 0 - }, 'sets' => [ { - 'class' => 'Leaderboard', + 'class' => 'Feedback', 'quantity' => 'all' }, { - 'class' => 'AccountToken', + 'quantity' => 'all', + 'class' => 'LeaderboardSet' + }, + { + 'class' => 'Transaction', 'quantity' => 'all' }, { - 'class' => 'LeaderboardValue', + 'class' => 'Entity', 'quantity' => 'all' }, { @@ -26,40 +27,31 @@ $VAR1 = { 'class' => 'SessionToken' }, { - 'class' => 'Administrator', + 'class' => 'AccountToken', 'quantity' => 'all' }, { - 'class' => 'LeaderboardSet', + 'class' => 'Organisation', 'quantity' => 'all' }, { 'quantity' => 'all', - 'class' => 'Feedback' + 'class' => 'User' }, { - 'class' => 'PendingOrganisation', + 'class' => 'Customer', 'quantity' => 'all' }, { - 'class' => 'User', - 'quantity' => 'all' - }, - { - 'class' => 'Transaction', + 'class' => 'Leaderboard', 'quantity' => 'all' }, { 'quantity' => 'all', - 'class' => 'Organisation' - }, - { - 'quantity' => 'all', - 'class' => 'Customer' - }, - { - 'quantity' => 'all', - 'class' => 'PendingTransaction' + 'class' => 'LeaderboardValue' } - ] + ], + 'might_have' => { + 'fetch' => 0 + } }; diff --git a/t/etc/fixtures/data/users/customers/1.fix b/t/etc/fixtures/data/users/customers/1.fix index 16d0da6..705c22d 100644 --- a/t/etc/fixtures/data/users/customers/1.fix +++ b/t/etc/fixtures/data/users/customers/1.fix @@ -1,5 +1,6 @@ $HASH1 = { display_name => 'Test User1', + entity_id => 1, full_name => 'Test User1', id => 1, postcode => 'LA1 1AA', diff --git a/t/etc/fixtures/data/users/customers/2.fix b/t/etc/fixtures/data/users/customers/2.fix index 3c31701..0fee4aa 100644 --- a/t/etc/fixtures/data/users/customers/2.fix +++ b/t/etc/fixtures/data/users/customers/2.fix @@ -1,5 +1,6 @@ $HASH1 = { display_name => 'Test User2', + entity_id => 2, full_name => 'Test User2', id => 2, postcode => 'LA1 1AA', diff --git a/t/etc/fixtures/data/users/customers/3.fix b/t/etc/fixtures/data/users/customers/3.fix index b434d5c..a64e402 100644 --- a/t/etc/fixtures/data/users/customers/3.fix +++ b/t/etc/fixtures/data/users/customers/3.fix @@ -1,5 +1,6 @@ $HASH1 = { display_name => 'Test User3', + entity_id => 3, full_name => 'Test User3', id => 3, postcode => 'LA1 1AA', diff --git a/t/etc/fixtures/data/users/customers/4.fix b/t/etc/fixtures/data/users/customers/4.fix index d5f881b..49d0530 100644 --- a/t/etc/fixtures/data/users/customers/4.fix +++ b/t/etc/fixtures/data/users/customers/4.fix @@ -1,5 +1,6 @@ $HASH1 = { display_name => 'Test User4', + entity_id => 4, full_name => 'Test User4', id => 4, postcode => 'LA1 1AA', diff --git a/t/etc/fixtures/data/users/customers/5.fix b/t/etc/fixtures/data/users/customers/5.fix new file mode 100644 index 0000000..50ef1c2 --- /dev/null +++ b/t/etc/fixtures/data/users/customers/5.fix @@ -0,0 +1,8 @@ +$HASH1 = { + display_name => 'Test Admin', + entity_id => 6, + full_name => 'Test Admin', + id => 5, + postcode => 'LA1 1AA', + year_of_birth => 2006 + }; diff --git a/t/etc/fixtures/data/users/entities/1.fix b/t/etc/fixtures/data/users/entities/1.fix new file mode 100644 index 0000000..a45e065 --- /dev/null +++ b/t/etc/fixtures/data/users/entities/1.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 1, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/users/entities/2.fix b/t/etc/fixtures/data/users/entities/2.fix new file mode 100644 index 0000000..20849e3 --- /dev/null +++ b/t/etc/fixtures/data/users/entities/2.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 2, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/users/entities/3.fix b/t/etc/fixtures/data/users/entities/3.fix new file mode 100644 index 0000000..3fd6a9a --- /dev/null +++ b/t/etc/fixtures/data/users/entities/3.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 3, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/users/entities/4.fix b/t/etc/fixtures/data/users/entities/4.fix new file mode 100644 index 0000000..bacb15b --- /dev/null +++ b/t/etc/fixtures/data/users/entities/4.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 4, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/users/entities/5.fix b/t/etc/fixtures/data/users/entities/5.fix new file mode 100644 index 0000000..9aa374d --- /dev/null +++ b/t/etc/fixtures/data/users/entities/5.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 5, + type => 'organisation' + }; diff --git a/t/etc/fixtures/data/users/entities/6.fix b/t/etc/fixtures/data/users/entities/6.fix new file mode 100644 index 0000000..ee9f57f --- /dev/null +++ b/t/etc/fixtures/data/users/entities/6.fix @@ -0,0 +1,4 @@ +$HASH1 = { + id => 6, + type => 'customer' + }; diff --git a/t/etc/fixtures/data/users/organisations/1.fix b/t/etc/fixtures/data/users/organisations/1.fix index 035a3ce..b761450 100644 --- a/t/etc/fixtures/data/users/organisations/1.fix +++ b/t/etc/fixtures/data/users/organisations/1.fix @@ -1,10 +1,16 @@ $HASH1 = { - id => 1, - name => 'Test Org', + country => undef, + entity_id + => 5, + id => 1, + name => 'Test Org', + pending => 0, postcode - => 'LA1 1AA', - sector => undef, + => 'LA1 1AA', + sector => undef, street_name - => 'Test Street', - town => 'Lancaster' + => 'Test Street', + submitted_by_id + => undef, + town => 'Lancaster' }; diff --git a/t/etc/fixtures/data/users/users/1.fix b/t/etc/fixtures/data/users/users/1.fix index 0946374..9507717 100644 --- a/t/etc/fixtures/data/users/users/1.fix +++ b/t/etc/fixtures/data/users/users/1.fix @@ -1,11 +1,8 @@ $HASH1 = { - customer_id - => 1, - email => 'test1@example.com', - id => 1, - join_date - => '2017-08-25 15:36:11', - organisation_id - => undef, - password => '$2a$08$yCau6xDkRFZINg80iVvMh.M3JnLq2g.LJ7GMJL5KQvO45pDL.D/Rq' + email => 'test1@example.com', + entity_id => 1, + id => 1, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$HSuznDeSU1fuONwKhp2/S.TX/X4p4g0dHtz20kVXxprm8hIg5QQma' }; diff --git a/t/etc/fixtures/data/users/users/2.fix b/t/etc/fixtures/data/users/users/2.fix index 9b35ef4..9654ada 100644 --- a/t/etc/fixtures/data/users/users/2.fix +++ b/t/etc/fixtures/data/users/users/2.fix @@ -1,11 +1,8 @@ $HASH1 = { - customer_id - => 2, - email => 'test2@example.com', - id => 2, - join_date - => '2017-08-25 15:36:11', - organisation_id - => undef, - password => '$2a$08$BZAsbHSW8TN/jlL2DFGDoeKFRKzj2dQTBwatxb0p/maefEcWcziom' + email => 'test2@example.com', + entity_id => 2, + id => 2, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$5XYLWPJvVGuWUvfSWj9cIOg4/tyB4fZ3knzwgw5UnBSKFBFIKOiFC' }; diff --git a/t/etc/fixtures/data/users/users/3.fix b/t/etc/fixtures/data/users/users/3.fix index f3ad391..fd07648 100644 --- a/t/etc/fixtures/data/users/users/3.fix +++ b/t/etc/fixtures/data/users/users/3.fix @@ -1,11 +1,8 @@ $HASH1 = { - customer_id - => 3, - email => 'test3@example.com', - id => 3, - join_date - => '2017-08-25 15:36:11', - organisation_id - => undef, - password => '$2a$08$xdkD/OA5izrOX9cvJDa4i..8T3YGmfVSo/G87wDRoGWnQmlC0gxOW' + email => 'test3@example.com', + entity_id => 3, + id => 3, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$p5VU4leHetEvuz2P3uMfYuuPTkDGg8wsM7QuSVKkXR.s3B9T2JaVW' }; diff --git a/t/etc/fixtures/data/users/users/4.fix b/t/etc/fixtures/data/users/users/4.fix index 47d83c3..e8300ec 100644 --- a/t/etc/fixtures/data/users/users/4.fix +++ b/t/etc/fixtures/data/users/users/4.fix @@ -1,11 +1,8 @@ $HASH1 = { - customer_id - => 4, - email => 'test4@example.com', - id => 4, - join_date - => '2017-08-25 15:36:11', - organisation_id - => undef, - password => '$2a$08$svjdm.Syn3f062pDIN3/ROTUU7W16n0zJFm9/sm3x7pfbMLZFV.5G' + email => 'test4@example.com', + entity_id => 4, + id => 4, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$fkkTUYA9zvt32iBbN7aOcOnmiHzOkbMEjN31PB9CsitGrE.KF7cAG' }; diff --git a/t/etc/fixtures/data/users/users/5.fix b/t/etc/fixtures/data/users/users/5.fix index 50e56c9..97859de 100644 --- a/t/etc/fixtures/data/users/users/5.fix +++ b/t/etc/fixtures/data/users/users/5.fix @@ -1,11 +1,8 @@ $HASH1 = { - customer_id - => undef, - email => 'org@example.com', - id => 5, - join_date - => '2017-08-25 15:36:11', - organisation_id - => 1, - password => '$2a$08$3PkZF7D9FiOq8hgU7cJ6puY86Fkl34bQj6dZeJRXPU8hhJIMZtge2' + email => 'org@example.com', + entity_id => 5, + id => 5, + is_admin => 0, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$MWC45.w1AnLPNNHiS96ICOHfNlTrIqB0.7OPzy5qB.z0pZB0theo.' }; diff --git a/t/etc/fixtures/data/users/users/6.fix b/t/etc/fixtures/data/users/users/6.fix new file mode 100644 index 0000000..d1b52dc --- /dev/null +++ b/t/etc/fixtures/data/users/users/6.fix @@ -0,0 +1,8 @@ +$HASH1 = { + email => 'admin@example.com', + entity_id => 6, + id => 6, + is_admin => 1, + join_date => '2017-08-31 12:17:25', + password => '$2a$08$1sPcPB9GnoNgzhBAk2bHSOqEmv6.Y6YLrAa2DJ6TR2WefzTYZQ92G' + }; diff --git a/t/schema/leaderboard.t b/t/schema/leaderboard.t index 5915760..5d2cbcb 100644 --- a/t/schema/leaderboard.t +++ b/t/schema/leaderboard.t @@ -77,9 +77,9 @@ my $now = DateTime->today(); for my $user ( $user1, $user2, $user3, $user4 ) { $tweak ++; - my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user->{email} })->entity; for ( 1 .. 10 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -87,7 +87,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { } for ( 11 .. 20 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -96,7 +96,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { } for ( 21 .. 30 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -105,7 +105,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { } for ( 31 .. 40 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -113,7 +113,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { }); } - is $user_result->transactions->count, 40, 'correct count for user' . $tweak; + is $user_result->purchases->count, 40, 'correct count for user' . $tweak; } sub test_leaderboard { @@ -130,7 +130,7 @@ sub test_leaderboard { {}, { order_by => { -desc => 'value' }, - columns => [ qw/ user_id value / ], + columns => [ qw/ entity_id value / ], }, ); $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); @@ -144,10 +144,10 @@ test_leaderboard( 'daily_total', $now, [ - { user_id => 4, value => 95 }, - { user_id => 3, value => 85 }, - { user_id => 2, value => 75 }, - { user_id => 1, value => 65 }, + { entity_id => 4, value => 95 }, + { entity_id => 3, value => 85 }, + { entity_id => 2, value => 75 }, + { entity_id => 1, value => 65 }, ] ); @@ -156,10 +156,10 @@ test_leaderboard( 'daily_count', $now, [ - { user_id => 1, value => 10 }, - { user_id => 2, value => 10 }, - { user_id => 3, value => 10 }, - { user_id => 4, value => 10 }, + { entity_id => 1, value => 10 }, + { entity_id => 2, value => 10 }, + { entity_id => 3, value => 10 }, + { entity_id => 4, value => 10 }, ] ); @@ -168,10 +168,10 @@ test_leaderboard( 'weekly_total', $now->clone->subtract( days => 7 ), [ - { user_id => 4, value => 195 }, - { user_id => 3, value => 185 }, - { user_id => 2, value => 175 }, - { user_id => 1, value => 165 }, + { entity_id => 4, value => 195 }, + { entity_id => 3, value => 185 }, + { entity_id => 2, value => 175 }, + { entity_id => 1, value => 165 }, ] ); @@ -180,10 +180,10 @@ test_leaderboard( 'weekly_count', $now->clone->subtract( days => 7 ), [ - { user_id => 1, value => 10 }, - { user_id => 2, value => 10 }, - { user_id => 3, value => 10 }, - { user_id => 4, value => 10 }, + { entity_id => 1, value => 10 }, + { entity_id => 2, value => 10 }, + { entity_id => 3, value => 10 }, + { entity_id => 4, value => 10 }, ] ); @@ -192,10 +192,10 @@ test_leaderboard( 'monthly_total', $now->clone->subtract( months => 1 ), [ - { user_id => 4, value => 490 }, - { user_id => 3, value => 470 }, - { user_id => 2, value => 450 }, - { user_id => 1, value => 430 }, + { entity_id => 4, value => 490 }, + { entity_id => 3, value => 470 }, + { entity_id => 2, value => 450 }, + { entity_id => 1, value => 430 }, ] ); @@ -204,10 +204,10 @@ test_leaderboard( 'monthly_count', $now->clone->subtract( months => 1 ), [ - { user_id => 1, value => 20 }, - { user_id => 2, value => 20 }, - { user_id => 3, value => 20 }, - { user_id => 4, value => 20 }, + { entity_id => 1, value => 20 }, + { entity_id => 2, value => 20 }, + { entity_id => 3, value => 20 }, + { entity_id => 4, value => 20 }, ] ); @@ -216,10 +216,10 @@ test_leaderboard( 'all_time_total', $now, [ - { user_id => 4, value => 885 }, - { user_id => 3, value => 855 }, - { user_id => 2, value => 825 }, - { user_id => 1, value => 795 }, + { entity_id => 4, value => 885 }, + { entity_id => 3, value => 855 }, + { entity_id => 2, value => 825 }, + { entity_id => 1, value => 795 }, ] ); @@ -228,10 +228,10 @@ test_leaderboard( 'all_time_count', $now, [ - { user_id => 1, value => 30 }, - { user_id => 2, value => 30 }, - { user_id => 3, value => 30 }, - { user_id => 4, value => 30 }, + { entity_id => 1, value => 30 }, + { entity_id => 2, value => 30 }, + { entity_id => 3, value => 30 }, + { entity_id => 4, value => 30 }, ] ); @@ -249,16 +249,16 @@ subtest 'get_latest' => sub { {}, { order_by => { -desc => 'value' }, - columns => [ qw/ user_id value / ], + columns => [ qw/ entity_id value / ], }, ); $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); my $expected = [ - { user_id => 4, value => 95 }, - { user_id => 3, value => 85 }, - { user_id => 2, value => 75 }, - { user_id => 1, value => 65 }, + { entity_id => 4, value => 95 }, + { entity_id => 3, value => 85 }, + { entity_id => 2, value => 75 }, + { entity_id => 1, value => 65 }, ]; is_deeply [ $today_values->all ], $expected, 'array as expected'; diff --git a/t/schema/leaderboard_trend.t b/t/schema/leaderboard_trend.t index 33e7ea9..030d238 100644 --- a/t/schema/leaderboard_trend.t +++ b/t/schema/leaderboard_trend.t @@ -76,15 +76,15 @@ my $tweak = 0; my $now = DateTime->today(); { - my $user_result = $schema->resultset('User')->find({ email => $user1->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user1->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 1, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 9, proof_image => 'a', @@ -93,15 +93,15 @@ my $now = DateTime->today(); } { - my $user_result = $schema->resultset('User')->find({ email => $user2->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user2->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 3, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 1, proof_image => 'a', @@ -110,15 +110,15 @@ my $now = DateTime->today(); } { - my $user_result = $schema->resultset('User')->find({ email => $user3->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user3->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 5, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 5, proof_image => 'a', @@ -127,15 +127,15 @@ my $now = DateTime->today(); } { - my $user_result = $schema->resultset('User')->find({ email => $user4->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user4->{email} })->entity; - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 9, proof_image => 'a', }); - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => 3, proof_image => 'a', @@ -162,7 +162,7 @@ sub test_leaderboard { {}, { order_by => { -desc => 'value' }, - columns => [ qw/ user_id value trend position / ], + columns => [ qw/ entity_id value trend position / ], }, ); $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); @@ -178,10 +178,10 @@ test_leaderboard( 'daily_total', $now, [ - { user_id => 4, value => 9, trend => -1, position => 1}, - { user_id => 3, value => 5, trend => 0, position => 2}, - { user_id => 2, value => 3, trend => -1, position => 3}, - { user_id => 1, value => 1, trend => 1, position => 4}, + { entity_id => 4, value => 9, trend => -1, position => 1}, + { entity_id => 3, value => 5, trend => 0, position => 2}, + { entity_id => 2, value => 3, trend => -1, position => 3}, + { entity_id => 1, value => 1, trend => 1, position => 4}, ] ); @@ -190,10 +190,10 @@ test_leaderboard( 'daily_count', $now, [ - { user_id => 1, value => 1, trend => 0, position => 1 }, - { user_id => 2, value => 1, trend => 0, position => 2 }, - { user_id => 3, value => 1, trend => 0, position => 3 }, - { user_id => 4, value => 1, trend => 0, position => 4 }, + { entity_id => 1, value => 1, trend => 0, position => 1 }, + { entity_id => 2, value => 1, trend => 0, position => 2 }, + { entity_id => 3, value => 1, trend => 0, position => 3 }, + { entity_id => 4, value => 1, trend => 0, position => 4 }, ] ); diff --git a/t/schema/resultset_leaderboard.t b/t/schema/resultset_leaderboard.t index 92b1120..4a27bbd 100644 --- a/t/schema/resultset_leaderboard.t +++ b/t/schema/resultset_leaderboard.t @@ -69,7 +69,7 @@ $framework->register_customer($_) $framework->register_organisation($org); -my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} }); +my $org_result = $schema->resultset('Organisation')->find({ name => $org->{name} })->entity; my $tweak = 0; @@ -77,9 +77,9 @@ my $now = DateTime->today(); for my $user ( $user1, $user2, $user3, $user4 ) { $tweak ++; - my $user_result = $schema->resultset('User')->find({ email => $user->{email} }); + my $user_result = $schema->resultset('User')->find({ email => $user->{email} })->entity; for ( 1 .. 10 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -87,7 +87,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { } for ( 11 .. 20 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -96,7 +96,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { } for ( 21 .. 30 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -105,7 +105,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { } for ( 31 .. 40 ) { - $user_result->create_related( 'transactions', { + $user_result->create_related( 'purchases', { seller_id => $org_result->id, value => $_ + $tweak, proof_image => 'a', @@ -113,7 +113,7 @@ for my $user ( $user1, $user2, $user3, $user4 ) { }); } - is $user_result->transactions->count, 40, 'correct count for user' . $tweak; + is $user_result->purchases->count, 40, 'correct count for user' . $tweak; } sub test_leaderboard { @@ -132,7 +132,7 @@ sub test_leaderboard { {}, { order_by => { -desc => 'value' }, - columns => [ qw/ user_id value position / ], + columns => [ qw/ entity_id value position / ], }, ); $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); @@ -146,10 +146,10 @@ test_leaderboard( 'daily_total', $now, [ - { user_id => 4, value => 95, position => 1 }, - { user_id => 3, value => 85, position => 2 }, - { user_id => 2, value => 75, position => 3 }, - { user_id => 1, value => 65, position => 4 }, + { entity_id => 4, value => 95, position => 1 }, + { entity_id => 3, value => 85, position => 2 }, + { entity_id => 2, value => 75, position => 3 }, + { entity_id => 1, value => 65, position => 4 }, ] ); @@ -158,10 +158,10 @@ test_leaderboard( 'daily_count', $now, [ - { user_id => 1, value => 10, position => 1 }, - { user_id => 2, value => 10, position => 2 }, - { user_id => 3, value => 10, position => 3 }, - { user_id => 4, value => 10, position => 4 }, + { entity_id => 1, value => 10, position => 1 }, + { entity_id => 2, value => 10, position => 2 }, + { entity_id => 3, value => 10, position => 3 }, + { entity_id => 4, value => 10, position => 4 }, ] ); @@ -170,10 +170,10 @@ test_leaderboard( 'weekly_total', $now->clone->subtract( days => 7 ), [ - { user_id => 4, value => 195, position => 1 }, - { user_id => 3, value => 185, position => 2 }, - { user_id => 2, value => 175, position => 3 }, - { user_id => 1, value => 165, position => 4 }, + { entity_id => 4, value => 195, position => 1 }, + { entity_id => 3, value => 185, position => 2 }, + { entity_id => 2, value => 175, position => 3 }, + { entity_id => 1, value => 165, position => 4 }, ] ); @@ -182,10 +182,10 @@ test_leaderboard( 'weekly_count', $now->clone->subtract( days => 7 ), [ - { user_id => 1, value => 10, position => 1 }, - { user_id => 2, value => 10, position => 2 }, - { user_id => 3, value => 10, position => 3 }, - { user_id => 4, value => 10, position => 4 }, + { entity_id => 1, value => 10, position => 1 }, + { entity_id => 2, value => 10, position => 2 }, + { entity_id => 3, value => 10, position => 3 }, + { entity_id => 4, value => 10, position => 4 }, ] ); @@ -194,10 +194,10 @@ test_leaderboard( 'monthly_total', $now->clone->subtract( months => 1 ), [ - { user_id => 4, value => 490, position => 1 }, - { user_id => 3, value => 470, position => 2 }, - { user_id => 2, value => 450, position => 3 }, - { user_id => 1, value => 430, position => 4 }, + { entity_id => 4, value => 490, position => 1 }, + { entity_id => 3, value => 470, position => 2 }, + { entity_id => 2, value => 450, position => 3 }, + { entity_id => 1, value => 430, position => 4 }, ] ); @@ -206,10 +206,10 @@ test_leaderboard( 'monthly_count', $now->clone->subtract( months => 1 ), [ - { user_id => 1, value => 20, position => 1 }, - { user_id => 2, value => 20, position => 2 }, - { user_id => 3, value => 20, position => 3 }, - { user_id => 4, value => 20, position => 4 }, + { entity_id => 1, value => 20, position => 1 }, + { entity_id => 2, value => 20, position => 2 }, + { entity_id => 3, value => 20, position => 3 }, + { entity_id => 4, value => 20, position => 4 }, ] ); @@ -218,10 +218,10 @@ test_leaderboard( 'all_time_total', $now, [ - { user_id => 4, value => 885, position => 1 }, - { user_id => 3, value => 855, position => 2 }, - { user_id => 2, value => 825, position => 3 }, - { user_id => 1, value => 795, position => 4 }, + { entity_id => 4, value => 885, position => 1 }, + { entity_id => 3, value => 855, position => 2 }, + { entity_id => 2, value => 825, position => 3 }, + { entity_id => 1, value => 795, position => 4 }, ] ); @@ -230,10 +230,10 @@ test_leaderboard( 'all_time_count', $now, [ - { user_id => 1, value => 30, position => 1 }, - { user_id => 2, value => 30, position => 2 }, - { user_id => 3, value => 30, position => 3 }, - { user_id => 4, value => 30, position => 4 }, + { entity_id => 1, value => 30, position => 1 }, + { entity_id => 2, value => 30, position => 2 }, + { entity_id => 3, value => 30, position => 3 }, + { entity_id => 4, value => 30, position => 4 }, ] ); @@ -251,16 +251,16 @@ subtest 'get_latest' => sub { {}, { order_by => { -desc => 'value' }, - columns => [ qw/ user_id value position / ], + columns => [ qw/ entity_id value position / ], }, ); $today_values->result_class( 'DBIx::Class::ResultClass::HashRefInflator' ); my $expected = [ - { user_id => 4, value => 95, position => 1 }, - { user_id => 3, value => 85, position => 2 }, - { user_id => 2, value => 75, position => 3 }, - { user_id => 1, value => 65, position => 4 }, + { entity_id => 4, value => 95, position => 1 }, + { entity_id => 3, value => 85, position => 2 }, + { entity_id => 2, value => 75, position => 3 }, + { entity_id => 1, value => 65, position => 4 }, ]; is_deeply [ $today_values->all ], $expected, 'array as expected'; diff --git a/templates/admin/organisations/add_org.html.ep b/templates/admin/organisations/add_org.html.ep index 6e7702e..be22d3c 100644 --- a/templates/admin/organisations/add_org.html.ep +++ b/templates/admin/organisations/add_org.html.ep @@ -15,19 +15,51 @@

Add an Organisation

- + diff --git a/templates/admin/organisations/list.html.ep b/templates/admin/organisations/list.html.ep index dad10cf..4db8974 100644 --- a/templates/admin/organisations/list.html.ep +++ b/templates/admin/organisations/list.html.ep @@ -20,7 +20,7 @@
% for my $valid_org ($valid_orgs_rs->all) { - +
%= $valid_org->name
@@ -41,7 +41,7 @@
% } else { % for my $pending_org ($pending_orgs_rs->all) { - +
%= $pending_org->name
diff --git a/templates/admin/organisations/pending_read.html.ep b/templates/admin/organisations/pending_read.html.ep deleted file mode 100644 index bbf99e0..0000000 --- a/templates/admin/organisations/pending_read.html.ep +++ /dev/null @@ -1,49 +0,0 @@ -% layout 'admin'; -% title 'Organisations'; -% content_for javascript => begin -% end -% if ( my $error = flash 'error' ) { - -% } elsif ( my $success = flash 'success' ) { - -% } -
-

- %= $pending_org->name -

-
-
-
-

- Transactions -

- -
diff --git a/templates/admin/organisations/valid_read.html.ep b/templates/admin/organisations/valid_read.html.ep index ffb7bb2..e390e5d 100644 --- a/templates/admin/organisations/valid_read.html.ep +++ b/templates/admin/organisations/valid_read.html.ep @@ -15,20 +15,53 @@

%= $valid_org->name

- +

@@ -38,13 +71,15 @@ % for my $transaction ( $transactions->all ) {
  • -
    -
    From: <%= $transaction->buyer->name %>
    -
    To: <%= $transaction->seller->name %>
    -
    Value: <%= $transaction->value %>
    -
    Submitted At: <%= $transaction->submitted_at %>
    -
    Purchase Time: <%= $transaction->purchase_time %>
    -
    + +
    +
    From: <%= $transaction->buyer->name %>
    +
    To: <%= $transaction->seller->name %>
    +
    Value: <%= $transaction->value %>
    +
    Submitted At: <%= $transaction->submitted_at %>
    +
    Purchase Time: <%= $transaction->purchase_time %>
    +
    +
  • % } diff --git a/templates/admin/transactions/index.html.ep b/templates/admin/transactions/index.html.ep new file mode 100644 index 0000000..191f845 --- /dev/null +++ b/templates/admin/transactions/index.html.ep @@ -0,0 +1,35 @@ +% layout 'admin'; +% title 'Transactions'; +% content_for javascript => begin +% end +% if ( my $error = flash 'error' ) { + +% } elsif ( my $success = flash 'success' ) { + +% } +
    + % for my $transaction ( $transactions->all ) { +
  • + +
  • + % } +
  • +
    + %= bootstrap_pagination( $c->param('page') || 1, $transactions->pager->last_page, { class => 'justify-content-center' } ); +
    +
  • +
    diff --git a/templates/admin/transactions/read.html.ep b/templates/admin/transactions/read.html.ep new file mode 100644 index 0000000..9eb9596 --- /dev/null +++ b/templates/admin/transactions/read.html.ep @@ -0,0 +1,50 @@ +% layout 'admin'; +% title 'Transactions'; +% content_for javascript => begin +% end +% if ( my $error = flash 'error' ) { + +% } elsif ( my $success = flash 'success' ) { + +% } +
    +

    + Transaction Details +
    +
    + +
    +
    +

    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +
    +
    +
    diff --git a/templates/admin/users/read.html.ep b/templates/admin/users/read.html.ep index 8e6cb47..2970b15 100644 --- a/templates/admin/users/read.html.ep +++ b/templates/admin/users/read.html.ep @@ -11,7 +11,7 @@ Success! <%= $success %>

    % } -
    +

    User Details

    @@ -25,14 +25,14 @@
    - +

    Leave blank unless you want to change their password

    - % if ( my $customer_rs = $user->customer ) { + % if ( my $customer_rs = $user->entity->customer ) {

    Customer Details

    @@ -52,7 +52,7 @@ - % } elsif ( my $org_rs = $user->organisation ) { + % } elsif ( my $org_rs = $user->entity->organisation ) {

    Organisation Details

    @@ -86,3 +86,30 @@
    +
    +

    + Transactions +

    + +
    diff --git a/templates/layouts/admin.html.ep b/templates/layouts/admin.html.ep index e13f18f..417937d 100644 --- a/templates/layouts/admin.html.ep +++ b/templates/layouts/admin.html.ep @@ -30,6 +30,7 @@