mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Fix some edge cases in recurrences.
This commit is contained in:
parent
a0b46d9d8a
commit
d5ee87ddee
@ -93,7 +93,7 @@ class UpdateRequest extends FormRequest
|
|||||||
}
|
}
|
||||||
/** @var array $transaction */
|
/** @var array $transaction */
|
||||||
foreach ($transactions as $transaction) {
|
foreach ($transactions as $transaction) {
|
||||||
$return[] = $this->getSingleRecurrenceData($transaction);
|
$return[] = $this->getSingleTransactionData($transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
|
@ -194,7 +194,7 @@ class UserController extends Controller
|
|||||||
Log::debug('Actually here');
|
Log::debug('Actually here');
|
||||||
$data = $request->getUserData();
|
$data = $request->getUserData();
|
||||||
|
|
||||||
var_dump($data);
|
//var_dump($data);
|
||||||
|
|
||||||
// update password
|
// update password
|
||||||
if (array_key_exists('password', $data) && '' !== $data['password']) {
|
if (array_key_exists('password', $data) && '' !== $data['password']) {
|
||||||
|
@ -76,6 +76,7 @@ use Illuminate\Support\Collection;
|
|||||||
* @mixin Eloquent
|
* @mixin Eloquent
|
||||||
* @property int|null $transaction_type_id
|
* @property int|null $transaction_type_id
|
||||||
* @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionTypeId($value)
|
* @method static \Illuminate\Database\Eloquent\Builder|RecurrenceTransaction whereTransactionTypeId($value)
|
||||||
|
* @property-read \FireflyIII\Models\TransactionType|null $transactionType
|
||||||
*/
|
*/
|
||||||
class RecurrenceTransaction extends Model
|
class RecurrenceTransaction extends Model
|
||||||
{
|
{
|
||||||
@ -154,4 +155,12 @@ class RecurrenceTransaction extends Model
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(TransactionCurrency::class);
|
return $this->belongsTo(TransactionCurrency::class);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @codeCoverageIgnore
|
||||||
|
* @return BelongsTo
|
||||||
|
*/
|
||||||
|
public function transactionType(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(TransactionType::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,9 @@ trait RecurringTransactionTrait
|
|||||||
*/
|
*/
|
||||||
protected function createTransactions(Recurrence $recurrence, array $transactions): void
|
protected function createTransactions(Recurrence $recurrence, array $transactions): void
|
||||||
{
|
{
|
||||||
foreach ($transactions as $array) {
|
Log::debug('Now in createTransactions()');
|
||||||
|
foreach ($transactions as $index => $array) {
|
||||||
|
Log::debug(sprintf('Now at transaction #%d', $index));
|
||||||
$sourceTypes = config(sprintf('firefly.expected_source_types.source.%s', $recurrence->transactionType->type));
|
$sourceTypes = config(sprintf('firefly.expected_source_types.source.%s', $recurrence->transactionType->type));
|
||||||
$destTypes = config(sprintf('firefly.expected_source_types.destination.%s', $recurrence->transactionType->type));
|
$destTypes = config(sprintf('firefly.expected_source_types.destination.%s', $recurrence->transactionType->type));
|
||||||
$source = $this->findAccount($sourceTypes, $array['source_id'], null);
|
$source = $this->findAccount($sourceTypes, $array['source_id'], null);
|
||||||
@ -125,11 +127,17 @@ trait RecurringTransactionTrait
|
|||||||
$currency = app('amount')->getDefaultCurrencyByUser($recurrence->user);
|
$currency = app('amount')->getDefaultCurrencyByUser($recurrence->user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log::debug(
|
||||||
|
sprintf('Will set the validator type to %s based on the type of the recurrence (#%d).', $recurrence->transactionType->type, $recurrence->id)
|
||||||
|
);
|
||||||
|
|
||||||
// once the accounts have been determined, we still verify their validity:
|
// once the accounts have been determined, we still verify their validity:
|
||||||
/** @var AccountValidator $validator */
|
/** @var AccountValidator $validator */
|
||||||
$validator = app(AccountValidator::class);
|
$validator = app(AccountValidator::class);
|
||||||
$validator->setUser($recurrence->user);
|
$validator->setUser($recurrence->user);
|
||||||
$validator->setTransactionType($recurrence->transactionType->type);
|
$validator->setTransactionType($recurrence->transactionType->type);
|
||||||
|
|
||||||
|
|
||||||
if (!$validator->validateSource($source->id, null, null)) {
|
if (!$validator->validateSource($source->id, null, null)) {
|
||||||
throw new FireflyException(sprintf('Source invalid: %s', $validator->sourceError)); // @codeCoverageIgnore
|
throw new FireflyException(sprintf('Source invalid: %s', $validator->sourceError)); // @codeCoverageIgnore
|
||||||
}
|
}
|
||||||
@ -335,6 +343,7 @@ trait RecurringTransactionTrait
|
|||||||
*/
|
*/
|
||||||
protected function deleteTransactions(Recurrence $recurrence): void
|
protected function deleteTransactions(Recurrence $recurrence): void
|
||||||
{
|
{
|
||||||
|
Log::debug('deleteTransactions()');
|
||||||
/** @var RecurrenceTransaction $transaction */
|
/** @var RecurrenceTransaction $transaction */
|
||||||
foreach ($recurrence->recurrenceTransactions as $transaction) {
|
foreach ($recurrence->recurrenceTransactions as $transaction) {
|
||||||
$transaction->recurrenceTransactionMeta()->delete();
|
$transaction->recurrenceTransactionMeta()->delete();
|
||||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Services\Internal\Update;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Factory\TransactionCurrencyFactory;
|
||||||
use FireflyIII\Models\Note;
|
use FireflyIII\Models\Note;
|
||||||
use FireflyIII\Models\Recurrence;
|
use FireflyIII\Models\Recurrence;
|
||||||
use FireflyIII\Models\RecurrenceRepetition;
|
use FireflyIII\Models\RecurrenceRepetition;
|
||||||
@ -155,7 +156,7 @@ class RecurrenceUpdateService
|
|||||||
}
|
}
|
||||||
// user added or removed repetitions, delete all and recreate:
|
// user added or removed repetitions, delete all and recreate:
|
||||||
if ($originalCount !== count($repetitions)) {
|
if ($originalCount !== count($repetitions)) {
|
||||||
Log::debug('Del + recreate');
|
Log::debug('Delete existing repetitions and create new ones.');
|
||||||
$this->deleteRepetitions($recurrence);
|
$this->deleteRepetitions($recurrence);
|
||||||
$this->createRepetitions($recurrence, $repetitions);
|
$this->createRepetitions($recurrence, $repetitions);
|
||||||
|
|
||||||
@ -232,12 +233,15 @@ class RecurrenceUpdateService
|
|||||||
}
|
}
|
||||||
// user added or removed repetitions, delete all and recreate:
|
// user added or removed repetitions, delete all and recreate:
|
||||||
if ($originalCount !== count($transactions)) {
|
if ($originalCount !== count($transactions)) {
|
||||||
Log::debug('Del + recreate');
|
Log::debug('Delete existing transactions and create new ones.');
|
||||||
$this->deleteTransactions($recurrence);
|
$this->deleteTransactions($recurrence);
|
||||||
$this->createTransactions($recurrence, $transactions);
|
$this->createTransactions($recurrence, $transactions);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
$currencyFactory = app(TransactionCurrencyFactory::class);
|
||||||
|
|
||||||
|
|
||||||
// loop all and try to match them:
|
// loop all and try to match them:
|
||||||
if ($originalCount === count($transactions)) {
|
if ($originalCount === count($transactions)) {
|
||||||
Log::debug('Loop and find');
|
Log::debug('Loop and find');
|
||||||
@ -246,8 +250,26 @@ class RecurrenceUpdateService
|
|||||||
if (null === $match) {
|
if (null === $match) {
|
||||||
throw new FireflyException('Cannot match recurring transaction to existing transaction. Not sure what to do. Break.');
|
throw new FireflyException('Cannot match recurring transaction to existing transaction. Not sure what to do. Break.');
|
||||||
}
|
}
|
||||||
// TODO find currency
|
$currency = null;
|
||||||
// TODO find foreign currency
|
$foreignCurrency = null;
|
||||||
|
if (array_key_exists('currency_id', $current) || array_key_exists('currency_code', $current)) {
|
||||||
|
$currency = $currencyFactory->find($current['currency_id'] ?? null, $currency['currency_code'] ?? null);
|
||||||
|
}
|
||||||
|
if (null === $currency) {
|
||||||
|
unset($current['currency_id'], $currency['currency_code']);
|
||||||
|
}
|
||||||
|
if (null !== $currency) {
|
||||||
|
$current['currency_id'] = (int)$currency->id;
|
||||||
|
}
|
||||||
|
if (array_key_exists('foreign_currency_id', $current) || array_key_exists('foreign_currency_code', $current)) {
|
||||||
|
$foreignCurrency = $currencyFactory->find($current['foreign_currency_id'] ?? null, $currency['foreign_currency_code'] ?? null);
|
||||||
|
}
|
||||||
|
if (null === $foreignCurrency) {
|
||||||
|
unset($current['foreign_currency_id'], $currency['foreign_currency_code']);
|
||||||
|
}
|
||||||
|
if (null !== $foreignCurrency) {
|
||||||
|
$current['foreign_currency_id'] = (int)$foreignCurrency->id;
|
||||||
|
}
|
||||||
|
|
||||||
// update fields
|
// update fields
|
||||||
$fields = [
|
$fields = [
|
||||||
@ -256,6 +278,8 @@ class RecurrenceUpdateService
|
|||||||
'amount' => 'amount',
|
'amount' => 'amount',
|
||||||
'foreign_amount' => 'foreign_amount',
|
'foreign_amount' => 'foreign_amount',
|
||||||
'description' => 'description',
|
'description' => 'description',
|
||||||
|
'currency_id' => 'transaction_currency_id',
|
||||||
|
'foreign_currency_id' => 'foreign_currency_id',
|
||||||
];
|
];
|
||||||
foreach ($fields as $field => $column) {
|
foreach ($fields as $field => $column) {
|
||||||
if (array_key_exists($field, $current)) {
|
if (array_key_exists($field, $current)) {
|
||||||
|
@ -24,6 +24,8 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Validation;
|
namespace FireflyIII\Validation;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Models\Recurrence;
|
||||||
|
use FireflyIII\Models\RecurrenceTransaction;
|
||||||
use Illuminate\Validation\Validator;
|
use Illuminate\Validation\Validator;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Log;
|
use Log;
|
||||||
@ -36,7 +38,8 @@ use Log;
|
|||||||
*/
|
*/
|
||||||
trait RecurrenceValidation
|
trait RecurrenceValidation
|
||||||
{
|
{
|
||||||
public function validateRecurringConfig(Validator $validator) {
|
public function validateRecurringConfig(Validator $validator)
|
||||||
|
{
|
||||||
$data = $validator->getData();
|
$data = $validator->getData();
|
||||||
$reps = array_key_exists('nr_of_repetitions', $data) ? (int)$data['nr_of_repetitions'] : null;
|
$reps = array_key_exists('nr_of_repetitions', $data) ? (int)$data['nr_of_repetitions'] : null;
|
||||||
$repeatUntil = array_key_exists('repeat_until', $data) ? new Carbon($data['repeat_until']) : null;
|
$repeatUntil = array_key_exists('repeat_until', $data) ? new Carbon($data['repeat_until']) : null;
|
||||||
@ -44,11 +47,13 @@ trait RecurrenceValidation
|
|||||||
if (null === $reps && null === $repeatUntil) {
|
if (null === $reps && null === $repeatUntil) {
|
||||||
$validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until'));
|
$validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until'));
|
||||||
$validator->errors()->add('repeat_until', trans('validation.require_repeat_until'));
|
$validator->errors()->add('repeat_until', trans('validation.require_repeat_until'));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($reps > 0 && null !== $repeatUntil) {
|
if ($reps > 0 && null !== $repeatUntil) {
|
||||||
$validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until'));
|
$validator->errors()->add('nr_of_repetitions', trans('validation.require_repeat_until'));
|
||||||
$validator->errors()->add('repeat_until', trans('validation.require_repeat_until'));
|
$validator->errors()->add('repeat_until', trans('validation.require_repeat_until'));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,6 +70,28 @@ trait RecurrenceValidation
|
|||||||
$data = $validator->getData();
|
$data = $validator->getData();
|
||||||
|
|
||||||
$transactionType = $data['type'] ?? 'invalid';
|
$transactionType = $data['type'] ?? 'invalid';
|
||||||
|
|
||||||
|
// grab model from parameter and try to set the transaction type from it
|
||||||
|
if ('invalid' === $transactionType) {
|
||||||
|
Log::debug('Type is invalid but we will search for it.');
|
||||||
|
/** @var Recurrence $recurrence */
|
||||||
|
$recurrence = $this->route()->parameter('recurrence');
|
||||||
|
if (null !== $recurrence) {
|
||||||
|
Log::debug('There is a recurrence in the route.');
|
||||||
|
// ok so we have a recurrence should be able to extract type somehow.
|
||||||
|
/** @var RecurrenceTransaction $first */
|
||||||
|
$first = $recurrence->recurrenceTransactions()->first();
|
||||||
|
if (null !== $first) {
|
||||||
|
$transactionType = $first->transactionType ? $first->transactionType->type : 'withdrawal';
|
||||||
|
Log::debug(sprintf('Determined type to be %s.', $transactionType));
|
||||||
|
}
|
||||||
|
if(null === $first) {
|
||||||
|
Log::warning('Just going to assume type is a withdrawal.');
|
||||||
|
$transactionType = 'withdrawal';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$transactions = $data['transactions'] ?? [];
|
$transactions = $data['transactions'] ?? [];
|
||||||
|
|
||||||
/** @var AccountValidator $accountValidator */
|
/** @var AccountValidator $accountValidator */
|
||||||
|
@ -212,10 +212,10 @@ class UpdateControllerTest extends TestCase
|
|||||||
// now loop fields, enough to create sets I guess?
|
// now loop fields, enough to create sets I guess?
|
||||||
// TODO maybe do some permutation stuff here?
|
// TODO maybe do some permutation stuff here?
|
||||||
$extraTransaction = [
|
$extraTransaction = [
|
||||||
'currency_id' => $faker->numberBetween(1, 4),
|
'currency_id' => (string)$faker->numberBetween(1, 4),
|
||||||
'foreign_currency_id' => $faker->numberBetween(4, 6),
|
'foreign_currency_id' => (string)$faker->numberBetween(4, 6),
|
||||||
'source_id' => $faker->numberBetween(1, 3),
|
'source_id' => $faker->numberBetween(1, 3),
|
||||||
'destination_id' => $faker->numberBetween(8),
|
'destination_id' => $faker->numberBetween(8, 8),
|
||||||
'amount' => number_format($faker->randomFloat(2, 10, 100), 2),
|
'amount' => number_format($faker->randomFloat(2, 10, 100), 2),
|
||||||
'foreign_amount' => number_format($faker->randomFloat(2, 10, 100), 2),
|
'foreign_amount' => number_format($faker->randomFloat(2, 10, 100), 2),
|
||||||
'description' => $faker->uuid,
|
'description' => $faker->uuid,
|
||||||
|
@ -196,7 +196,7 @@ trait TestHelpers
|
|||||||
if (array_key_exists($rKey, $submissionArray)) {
|
if (array_key_exists($rKey, $submissionArray)) {
|
||||||
// comparison must be on array:
|
// comparison must be on array:
|
||||||
if (is_array($submissionArray[$rKey]) && is_array($rValue)) {
|
if (is_array($submissionArray[$rKey]) && is_array($rValue)) {
|
||||||
$this->compareArray($originalAttributes, $rKey, $submissionArray[$rKey], $rValue);
|
$this->compareArray($submissionArray, $rKey, $submissionArray[$rKey], $rValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array($submissionArray[$rKey]) && !is_array($rValue)) {
|
if (!is_array($submissionArray[$rKey]) && !is_array($rValue)) {
|
||||||
@ -261,7 +261,7 @@ trait TestHelpers
|
|||||||
$responseBody = $response->content();
|
$responseBody = $response->content();
|
||||||
$responseJson = json_decode($responseBody, true);
|
$responseJson = json_decode($responseBody, true);
|
||||||
$status = $response->getStatusCode();
|
$status = $response->getStatusCode();
|
||||||
$this->assertEquals($status, 200, sprintf("Submission: %s\nResponse: %s", json_encode($submission), $responseBody));
|
$this->assertEquals($status, 200, sprintf("Submission:\n%s\nResponse: %s", json_encode($submission), $responseBody));
|
||||||
|
|
||||||
$response->assertHeader('Content-Type', 'application/vnd.api+json');
|
$response->assertHeader('Content-Type', 'application/vnd.api+json');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user