From 57556d252b5f3bc0050697c84638e552e0ea76a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20=22flowdy=22=20He=C3=9F?= Date: Sun, 26 Jun 2016 17:49:12 +0200 Subject: [PATCH] added ORM api and some tests --- TrsrDB.pm | 26 +++++++++ TrsrDB/Account.pm | 34 ++++++++++++ TrsrDB/AvailableCredits.pm | 19 +++++++ TrsrDB/Balance.pm | 14 +++++ TrsrDB/Credit.pm | 29 ++++++++++ TrsrDB/CurrentDebts.pm | 19 +++++++ TrsrDB/Debit.pm | 29 ++++++++++ TrsrDB/History.pm | 14 +++++ TrsrDB/ReconstructedBankStatement.pm | 9 ++++ TrsrDB/Transfer.pm | 22 ++++++++ t/schema.t | 79 ++++++++++++++++++++++++++++ 11 files changed, 294 insertions(+) create mode 100644 TrsrDB.pm create mode 100644 TrsrDB/Account.pm create mode 100644 TrsrDB/AvailableCredits.pm create mode 100644 TrsrDB/Balance.pm create mode 100644 TrsrDB/Credit.pm create mode 100644 TrsrDB/CurrentDebts.pm create mode 100644 TrsrDB/Debit.pm create mode 100644 TrsrDB/History.pm create mode 100644 TrsrDB/ReconstructedBankStatement.pm create mode 100644 TrsrDB/Transfer.pm create mode 100644 t/schema.t diff --git a/TrsrDB.pm b/TrsrDB.pm new file mode 100644 index 0000000..b9a9c3b --- /dev/null +++ b/TrsrDB.pm @@ -0,0 +1,26 @@ +use strict; + +package TrsrDB; +use base qw/DBIx::Class::Schema/; +use Carp qw/croak/; + +__PACKAGE__->load_classes(qw| + Account Debit Credit Transfer CurrentDebts AvailableCredits + Balance ReconstructedBankStatement History +|); + +sub import { + my ($class, $dbh_ref, $filename) = @_; + return if @_ == 1; + croak "use TrsrDB \$your_db_handle missing" if !defined $dbh_ref; + $$dbh_ref = $class->connect( + "DBI:SQLite:" . ($filename // ":memory:"), + "", "", { + sqlite_unicode => 1, + on_connect_call => 'use_foreign_keys', + on_connect_do => 'PRAGMA recursive_triggers = 1', + } + ); +} + +1; diff --git a/TrsrDB/Account.pm b/TrsrDB/Account.pm new file mode 100644 index 0000000..a48a46e --- /dev/null +++ b/TrsrDB/Account.pm @@ -0,0 +1,34 @@ +use strict; + +package TrsrDB::Account; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('Account'); +__PACKAGE__->add_columns(qw/ID type altId IBAN/); +__PACKAGE__->set_primary_key('ID'); + +__PACKAGE__->has_many( + statement_rows => 'TrsrDB::ReconstructedBankStatement', + { 'foreign.account' => 'self.ID' } +); +__PACKAGE__->has_many( + debts => 'TrsrDB::Debit', + { 'foreign.debtor' => 'self.ID' } +); +__PACKAGE__->has_many( + current_debts => 'TrsrDB::CurrentDebts', + { 'foreign.debtor' => 'self.ID' } +); +__PACKAGE__->has_many( + credits => 'TrsrDB::Credit', + { 'foreign.account' => 'self.ID' } +); +__PACKAGE__->has_many( + available_credits => 'TrsrDB::AvailableCredits', + { 'foreign.account' => 'self.ID' } +); +__PACKAGE__->has_one( + balance => 'TrsrDB::Balance', 'ID' +); + +1; diff --git a/TrsrDB/AvailableCredits.pm b/TrsrDB/AvailableCredits.pm new file mode 100644 index 0000000..00490f3 --- /dev/null +++ b/TrsrDB/AvailableCredits.pm @@ -0,0 +1,19 @@ +use strict; + +package TrsrDB::AvailableCredits; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('AvailableCredits'); +__PACKAGE__->add_columns(qw/ Id account date purpose difference /); +__PACKAGE__->set_primary_key("Id"); + +__PACKAGE__->belongs_to( + account => 'TrsrDB::Account', + { 'foreign.ID' => 'self.account' } +); + +__PACKAGE__->many_to_many( + suggested_to_pay => account => 'current_debts' +); + +1; diff --git a/TrsrDB/Balance.pm b/TrsrDB/Balance.pm new file mode 100644 index 0000000..4f540d5 --- /dev/null +++ b/TrsrDB/Balance.pm @@ -0,0 +1,14 @@ +use strict; + +package TrsrDB::Balance; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table("Balance"); +__PACKAGE__->add_columns(qw/ID credit promised debt/); +__PACKAGE__->set_primary_key("ID"); + +__PACKAGE__->belongs_to( + account => 'TrsrDB::Account', 'ID' +); + +1; diff --git a/TrsrDB/Credit.pm b/TrsrDB/Credit.pm new file mode 100644 index 0000000..064687f --- /dev/null +++ b/TrsrDB/Credit.pm @@ -0,0 +1,29 @@ +use strict; + +package TrsrDB::Credit; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('Credit'); +__PACKAGE__->add_column("Id" => { data_type => 'INTEGER' }); +__PACKAGE__->add_column("account"); +__PACKAGE__->add_column("date" => { data_type => 'DATE' }); +__PACKAGE__->add_column("purpose"); +__PACKAGE__->add_column("value" => { data_type => 'INTEGER' }); +__PACKAGE__->add_column("spent" => { data_type => 'INTEGER', default => 0 }); +__PACKAGE__->set_primary_key("Id"); + +__PACKAGE__->belongs_to( + account => 'TrsrDB::Account', + { 'foreign.ID' => 'self.account' } +); + +__PACKAGE__->has_many( + outgoings => 'TrsrDB::Transfer', + { 'foreign.fromCredit' => 'self.Id' } +); + +__PACKAGE__->many_to_many( + paid_bills => 'outgoings' => 'debit' +); + +1; diff --git a/TrsrDB/CurrentDebts.pm b/TrsrDB/CurrentDebts.pm new file mode 100644 index 0000000..fe47d0e --- /dev/null +++ b/TrsrDB/CurrentDebts.pm @@ -0,0 +1,19 @@ +use strict; + +package TrsrDB::CurrentDebts; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('CurrentDebts'); +__PACKAGE__->add_columns(qw/billId debtor targetCredit date purpose difference/); +__PACKAGE__->set_primary_key("billId"); + +__PACKAGE__->belongs_to( + account => 'TrsrDB::Account', + { 'foreign.ID' => 'self.account' } +); + +__PACKAGE__->many_to_many( + payable_with => account => 'available_credits' +); + +1; diff --git a/TrsrDB/Debit.pm b/TrsrDB/Debit.pm new file mode 100644 index 0000000..edf83bb --- /dev/null +++ b/TrsrDB/Debit.pm @@ -0,0 +1,29 @@ +use strict; + +package TrsrDB::Debit; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('Debit'); +__PACKAGE__->add_column("billId"); +__PACKAGE__->add_column("debtor"); +__PACKAGE__->add_column("targetCredit" => { data_type => 'INTEGER' }); +__PACKAGE__->add_column("date" => { data_type => 'DATE' }); +__PACKAGE__->add_column("purpose"); +__PACKAGE__->add_column("value" => { data_type => 'INTEGER' }); +__PACKAGE__->add_column("paid" => { data_type => 'INTEGER', default => 0 }); +__PACKAGE__->set_primary_key("billId"); + +__PACKAGE__->belongs_to( + account => 'TrsrDB::Account', + { 'foreign.ID' => 'self.debtor' } +); + +__PACKAGE__->has_many( + incomings => 'TrsrDB::Transfer', 'billId' +); + +__PACKAGE__->many_to_many( + paid_with => incomings => 'credit' +); + +1; diff --git a/TrsrDB/History.pm b/TrsrDB/History.pm new file mode 100644 index 0000000..1afdbdb --- /dev/null +++ b/TrsrDB/History.pm @@ -0,0 +1,14 @@ +use strict; + +package TrsrDB::History; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('History'); +__PACKAGE__->add_columns(qw/date purpose account credit debit contra billId/); + +__PACKAGE__->belongs_to( + account => 'TrsrDB::Account', + { 'foreign.ID' => 'self.account' } +); + +1; diff --git a/TrsrDB/ReconstructedBankStatement.pm b/TrsrDB/ReconstructedBankStatement.pm new file mode 100644 index 0000000..0ed4639 --- /dev/null +++ b/TrsrDB/ReconstructedBankStatement.pm @@ -0,0 +1,9 @@ +use strict; + +package TrsrDB::ReconstructedBankStatement; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('ReconstructedBankStatement'); +__PACKAGE__->add_columns(qw/date purpose account credit debit/); + +1; diff --git a/TrsrDB/Transfer.pm b/TrsrDB/Transfer.pm new file mode 100644 index 0000000..214ceb0 --- /dev/null +++ b/TrsrDB/Transfer.pm @@ -0,0 +1,22 @@ +use strict; + +package TrsrDB::Transfer; +use base qw/DBIx::Class::Core/; + +__PACKAGE__->table('Transfer'); +__PACKAGE__->add_column("timestamp" => { data_type => 'TIMESTAMP' }); +__PACKAGE__->add_column("billId"); +__PACKAGE__->add_column("fromCredit"); +__PACKAGE__->add_column("amount" => { data_type => 'INTEGER', nullable => 1 }); +__PACKAGE__->set_primary_key("billId", "fromCredit"); + +__PACKAGE__->belongs_to( + credit => 'TrsrDB::Credit', + { 'foreign.Id' => 'self.fromCredit' } +); + +__PACKAGE__->belongs_to( + debit => 'TrsrDB::Credit', 'billId' +); + +1; diff --git a/t/schema.t b/t/schema.t new file mode 100644 index 0000000..df62465 --- /dev/null +++ b/t/schema.t @@ -0,0 +1,79 @@ +use strict; + +my $db; +use TrsrDB \$db => 'trsr.db'; +use Test::More; + +$db->resultset("Account")->create({ ID => "Club", altId => 1, type => 'eV' }); +$db->resultset("Account")->create({ ID => "john", altId => 44, type => 'Member' }); +$db->resultset("Account")->create({ + ID => "alex", altId => 6, type => 'Member', + IBAN => 'DE1234567890123456' # used for verification in outgoing bank transfers +}); + +is_deeply + [ $db->resultset("Account")->search( + {}, { -order_by => { asc => ['ID'] } } + )->get_column('ID')->all + ], + [ qw/ Club alex john / ], + "Registering new accounts" +; + +$db->resultset("Credit")->create($_) for + { account => "Club", date => "2016-01-01", + purpose => "Membership fees May 2016 until incl. April 2017", + value => 0 + }, + { account => "john", date => "2016-04-23", + purpose => "Membership fee 2016f.", + value => 7200 + }, + { account => "alex", date => "2016-01-15", + purpose => "Payment for Server Hosting 2016", + value => 0, + } +; + +is_deeply [ map { $db->resultset("Account")->find($_)->credits->count() } qw(Club john alex) ], + [ 1, 1, 1 ], "Entering one credit per account"; + +my %months = ( + '05' => 'May 2016', '06' => 'June 2016', '07' => 'July 2016', '08' => 'August 2016', + '09' => 'September 2016', '10' => 'October 2016', '11' => 'November 2016', '12' => 'December 2016', + '01' => 'January 2017', '02' => 'February 2017', '03' => 'March 2017', '04' => 'April 2017', +); +while ( my ($num, $month) = each %months ) { + my $yy = substr $month, -2; + $db->resultset("Debit")->create({ + billId => "MB$yy$num-john", + debtor => "john", + targetCredit => 1, + date => "16-05-01", + purpose => "Membership fee $month", + value => 600 + }); +} + +is $db->resultset("Account")->find("john")->current_debts->count(), 12, + "Entering outstanding member fees for john"; + +$db->resultset("Account")->find("Club")->add_to_debts({ + billId => "TWX2016/123", + targetCredit => 3, + date => "2016-01-15", + purpose => "Server Hosting 2016", + value => 23450 +}); + +is $db->resultset("Debit")->search({ debtor => 'Club' })->single->billId, "TWX2016/123", "Invoicing server hosting for club"; + +is_deeply { map { $_->ID => {$_->get_columns} } $db->resultset("Balance")->all }, + { john => { ID => 'john', credit => 7200, debt => 7200, promised => 0 }, + Club => { ID => 'Club', credit => 0, debt => 23450, promised => 7200 }, + alex => { ID => 'alex', credit => 0, debt => 0, promised => 23450 }, + }, + "Get balances" +; + +done_testing();