mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-12 15:35:15 +00:00
Complete API for recurring transactions.
This commit is contained in:
@@ -155,14 +155,25 @@ class RecurrenceController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param string $object
|
||||
* @param RecurrenceRequest $request
|
||||
* @param Recurrence $recurrence
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(Request $request, string $object): JsonResponse
|
||||
public function update(RecurrenceRequest $request, Recurrence $recurrence): JsonResponse
|
||||
{
|
||||
// todo replace code and replace request object.
|
||||
$data = $request->getAll();
|
||||
|
||||
//
|
||||
|
||||
$category = $this->repository->update($recurrence, $data);
|
||||
$manager = new Manager();
|
||||
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
|
||||
$manager->setSerializer(new JsonApiSerializer($baseUrl));
|
||||
|
||||
$resource = new Item($category, new RecurrenceTransformer($this->parameters), 'recurrences');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
|
||||
}
|
||||
}
|
@@ -124,7 +124,7 @@ class RecurrenceRequest extends Request
|
||||
|
||||
return [
|
||||
'type' => 'required|in:withdrawal,transfer,deposit',
|
||||
'title' => 'required|between:1,255',
|
||||
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
|
||||
'description' => 'between:1,65000',
|
||||
'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')),
|
||||
'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')),
|
||||
@@ -139,8 +139,8 @@ class RecurrenceRequest extends Request
|
||||
// rules for repetitions.
|
||||
'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly',
|
||||
'repetitions.*.moment' => 'between:0,10',
|
||||
'repetitions.*.skip' => 'required|between:0,31',
|
||||
'repetitions.*.weekend' => 'required|between:1,4',
|
||||
'repetitions.*.skip' => 'required|numeric|between:0,31',
|
||||
'repetitions.*.weekend' => 'required|numeric|min:1|max:4',
|
||||
|
||||
// rules for transactions.
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code',
|
||||
|
@@ -72,7 +72,7 @@ class CategoryFactory
|
||||
|
||||
Log::debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName));
|
||||
|
||||
if (\strlen($categoryName) === 0 && $categoryId === 0) {
|
||||
if ('' === $categoryName && $categoryId === 0) {
|
||||
return null;
|
||||
}
|
||||
// first by ID:
|
||||
|
@@ -66,21 +66,26 @@ class RecurrenceFormRequest extends Request
|
||||
],
|
||||
'transactions' => [
|
||||
[
|
||||
'transaction_currency_id' => $this->integer('transaction_currency_id'),
|
||||
'type' => $this->string('transaction_type'),
|
||||
'description' => $this->string('transaction_description'),
|
||||
'amount' => $this->string('amount'),
|
||||
'foreign_amount' => null,
|
||||
'foreign_currency_id' => null,
|
||||
'budget_id' => $this->integer('budget_id'),
|
||||
'category_name' => $this->string('category'),
|
||||
'currency_id' => $this->integer('transaction_currency_id'),
|
||||
'currency_code' => null,
|
||||
'type' => $this->string('transaction_type'),
|
||||
'description' => $this->string('transaction_description'),
|
||||
'amount' => $this->string('amount'),
|
||||
'foreign_amount' => null,
|
||||
'foreign_currency_id' => null,
|
||||
'foreign_currency_code' => null,
|
||||
'budget_id' => $this->integer('budget_id'),
|
||||
'budget_name' => null,
|
||||
'category_id' => null,
|
||||
'category_name' => $this->string('category'),
|
||||
|
||||
],
|
||||
],
|
||||
'meta' => [
|
||||
// tags and piggy bank ID.
|
||||
'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')) : [],
|
||||
'piggy_bank_id' => $this->integer('piggy_bank_id'),
|
||||
'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')) : [],
|
||||
'piggy_bank_id' => $this->integer('piggy_bank_id'),
|
||||
'piggy_bank_name' => null,
|
||||
],
|
||||
'repetitions' => [
|
||||
[
|
||||
@@ -98,7 +103,11 @@ class RecurrenceFormRequest extends Request
|
||||
$return['transactions'][0]['foreign_amount'] = $this->string('foreign_amount');
|
||||
$return['transactions'][0]['foreign_currency_id'] = $this->integer('foreign_currency_id');
|
||||
}
|
||||
|
||||
// default values:
|
||||
$return['transactions'][0]['source_id'] = null;
|
||||
$return['transactions'][0]['source_name'] = null;
|
||||
$return['transactions'][0]['destination_id'] = null;
|
||||
$return['transactions'][0]['destination_name'] = null;
|
||||
// fill in source and destination account data
|
||||
switch ($this->string('transaction_type')) {
|
||||
default:
|
||||
|
@@ -73,7 +73,7 @@ class RecurrenceTransaction extends Model
|
||||
*/
|
||||
public function destinationAccount(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
return $this->belongsTo(Account::class,'destination_id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +109,7 @@ class RecurrenceTransaction extends Model
|
||||
*/
|
||||
public function sourceAccount(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
return $this->belongsTo(Account::class,'source_id');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -32,6 +32,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Preferences;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
@@ -42,6 +43,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
* @property User $user
|
||||
* @property int $bill_id
|
||||
* @property Collection $categories
|
||||
* @property bool $completed
|
||||
*/
|
||||
class TransactionJournal extends Model
|
||||
{
|
||||
|
@@ -24,7 +24,10 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Services\Internal\Support;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\BudgetFactory;
|
||||
use FireflyIII\Factory\CategoryFactory;
|
||||
use FireflyIII\Factory\PiggyBankFactory;
|
||||
use FireflyIII\Factory\TransactionCurrencyFactory;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\RecurrenceMeta;
|
||||
@@ -69,32 +72,39 @@ trait RecurringTransactionTrait
|
||||
*/
|
||||
public function createTransactions(Recurrence $recurrence, array $transactions): void
|
||||
{
|
||||
|
||||
foreach ($transactions as $array) {
|
||||
$source = null;
|
||||
$destination = null;
|
||||
switch ($recurrence->transactionType->type) {
|
||||
case TransactionType::WITHDRAWAL:
|
||||
$source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name'] ?? null);
|
||||
$destination = $this->findAccount(AccountType::EXPENSE, $array['destination_id'] ?? null, $array['destination_name']);
|
||||
$source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name']);
|
||||
$destination = $this->findAccount(AccountType::EXPENSE, $array['destination_id'], $array['destination_name']);
|
||||
break;
|
||||
case TransactionType::DEPOSIT:
|
||||
$source = $this->findAccount(AccountType::REVENUE, $array['source_id'] ?? null, $array['source_name']);
|
||||
$destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name'] ?? null);
|
||||
$source = $this->findAccount(AccountType::REVENUE, $array['source_id'], $array['source_name']);
|
||||
$destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name']);
|
||||
break;
|
||||
case TransactionType::TRANSFER:
|
||||
$source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name'] ?? null);
|
||||
$destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name'] ?? null);
|
||||
$source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name']);
|
||||
$destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name']);
|
||||
break;
|
||||
}
|
||||
|
||||
/** @var TransactionCurrencyFactory $factory */
|
||||
$factory = app(TransactionCurrencyFactory::class);
|
||||
$currency = $factory->find($array['currency_id'], $array['currency_code']);
|
||||
$foreignCurrency = $factory->find($array['foreign_currency_id'], $array['foreign_currency_code']);
|
||||
$defaultCurrency = app('amount')->getDefaultCurrencyByUser($recurrence->user);
|
||||
if (null === $currency) {
|
||||
$currency = $defaultCurrency;
|
||||
}
|
||||
$transaction = new RecurrenceTransaction(
|
||||
[
|
||||
'recurrence_id' => $recurrence->id,
|
||||
'transaction_currency_id' => $array['transaction_currency_id'],
|
||||
'foreign_currency_id' => '' === (string)$array['foreign_currency_id'] ? null : $array['foreign_currency_id'],
|
||||
'source_id' => $source->id,
|
||||
'destination_id' => $destination->id,
|
||||
'transaction_currency_id' => $currency->id,
|
||||
'foreign_currency_id' => null === $foreignCurrency ? null : $foreignCurrency->id,
|
||||
'source_id' => $source->id,
|
||||
'destination_id' => $destination->id,
|
||||
'amount' => $array['amount'],
|
||||
'foreign_amount' => '' === (string)$array['foreign_amount'] ? null : (string)$array['foreign_amount'],
|
||||
'description' => $array['description'],
|
||||
@@ -102,22 +112,32 @@ trait RecurringTransactionTrait
|
||||
);
|
||||
$transaction->save();
|
||||
|
||||
/** @var BudgetFactory $budgetFactory */
|
||||
$budgetFactory = app(BudgetFactory::class);
|
||||
$budgetFactory->setUser($recurrence->user);
|
||||
$budget = $budgetFactory->find($array['budget_id'], $array['budget_name']);
|
||||
|
||||
/** @var CategoryFactory $categoryFactory */
|
||||
$categoryFactory = app(CategoryFactory::class);
|
||||
$categoryFactory->setUser($recurrence->user);
|
||||
$category = $categoryFactory->findOrCreate($array['category_id'], $array['category_name']);
|
||||
|
||||
// create recurrence transaction meta:
|
||||
if ($array['budget_id'] > 0) {
|
||||
if (null !== $budget) {
|
||||
RecurrenceTransactionMeta::create(
|
||||
[
|
||||
'rt_id' => $transaction->id,
|
||||
'name' => 'budget_id',
|
||||
'value' => $array['budget_id'],
|
||||
'value' => $budget->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
if ('' !== (string)$array['category_name']) {
|
||||
if (null !== $category) {
|
||||
RecurrenceTransactionMeta::create(
|
||||
[
|
||||
'rt_id' => $transaction->id,
|
||||
'name' => 'category_name',
|
||||
'value' => $array['category_name'],
|
||||
'value' => $category->name,
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -155,20 +175,27 @@ trait RecurringTransactionTrait
|
||||
public function updateMetaData(Recurrence $recurrence, array $data): void
|
||||
{
|
||||
// only two special meta fields right now. Let's just hard code them.
|
||||
$piggyId = (int)($data['meta']['piggy_bank_id'] ?? 0.0);
|
||||
if ($piggyId > 0) {
|
||||
$piggyId = (int)($data['meta']['piggy_bank_id'] ?? 0.0);
|
||||
$piggyName = $data['meta']['piggy_bank_name'] ?? '';
|
||||
/** @var PiggyBankFactory $factory */
|
||||
$factory = app(PiggyBankFactory::class);
|
||||
$factory->setUser($recurrence->user);
|
||||
$piggyBank = $factory->find($piggyId, $piggyName);
|
||||
if (null !== $piggyBank) {
|
||||
/** @var RecurrenceMeta $entry */
|
||||
$entry = $recurrence->recurrenceMeta()->where('name', 'piggy_bank_id')->first();
|
||||
if (null === $entry) {
|
||||
$entry = RecurrenceMeta::create(['recurrence_id' => $recurrence->id, 'name' => 'piggy_bank_id', 'value' => $piggyId]);
|
||||
$entry = RecurrenceMeta::create(['recurrence_id' => $recurrence->id, 'name' => 'piggy_bank_id', 'value' => $piggyBank->id]);
|
||||
}
|
||||
$entry->value = $piggyId;
|
||||
$entry->value = $piggyBank->id;
|
||||
$entry->save();
|
||||
}
|
||||
if ($piggyId === 0) {
|
||||
if (null === $piggyBank) {
|
||||
// delete if present
|
||||
$recurrence->recurrenceMeta()->where('name', 'piggy_bank_id')->delete();
|
||||
}
|
||||
|
||||
|
||||
$tags = $data['meta']['tags'] ?? [];
|
||||
if (\count($tags) > 0) {
|
||||
/** @var RecurrenceMeta $entry */
|
||||
|
@@ -65,11 +65,13 @@ class RecurrenceUpdateService
|
||||
$recurrence->apply_rules = $data['recurrence']['apply_rules'] ?? $recurrence->apply_rules;
|
||||
$recurrence->active = $data['recurrence']['active'] ?? $recurrence->active;
|
||||
|
||||
if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'until_date'])) {
|
||||
$recurrence->repetitions = 0;
|
||||
}
|
||||
if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'times'])) {
|
||||
$recurrence->repeat_until = null;
|
||||
if(isset($data['recurrence']['repetition_end'])) {
|
||||
if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'until_date'])) {
|
||||
$recurrence->repetitions = 0;
|
||||
}
|
||||
if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'times'])) {
|
||||
$recurrence->repeat_until = null;
|
||||
}
|
||||
}
|
||||
$recurrence->save();
|
||||
|
||||
|
Reference in New Issue
Block a user