diff --git a/app/Factory/TransactionFactory.php b/app/Factory/TransactionFactory.php new file mode 100644 index 0000000000..fc682825e6 --- /dev/null +++ b/app/Factory/TransactionFactory.php @@ -0,0 +1,343 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Factory; + + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\Budget; +use FireflyIII\Models\Category; +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\User; +use Illuminate\Support\Collection; + +/** + * Class TransactionFactory + */ +class TransactionFactory +{ + /** @var User */ + private $user; + + /** + * @param array $data + * + * @return Transaction + */ + public function create(array $data): Transaction + { + $foreignCurrencyId = is_null($data['foreign_currency']) ? null : $data['foreign_currency']->id; + $values = [ + 'reconciled' => $data['reconciled'], + 'account_id' => $data['account']->id, + 'transaction_journal_id' => $data['transaction_journal']->id, + 'description' => $data['description'], + 'transaction_currency_id' => $data['currency']->id, + 'amount' => $data['amount'], + 'foreign_amount' => $data['foreign_amount'], + 'foreign_currency_id' => $foreignCurrencyId, + 'identifier' => $data['identifier'], + ]; + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepositoryInterface::class); + $transaction = $repository->storeBasicTransaction($values); + + // todo: add budget, category + + + return $transaction; + } + + /** + * Create a pair of transactions based on the data given in the array. + * + * @param TransactionJournal $journal + * @param array $data + * + * @return Collection + * @throws FireflyException + */ + public function createPair(TransactionJournal $journal, array $data): Collection + { + // all this data is the same for both transactions: + $currency = $this->findCurrency($data['currency_id'], $data['currency_code']); + $foreignCurrency = $this->findCurrency($data['foreign_currency_id'], $data['foreign_currency_code']); + $budget = $this->findBudget($data['budget_id'], $data['budget_name']); + $category = $this->findCategory($data['category_id'], $data['category_name']); + + + // type of source account depends on journal type: + $sourceType = $this->accountType($journal, 'source'); + $sourceAccount = $this->findAccount($sourceType, $data['source_account_id'], $data['source_account_name']); + + // same for destination account: + $destinationType = $this->accountType($journal, 'destination'); + $destinationAccount = $this->findAccount($destinationType, $data['destination_account_id'], $data['destination_account_name']); + + // first make a "negative" (source) transaction based on the data in the array. + $sourceTransactionData = [ + 'description' => $journal->description === $data['description'] ? null : $data['description'], + 'amount' => app('steam')->negative(strval($data['amount'])), + 'foreign_amount' => is_null($data['foreign_amount']) ? null : app('steam')->negative(strval($data['foreign_amount'])), + 'currency' => $currency, + 'foreign_currency' => $foreignCurrency, + 'budget' => $budget, + 'category' => $category, + 'account' => $sourceAccount, + 'transaction_journal' => $journal, + 'reconciled' => $data['reconciled'], + 'identifier' => $data['identifier'], + ]; + $this->create($sourceTransactionData); + + + print_r($data); + print_r($sourceTransactionData); + exit; + + + // then make a "positive" transaction based on the data in the array. + + } + + /** + * @param User $user + */ + public function setUser(User $user) + { + $this->user = $user; + } + + /** + * @param TransactionJournal $journal + * @param string $direction + * + * @return string + * @throws FireflyException + */ + protected function accountType(TransactionJournal $journal, string $direction): string + { + $types = []; + $type = $journal->transactionType->type; + switch ($type) { + default: + throw new FireflyException(sprintf('Cannot handle type "%s" in accountType()', $type)); + case TransactionType::WITHDRAWAL: + $types['source'] = AccountType::ASSET; + $types['destination'] = AccountType::EXPENSE; + break; + case TransactionType::DEPOSIT: + $types['source'] = AccountType::REVENUE; + $types['destination'] = AccountType::ASSET; + break; + case TransactionType::TRANSFER: + $types['source'] = AccountType::ASSET; + $types['destination'] = AccountType::ASSET; + break; + } + if (!isset($types[$direction])) { + throw new FireflyException(sprintf('No type set for direction "%s" and type "%s"', $type, $direction)); + } + + return $types[$direction]; + } + + /** + * @param string $expectedType + * @param int|null $accountId + * @param string|null $accountName + * + * @return Account + * @throws FireflyException + */ + protected function findAccount(string $expectedType, ?int $accountId, ?string $accountName): Account + { + $accountId = intval($accountId); + $accountName = strval($accountName); + /** @var AccountRepositoryInterface $repository */ + $repository = app(AccountRepositoryInterface::class); + $repository->setUser($this->user); + + switch ($expectedType) { + case AccountType::ASSET: + if ($accountId > 0) { + // must be able to find it based on ID. Validator should catch invalid ID's. + return $repository->findNull($accountId); + } + + // alternatively, return by name. Validator should catch invalid names. + return $repository->findByName($accountName, [AccountType::ASSET]); + break; + case AccountType::EXPENSE: + if ($accountId > 0) { + // must be able to find it based on ID. Validator should catch invalid ID's. + return $repository->findNull($accountId); + } + if (strlen($accountName) > 0) { + // alternatively, return by name. Validator should catch invalid names. + return $repository->findByName($accountName, [AccountType::EXPENSE]); + } + + // return cash account: + return $repository->getCashAccount(); + break; + case AccountType::REVENUE: + if ($accountId > 0) { + // must be able to find it based on ID. Validator should catch invalid ID's. + return $repository->findNull($accountId); + } + if (strlen($accountName) > 0) { + // alternatively, return by name. Validator should catch invalid names. + return $repository->findByName($accountName, [AccountType::REVENUE]); + } + + // return cash account: + return $repository->getCashAccount(); + + default: + throw new FireflyException(sprintf('Cannot find account of type "%s".', $expectedType)); + + } + } + + /** + * @param int|null $budgetId + * @param null|string $budgetName + * + * @return Budget|null + */ + protected function findBudget(?int $budgetId, ?string $budgetName): ?Budget + { + $budgetId = intval($budgetId); + $budgetName = strval($budgetName); + if (strlen($budgetName) === 0 && $budgetId === 0) { + return null; + } + /** @var BudgetRepositoryInterface $repository */ + $repository = app(BudgetRepositoryInterface::class); + $budget = null; + $repository->setUser($this->user); + + // first by ID: + if ($budgetId > 0) { + $budget = $repository->findNull($budgetId); + if (!is_null($budget)) { + return $budget; + } + } + + if (strlen($budgetName) > 0) { + $budget = $repository->findByName($budgetName); + if (!is_null($budget)) { + return $budget; + } + } + + return null; + } + + /** + * @param int|null $categoryId + * @param null|string $categoryName + * + * @return Category|null + */ + protected function findCategory(?int $categoryId, ?string $categoryName): ?Category + { + $categoryId = intval($categoryId); + $categoryName = strval($categoryName); + + if (strlen($categoryName) === 0 && $categoryId === 0) { + return null; + } + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $category = null; + $repository->setUser($this->user); + + // first by ID: + if ($categoryId > 0) { + $category = $repository->findNull($categoryId); + if (!is_null($category)) { + return $category; + } + } + + if (strlen($categoryName) > 0) { + $category = $repository->findByName($categoryName); + if (!is_null($category)) { + return $category; + } + // create it? + } + + return null; + } + + /** + * @param int|null $currencyId + * @param null|string $currencyCode + * + * @return TransactionCurrency|null + */ + protected function findCurrency(?int $currencyId, ?string $currencyCode): ?TransactionCurrency + { + $currencyCode = strval($currencyCode); + $currencyId = intval($currencyId); + + if (strlen($currencyCode) === 0 && intval($currencyId) === 0) { + return null; + } + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class); + $currency = null; + $repository->setUser($this->user); + + // first by ID: + if ($currencyId > 0) { + $currency = $repository->findNull($currencyId); + if (!is_null($currency)) { + return $currency; + } + } + // then by code: + if (strlen($currencyCode) > 0) { + $currency = $repository->findByCodeNull($currencyCode); + if (!is_null($currency)) { + return $currency; + } + } + + return null; + } +} \ No newline at end of file diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php new file mode 100644 index 0000000000..3dd67257e6 --- /dev/null +++ b/app/Factory/TransactionJournalFactory.php @@ -0,0 +1,170 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Factory; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Bill; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\Journal\JournalRepositoryInterface; +use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface; +use FireflyIII\User; + +/** + * Class TransactionJournalFactory + */ +class TransactionJournalFactory +{ + /** @var User */ + private $user; + + /** + * TransactionJournalFactory constructor. + */ + public function __construct() + { + + } + + /** + * Create a new transaction journal and associated transactions. + * + * @param array $data + * + * @return TransactionJournal + * @throws FireflyException + */ + public function create(array $data): TransactionJournal + { + $type = $this->findTransactionType($data['type']); + $bill = $this->findBill($data['bill_id'], $data['bill_name']); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + /** @var JournalRepositoryInterface $repository */ + $repository = app(JournalRepositoryInterface::class); + + $values = [ + 'user_id' => $data['user'], + 'transaction_type_id' => $type->id, + 'bill_id' => is_null($bill) ? null : $bill->id, + 'transaction_currency_id' => $defaultCurrency->id, + 'description' => $data['description'], + 'date' => $data['date']->format('Y-m-d'), + 'order' => 0, + 'tag_count' => 0, + 'completed' => 0, + ]; + + $journal = $repository->storeBasic($values); + + // todo link other stuff to journal (meta-data etc). + + // start creating transactions: + /** @var array $trData */ + foreach ($data['transactions'] as $trData) { + $factory = new TransactionFactory(); + $factory->setUser($this->user); + + $trData['reconciled'] = $data['reconciled'] ?? false; + $transactions = $factory->createPair($journal, $trData); + } + + echo 'id is ', $journal->id; + exit; + + + //$currency = $this->findCurrency($data['currency_id'], $data['currency_code']); + + return $journal; + } + + /** + * Set the user. + * + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + + /** + * Find the given bill based on the ID or the name. ID takes precedence over the name. + * + * @param int $billId + * @param string $billName + * + * @return Bill|null + */ + protected function findBill(int $billId, string $billName): ?Bill + { + if (strlen($billName) === 0 && $billId === 0) { + return null; + } + /** @var BillRepositoryInterface $repository */ + $repository = app(BillRepositoryInterface::class); + $repository->setUser($this->user); + + // first find by ID: + if ($billId > 0) { + /** @var Bill $bill */ + $bill = $repository->find($billId); + if (!is_null($bill)) { + return $bill; + } + } + + // then find by name: + if (strlen($billName) > 0) { + $bill = $repository->findByName($billName); + if (!is_null($bill)) { + return $bill; + } + } + + return null; + } + + /** + * Get the transaction type. Since this is mandatory, will throw an exception when nothing comes up. Will always + * use TransactionType repository. + * + * @param string $type + * + * @return TransactionType + * @throws FireflyException + */ + private function findTransactionType(string $type): TransactionType + { + /** @var TransactionTypeRepositoryInterface $repository */ + $repository = app(TransactionTypeRepositoryInterface::class); + $transactionType = $repository->findByType($type); + if (is_null($transactionType)) { + throw new FireflyException(sprintf('Could not find transaction type for "%s"', $type)); + } + + return $transactionType; + } + +} \ No newline at end of file