From c1fb530eea90fcf7c1c294b44d6d833e4dbcd809 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 24 Jun 2018 12:01:23 -0400 Subject: add scheduled transactions --- lib/WWW/YNAB.pm | 72 +++++++++++++++++++----- lib/WWW/YNAB/Budget.pm | 42 ++++++++++++++ lib/WWW/YNAB/ScheduledSubTransaction.pm | 48 ++++++++++++++++ lib/WWW/YNAB/ScheduledTransaction.pm | 98 +++++++++++++++++++++++++++++++++ t/budget.t | 34 ++++++++++++ t/lib/WWW/YNAB/MockUA.pm | 85 +++++++++++++++++++++++++++- t/scheduled_transaction.t | 55 ++++++++++++++++++ 7 files changed, 417 insertions(+), 17 deletions(-) create mode 100644 lib/WWW/YNAB/ScheduledSubTransaction.pm create mode 100644 lib/WWW/YNAB/ScheduledTransaction.pm create mode 100644 t/scheduled_transaction.t diff --git a/lib/WWW/YNAB.pm b/lib/WWW/YNAB.pm index 192b5b0..74d0f4d 100644 --- a/lib/WWW/YNAB.pm +++ b/lib/WWW/YNAB.pm @@ -9,6 +9,8 @@ use WWW::YNAB::CategoryGroup; use WWW::YNAB::Category; use WWW::YNAB::Month; use WWW::YNAB::Payee; +use WWW::YNAB::ScheduledSubTransaction; +use WWW::YNAB::ScheduledTransaction; use WWW::YNAB::SubTransaction; use WWW::YNAB::Transaction; use WWW::YNAB::UA; @@ -117,21 +119,27 @@ sub budget { my @transactions = map { my %transaction = %$_; - ($transaction{account_name}) = map { - $_->{name} - } grep { - $_->{id} eq $transaction{account_id} - } @{ $budget{accounts} }; - ($transaction{payee_name}) = map { - $_->{name} - } grep { - $_->{id} eq $transaction{payee_id} - } @{ $budget{payees} }; - ($transaction{category_name}) = map { - $_->{name} - } grep { - $_->{id} eq $transaction{category_id} - } @{ $budget{categories} }; + if ($transaction{account_id}) { + ($transaction{account_name}) = map { + $_->{name} + } grep { + $_->{id} eq $transaction{account_id} + } @{ $budget{accounts} }; + } + if ($transaction{payee_id}) { + ($transaction{payee_name}) = map { + $_->{name} + } grep { + $_->{id} eq $transaction{payee_id} + } @{ $budget{payees} }; + } + if ($transaction{category_id}) { + ($transaction{category_name}) = map { + $_->{name} + } grep { + $_->{id} eq $transaction{category_id} + } @{ $budget{categories} }; + } $transaction{subtransactions} = [ map { $self->model_from_data('WWW::YNAB::SubTransaction', $_) @@ -143,6 +151,40 @@ sub budget { } @{ $budget{transactions} }; $budget{transactions} = \@transactions; + my @scheduled_transactions = map { + my %transaction = %$_; + if ($transaction{account_id}) { + ($transaction{account_name}) = map { + $_->{name} + } grep { + $_->{id} eq $transaction{account_id} + } @{ $budget{accounts} }; + } + if ($transaction{payee_id}) { + ($transaction{payee_name}) = map { + $_->{name} + } grep { + $_->{id} eq $transaction{payee_id} + } @{ $budget{payees} }; + } + if ($transaction{category_id}) { + ($transaction{category_name}) = map { + $_->{name} + } grep { + $_->{id} eq $transaction{category_id} + } @{ $budget{categories} }; + } + $transaction{subtransactions} = [ + map { + $self->model_from_data('WWW::YNAB::ScheduledSubTransaction', $_) + } grep { + $_->{scheduled_transaction_id} eq $transaction{id} + } @{ $budget{scheduled_subtransactions} } + ]; + $self->model_from_data('WWW::YNAB::ScheduledTransaction', \%transaction) + } @{ $budget{scheduled_transactions} }; + $budget{scheduled_transactions} = \@scheduled_transactions; + $self->model_from_data( 'WWW::YNAB::Budget', \%budget, diff --git a/lib/WWW/YNAB/Budget.pm b/lib/WWW/YNAB/Budget.pm index 97d0d84..5a8bdee 100644 --- a/lib/WWW/YNAB/Budget.pm +++ b/lib/WWW/YNAB/Budget.pm @@ -98,6 +98,18 @@ has _transactions => ( } ); +has _scheduled_transactions => ( + traits => ['Array'], + is => 'ro', + isa => 'ArrayRef[WWW::YNAB::ScheduledTransaction]', + init_arg => 'scheduled_transactions', + lazy => 1, + builder => '_build_scheduled_transactions', + handles => { + scheduled_transactions => 'elements', + } +); + has _ua => ( is => 'ro', isa => 'WWW::YNAB::UA', @@ -257,6 +269,36 @@ sub transaction { $self->model_from_data('WWW::YNAB::Transaction', \%transaction); } +sub _build_scheduled_transactions { + my $self = shift; + + my $data = $self->_ua->get("/budgets/${\$self->id}/scheduled_transactions"); + [ + map { + my %transaction = %$_; + my @subtransactions = map { + $self->model_from_data('WWW::YNAB::ScheduledSubTransaction', $_) + } @{ $transaction{subtransactions} }; + $transaction{subtransactions} = \@subtransactions; + $self->model_from_data('WWW::YNAB::ScheduledTransaction', \%transaction) + } @{ $data->{data}{scheduled_transactions} } + ] +} + +sub scheduled_transaction { + my $self = shift; + my ($id) = @_; + + my $data = $self->_ua->get("/budgets/${\$self->id}/scheduled_transactions/$id"); + my $transaction = $data->{data}{scheduled_transaction}; + my %transaction = %$transaction; + my @subtransactions = map { + $self->model_from_data('WWW::YNAB::ScheduledSubTransaction', $_) + } @{ $transaction{subtransactions} }; + $transaction{subtransactions} = \@subtransactions; + $self->model_from_data('WWW::YNAB::ScheduledTransaction', \%transaction); +} + __PACKAGE__->meta->make_immutable; no Moose; diff --git a/lib/WWW/YNAB/ScheduledSubTransaction.pm b/lib/WWW/YNAB/ScheduledSubTransaction.pm new file mode 100644 index 0000000..d275f4d --- /dev/null +++ b/lib/WWW/YNAB/ScheduledSubTransaction.pm @@ -0,0 +1,48 @@ +package WWW::YNAB::ScheduledSubTransaction; +use Moose; + +has id => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has scheduled_transaction_id => ( + is => 'ro', + isa => 'Str', +); + +has amount => ( + is => 'ro', + isa => 'Int', +); + +has memo => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has payee_id => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has category_id => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has transfer_account_id => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has deleted => ( + is => 'ro', + isa => 'Bool', +); + +__PACKAGE__->meta->make_immutable; +no Moose; + +1; diff --git a/lib/WWW/YNAB/ScheduledTransaction.pm b/lib/WWW/YNAB/ScheduledTransaction.pm new file mode 100644 index 0000000..98c67f1 --- /dev/null +++ b/lib/WWW/YNAB/ScheduledTransaction.pm @@ -0,0 +1,98 @@ +package WWW::YNAB::ScheduledTransaction; +use Moose; + +use Moose::Util::TypeConstraints qw(enum maybe_type); + +has id => ( + is => 'ro', + isa => 'Str', + required => 1, +); + +has date_first => ( + is => 'ro', + isa => 'Str', +); + +has date_next => ( + is => 'ro', + isa => 'Str', +); + +has frequency => ( + is => 'ro', + isa => enum([qw( + never daily weekly everyOtherWeek twiceAMonth every4Weeks + monthly everyOtherMonth every3Months every4Months twiceAYear + yearly everyOtherYear + )]), +); + +has amount => ( + is => 'ro', + isa => 'Int', +); + +has memo => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has flag_color => ( + is => 'ro', + isa => maybe_type(enum([qw(red orange yellow green blue purple)])), +); + +has account_id => ( + is => 'ro', + isa => 'Str', +); + +has payee_id => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has category_id => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has transfer_account_id => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has deleted => ( + is => 'ro', + isa => 'Bool', +); + +has account_name => ( + is => 'ro', + isa => 'Str', +); + +has payee_name => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has category_name => ( + is => 'ro', + isa => 'Maybe[Str]', +); + +has subtransactions => ( + traits => ['Array'], + is => 'bare', + isa => 'ArrayRef[WWW::YNAB::ScheduledSubTransaction]', + handles => { + subtransactions => 'elements', + } +); + +__PACKAGE__->meta->make_immutable; +no Moose; + +1; diff --git a/t/budget.t b/t/budget.t index 3886365..fe46d38 100644 --- a/t/budget.t +++ b/t/budget.t @@ -113,6 +113,40 @@ is($subtransactions[0]->category_id, "33333333-3333-3333-3333-444444444444"); is($subtransactions[0]->transfer_account_id, undef); ok(!$subtransactions[0]->deleted); +my @scheduled_transactions = $budget->scheduled_transactions; +is(scalar @scheduled_transactions, 1); +isa_ok($scheduled_transactions[0], 'WWW::YNAB::ScheduledTransaction'); +is($scheduled_transactions[0]->id, "66666666-6666-6666-6666-666666666666"); +is($scheduled_transactions[0]->date_first, "2018-06-05"); +is($scheduled_transactions[0]->date_next, "2018-07-05"); +is($scheduled_transactions[0]->frequency, "monthly"); +is($scheduled_transactions[0]->amount, -100000); +is($scheduled_transactions[0]->memo, "cable"); +is($scheduled_transactions[0]->flag_color, "purple"); +is($scheduled_transactions[0]->account_id, "00000000-0000-0000-0000-111111111111"); +is($scheduled_transactions[0]->payee_id, undef); +is($scheduled_transactions[0]->category_id, "33333333-3333-3333-3333-666666666666"); +is($scheduled_transactions[0]->transfer_account_id, undef); +ok(!$scheduled_transactions[0]->deleted); +is($scheduled_transactions[0]->account_name, "Checking Account"); +is($scheduled_transactions[0]->payee_name, undef); +# this is i think a bug in their api - they don't return a category object for +# "split" even though they have a category id for it, and the name shows up in +# the direct transaction/scheduled_transaction individual object request +is($scheduled_transactions[0]->category_name, undef); +is(scalar $scheduled_transactions[0]->subtransactions, 2); + +my @scheduled_subtransactions = $scheduled_transactions[0]->subtransactions; +is(scalar @scheduled_subtransactions, 2); +is($scheduled_subtransactions[0]->id, "77777777-7777-7777-7777-777777777777"); +is($scheduled_subtransactions[0]->scheduled_transaction_id, "66666666-6666-6666-6666-666666666666"); +is($scheduled_subtransactions[0]->amount, -50000); +is($scheduled_subtransactions[0]->memo, "tv"); +is($scheduled_subtransactions[0]->payee_id, undef); +is($scheduled_subtransactions[0]->category_id, "33333333-3333-3333-3333-444444444444"); +is($scheduled_subtransactions[0]->transfer_account_id, undef); +ok(!$scheduled_subtransactions[0]->deleted); + is(scalar $ua->test_requests, 1); done_testing; diff --git a/t/lib/WWW/YNAB/MockUA.pm b/t/lib/WWW/YNAB/MockUA.pm index 24519cd..1827111 100644 --- a/t/lib/WWW/YNAB/MockUA.pm +++ b/t/lib/WWW/YNAB/MockUA.pm @@ -377,8 +377,44 @@ EOF "deleted": false } ], - "scheduled_transactions": [], - "scheduled_subtransactions": [] + "scheduled_transactions": [ + { + "id": "66666666-6666-6666-6666-666666666666", + "date_first": "2018-06-05", + "date_next": "2018-07-05", + "frequency": "monthly", + "amount": -100000, + "memo": "cable", + "flag_color": "purple", + "account_id": "00000000-0000-0000-0000-111111111111", + "payee_id": null, + "category_id": "33333333-3333-3333-3333-666666666666", + "transfer_account_id": null, + "deleted": false + } + ], + "scheduled_subtransactions": [ + { + "id": "77777777-7777-7777-7777-777777777777", + "scheduled_transaction_id": "66666666-6666-6666-6666-666666666666", + "amount": -50000, + "memo": "tv", + "payee_id": null, + "category_id": "33333333-3333-3333-3333-444444444444", + "transfer_account_id": null, + "deleted": false + }, + { + "id": "77777777-7777-7777-7777-888888888888", + "scheduled_transaction_id": "66666666-6666-6666-6666-666666666666", + "amount": -50000, + "memo": "internet", + "payee_id": null, + "category_id": "33333333-3333-3333-3333-444444444444", + "transfer_account_id": null, + "deleted": false + } + ] }, "server_knowledge": 1 } @@ -533,6 +569,51 @@ EOF } } } +EOF + 'https://api.youneedabudget.com/v1/budgets/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/scheduled_transactions/66666666-6666-6666-6666-666666666666' => <<'EOF', +{ + "data": { + "scheduled_transaction": { + "id": "66666666-6666-6666-6666-666666666666", + "date_first": "2018-06-05", + "date_next": "2018-07-05", + "frequency": "monthly", + "amount": -100000, + "memo": "cable", + "flag_color": "purple", + "account_id": "00000000-0000-0000-0000-111111111111", + "account_name": "Checking Account", + "payee_id": null, + "payee_name": null, + "category_id": "33333333-3333-3333-3333-666666666666", + "category_name": "Split (Multiple Categories)...", + "transfer_account_id": null, + "deleted": false, + "subtransactions": [ + { + "id": "77777777-7777-7777-7777-777777777777", + "scheduled_transaction_id": "66666666-6666-6666-6666-666666666666", + "amount": -50000, + "memo": "tv", + "payee_id": null, + "category_id": "33333333-3333-3333-3333-444444444444", + "transfer_account_id": null, + "deleted": false + }, + { + "id": "77777777-7777-7777-7777-888888888888", + "scheduled_transaction_id": "66666666-6666-6666-6666-666666666666", + "amount": -50000, + "memo": "internet", + "payee_id": null, + "category_id": "33333333-3333-3333-3333-444444444444", + "transfer_account_id": null, + "deleted": false + } + ] + } + } +} EOF ); diff --git a/t/scheduled_transaction.t b/t/scheduled_transaction.t new file mode 100644 index 0000000..98960ea --- /dev/null +++ b/t/scheduled_transaction.t @@ -0,0 +1,55 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use Test::More; + +use lib 't/lib'; + +use WWW::YNAB; +use WWW::YNAB::MockUA; + +my $ua = WWW::YNAB::MockUA->new; +my $ynab = WWW::YNAB->new( + access_token => 'abcdef', + ua => $ua, +); + +is(scalar $ua->test_requests, 0); + +my $budget = $ynab->budget('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'); +is(scalar $ua->test_requests, 1); +isa_ok($budget, 'WWW::YNAB::Budget'); + +my $scheduled_transaction = $budget->scheduled_transaction('66666666-6666-6666-6666-666666666666'); +is(scalar $ua->test_requests, 2); +isa_ok($scheduled_transaction, 'WWW::YNAB::ScheduledTransaction'); + +is($scheduled_transaction->id, "66666666-6666-6666-6666-666666666666"); +is($scheduled_transaction->date_first, "2018-06-05"); +is($scheduled_transaction->date_next, "2018-07-05"); +is($scheduled_transaction->frequency, "monthly"); +is($scheduled_transaction->amount, -100000); +is($scheduled_transaction->memo, "cable"); +is($scheduled_transaction->flag_color, "purple"); +is($scheduled_transaction->account_id, "00000000-0000-0000-0000-111111111111"); +is($scheduled_transaction->payee_id, undef); +is($scheduled_transaction->category_id, "33333333-3333-3333-3333-666666666666"); +is($scheduled_transaction->transfer_account_id, undef); +ok(!$scheduled_transaction->deleted); +is($scheduled_transaction->account_name, "Checking Account"); +is($scheduled_transaction->payee_name, undef); +is($scheduled_transaction->category_name, "Split (Multiple Categories)..."); +is(scalar $scheduled_transaction->subtransactions, 2); + +my @scheduled_subtransactions = $scheduled_transaction->subtransactions; +is(scalar @scheduled_subtransactions, 2); +is($scheduled_subtransactions[0]->id, "77777777-7777-7777-7777-777777777777"); +is($scheduled_subtransactions[0]->scheduled_transaction_id, "66666666-6666-6666-6666-666666666666"); +is($scheduled_subtransactions[0]->amount, -50000); +is($scheduled_subtransactions[0]->memo, "tv"); +is($scheduled_subtransactions[0]->payee_id, undef); +is($scheduled_subtransactions[0]->category_id, "33333333-3333-3333-3333-444444444444"); +is($scheduled_subtransactions[0]->transfer_account_id, undef); +ok(!$scheduled_subtransactions[0]->deleted); + +done_testing; -- cgit v1.2.3