2018-02-06 07:49:56 +01:00
< ? php
/**
* BillTransformer . php
2019-10-02 06:37:26 +02:00
* Copyright ( c ) 2019 thegrumpydictator @ gmail . com
2018-02-06 07:49:56 +01:00
*
2019-10-02 06:37:26 +02:00
* This file is part of Firefly III ( https :// github . com / firefly - iii ) .
2018-02-06 07:49:56 +01:00
*
2019-10-02 06:37:26 +02:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
2018-02-06 07:49:56 +01:00
*
2019-10-02 06:37:26 +02:00
* This program is distributed in the hope that it will be useful ,
2018-02-06 07:49:56 +01:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2019-10-02 06:37:26 +02:00
* GNU Affero General Public License for more details .
2018-02-06 07:49:56 +01:00
*
2019-10-02 06:37:26 +02:00
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2018-02-06 07:49:56 +01:00
*/
declare ( strict_types = 1 );
2018-02-06 19:49:29 +01:00
namespace FireflyIII\Transformers ;
2018-02-06 07:49:56 +01:00
use Carbon\Carbon ;
use FireflyIII\Models\Bill ;
2019-08-21 04:59:35 +02:00
use FireflyIII\Models\TransactionJournal ;
2018-02-06 07:49:56 +01:00
use FireflyIII\Repositories\Bill\BillRepositoryInterface ;
use Illuminate\Support\Collection ;
2018-02-17 10:47:06 +01:00
use Log ;
2018-02-06 07:49:56 +01:00
/**
* Class BillTransformer
*/
2018-12-16 13:55:19 +01:00
class BillTransformer extends AbstractTransformer
2018-02-06 07:49:56 +01:00
{
2018-09-27 07:43:30 +02:00
/** @var BillRepositoryInterface */
private $repository ;
2018-02-06 07:49:56 +01:00
/**
* BillTransformer constructor .
*
2018-02-16 22:47:08 +01:00
* @ codeCoverageIgnore
2018-02-06 07:49:56 +01:00
*/
2018-12-16 13:55:19 +01:00
public function __construct ()
2018-02-06 07:49:56 +01:00
{
2018-09-27 07:43:30 +02:00
$this -> repository = app ( BillRepositoryInterface :: class );
2018-12-15 07:59:02 +01:00
if ( 'testing' === config ( 'app.env' )) {
2019-06-07 18:20:15 +02:00
Log :: warning ( sprintf ( '%s should not be instantiated in the TEST environment!' , get_class ( $this )));
2018-12-15 07:59:02 +01:00
}
2018-02-06 07:49:56 +01:00
}
/**
2018-02-16 22:47:08 +01:00
* Transform the bill .
*
2018-02-06 07:49:56 +01:00
* @ param Bill $bill
*
* @ return array
*/
public function transform ( Bill $bill ) : array
{
$paidData = $this -> paidData ( $bill );
2018-02-07 10:49:24 +01:00
$payDates = $this -> payDates ( $bill );
2019-12-20 05:38:26 +01:00
2018-12-19 06:06:01 +01:00
$currency = $bill -> transactionCurrency ;
$notes = $this -> repository -> getNoteText ( $bill );
$notes = '' === $notes ? null : $notes ;
2018-09-27 07:43:30 +02:00
$this -> repository -> setUser ( $bill -> user );
$data = [
2018-12-19 06:06:01 +01:00
'id' => ( int ) $bill -> id ,
'created_at' => $bill -> created_at -> toAtomString (),
'updated_at' => $bill -> updated_at -> toAtomString (),
'currency_id' => $bill -> transaction_currency_id ,
'currency_code' => $currency -> code ,
'currency_symbol' => $currency -> symbol ,
'currency_decimal_places' => $currency -> decimal_places ,
'name' => $bill -> name ,
'amount_min' => round (( float ) $bill -> amount_min , $currency -> decimal_places ),
'amount_max' => round (( float ) $bill -> amount_max , $currency -> decimal_places ),
'date' => $bill -> date -> format ( 'Y-m-d' ),
'repeat_freq' => $bill -> repeat_freq ,
'skip' => ( int ) $bill -> skip ,
'active' => $bill -> active ,
'notes' => $notes ,
'next_expected_match' => $paidData [ 'next_expected_match' ],
'pay_dates' => $payDates ,
'paid_dates' => $paidData [ 'paid_dates' ],
'links' => [
2018-02-06 07:49:56 +01:00
[
'rel' => 'self' ,
2018-02-11 07:46:34 +01:00
'uri' => '/bills/' . $bill -> id ,
2018-02-06 07:49:56 +01:00
],
],
];
return $data ;
}
/**
* Returns the latest date in the set , or start when set is empty .
*
* @ param Collection $dates
* @ param Carbon $default
*
* @ return Carbon
*/
protected function lastPaidDate ( Collection $dates , Carbon $default ) : Carbon
{
if ( 0 === $dates -> count ()) {
return $default ; // @codeCoverageIgnore
}
2019-08-21 04:59:35 +02:00
$latest = $dates -> first () -> date ;
/** @var TransactionJournal $date */
foreach ( $dates as $journal ) {
if ( $journal -> date -> gte ( $latest )) {
$latest = $journal -> date ;
2018-02-06 07:49:56 +01:00
}
}
return $latest ;
}
/**
* Given a bill and a date , this method will tell you at which moment this bill expects its next
* transaction . Whether or not it is there already , is not relevant .
*
* @ param Bill $bill
* @ param Carbon $date
*
2019-08-17 10:47:29 +02:00
* @ return Carbon
2018-02-06 07:49:56 +01:00
*/
protected function nextDateMatch ( Bill $bill , Carbon $date ) : Carbon
{
2019-12-20 05:38:26 +01:00
Log :: debug ( sprintf ( 'Now in nextDateMatch(%d, %s)' , $bill -> id , $date -> format ( 'Y-m-d' )));
2018-02-06 07:49:56 +01:00
$start = clone $bill -> date ;
2019-12-20 05:38:26 +01:00
Log :: debug ( sprintf ( 'Bill start date is %s' , $start -> format ( 'Y-m-d' )));
2018-02-06 07:49:56 +01:00
while ( $start < $date ) {
2019-12-20 05:38:26 +01:00
Log :: debug (
sprintf (
'%s (bill start date) < %s (given date) so we jump ahead one period (with a skip maybe).' , $start -> format ( 'Y-m-d' ), $date -> format ( 'Y-m-d' )
)
);
2018-02-06 07:49:56 +01:00
$start = app ( 'navigation' ) -> addPeriod ( $start , $bill -> repeat_freq , $bill -> skip );
}
2019-12-20 05:38:26 +01:00
Log :: debug ( sprintf ( 'End of loop, bill start date is now %s' , $start -> format ( 'Y-m-d' )));
2018-02-06 07:49:56 +01:00
return $start ;
}
/**
2018-02-17 10:47:06 +01:00
* Get the data the bill was paid and predict the next expected match .
*
2018-02-06 07:49:56 +01:00
* @ param Bill $bill
*
* @ return array
*/
protected function paidData ( Bill $bill ) : array
{
2018-02-17 10:47:06 +01:00
Log :: debug ( sprintf ( 'Now in paidData for bill #%d' , $bill -> id ));
2018-04-02 14:50:17 +02:00
if ( null === $this -> parameters -> get ( 'start' ) || null === $this -> parameters -> get ( 'end' )) {
2018-02-17 10:47:06 +01:00
Log :: debug ( 'parameters are NULL, return empty array' );
2018-02-06 18:11:33 +01:00
return [
'paid_dates' => [],
'next_expected_match' => null ,
];
}
2018-12-19 06:06:01 +01:00
$set = $this -> repository -> getPaidDatesInRange ( $bill , $this -> parameters -> get ( 'start' ), $this -> parameters -> get ( 'end' ));
2018-02-17 10:47:06 +01:00
Log :: debug ( sprintf ( 'Count %d entries in getPaidDatesInRange()' , $set -> count ()));
2018-02-06 07:49:56 +01:00
// calculate next expected match:
2018-02-11 20:45:33 +01:00
$lastPaidDate = $this -> lastPaidDate ( $set , $this -> parameters -> get ( 'start' ));
2018-02-06 07:49:56 +01:00
$nextMatch = clone $bill -> date ;
while ( $nextMatch < $lastPaidDate ) {
$nextMatch = app ( 'navigation' ) -> addPeriod ( $nextMatch , $bill -> repeat_freq , $bill -> skip );
}
$end = app ( 'navigation' ) -> addPeriod ( $nextMatch , $bill -> repeat_freq , $bill -> skip );
2019-08-21 04:59:35 +02:00
if ( $set -> count () > 0 ) {
2018-02-06 07:49:56 +01:00
$nextMatch = clone $end ;
}
2019-08-21 04:59:35 +02:00
$result = [];
foreach ( $set as $entry ) {
$result [] = [
'transaction_group_id' => ( int ) $entry -> transaction_group_id ,
'transaction_journal_id' => ( int ) $entry -> id ,
'date' => $entry -> date -> format ( 'Y-m-d' ),
];
}
2018-02-06 07:49:56 +01:00
return [
2019-08-21 04:59:35 +02:00
'paid_dates' => $result ,
2018-02-06 07:49:56 +01:00
'next_expected_match' => $nextMatch -> format ( 'Y-m-d' ),
];
}
/**
* @ param Bill $bill
*
* @ return array
*/
protected function payDates ( Bill $bill ) : array
{
2019-12-20 05:38:26 +01:00
Log :: debug ( sprintf ( 'Now in payDates() for bill #%d' , $bill -> id ));
2018-04-02 14:50:17 +02:00
if ( null === $this -> parameters -> get ( 'start' ) || null === $this -> parameters -> get ( 'end' )) {
2019-12-20 05:38:26 +01:00
Log :: debug ( 'No start or end date, give empty array.' );
2018-02-07 10:49:24 +01:00
return [];
}
2019-12-20 05:38:26 +01:00
Log :: debug (
sprintf (
'Start date is %s, end is %s' , $this -> parameters -> get ( 'start' ) -> format ( 'Y-m-d' ),
$this -> parameters -> get ( 'end' ) -> format ( 'Y-m-d' )
)
);
2018-02-06 07:49:56 +01:00
$set = new Collection ;
2018-02-11 20:45:33 +01:00
$currentStart = clone $this -> parameters -> get ( 'start' );
2019-12-20 05:38:26 +01:00
$loop = 0 ;
2018-02-11 20:45:33 +01:00
while ( $currentStart <= $this -> parameters -> get ( 'end' )) {
2019-12-20 05:38:26 +01:00
Log :: debug (
sprintf (
'In loop #%d, where %s (start param) <= %s (end param).' , $loop , $currentStart -> format ( 'Y-m-d' ),
$this -> parameters -> get ( 'end' ) -> format ( 'Y-m-d' )
)
);
2018-02-06 07:49:56 +01:00
$nextExpectedMatch = $this -> nextDateMatch ( $bill , $currentStart );
2019-12-20 05:38:26 +01:00
Log :: debug ( sprintf ( 'Next expected match is %s' , $nextExpectedMatch -> format ( 'Y-m-d' )));
2018-02-06 07:49:56 +01:00
// If nextExpectedMatch is after end, we continue:
2018-02-11 20:45:33 +01:00
if ( $nextExpectedMatch > $this -> parameters -> get ( 'end' )) {
2019-12-20 05:38:26 +01:00
Log :: debug (
sprintf ( '%s is > %s, so were not going to use it.' , $nextExpectedMatch -> format ( 'Y-m-d' ), $this -> parameters -> get ( 'end' ) -> format ( 'Y-m-d' ))
);
2018-02-06 07:49:56 +01:00
break ;
}
// add to set
$set -> push ( clone $nextExpectedMatch );
2020-01-09 19:42:28 +01:00
Log :: debug ( sprintf ( 'Add next expected match (%s) to set because its in the current start/end range, which now contains %d item(s)' , $nextExpectedMatch -> format ( 'Y-m-d' ), $set -> count ()));
2018-02-06 07:49:56 +01:00
$nextExpectedMatch -> addDay ();
$currentStart = clone $nextExpectedMatch ;
2019-12-20 05:38:26 +01:00
$loop ++ ;
2018-02-06 07:49:56 +01:00
}
2019-12-20 05:38:26 +01:00
Log :: debug ( sprintf ( 'Loop has ended after %d loops' , $loop ));
2018-02-06 07:49:56 +01:00
$simple = $set -> map (
2019-09-06 06:30:01 +02:00
static function ( Carbon $date ) {
2018-02-06 07:49:56 +01:00
return $date -> format ( 'Y-m-d' );
}
);
return $simple -> toArray ();
}
2018-03-05 19:35:58 +01:00
}