James Cole
2025-01-19 07:18:07 +01:00
parent 4b2807de48
commit 950c60d55c

View File

@@ -25,6 +25,8 @@ declare(strict_types=1);
namespace FireflyIII\Support\Export; namespace FireflyIII\Support\Export;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -50,6 +52,8 @@ use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\ConvertsDataTypes;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@@ -82,8 +86,8 @@ class ExportDataGenerator
public function __construct() public function __construct()
{ {
$this->accounts = new Collection(); $this->accounts = new Collection();
$this->start = today(config('app.timezone')); $this->start = today(config('app.timezone'));
$this->start->subYear(); $this->start->subYear();
$this->end = today(config('app.timezone')); $this->end = today(config('app.timezone'));
$this->exportTransactions = false; $this->exportTransactions = false;
@@ -141,7 +145,7 @@ class ExportDataGenerator
*/ */
private function exportAccounts(): string private function exportAccounts(): string
{ {
$header = [ $header = [
'user_id', 'user_id',
'account_id', 'account_id',
'created_at', 'created_at',
@@ -162,7 +166,7 @@ class ExportDataGenerator
]; ];
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$allAccounts = $repository->getAccountsByType([]); $allAccounts = $repository->getAccountsByType([]);
$records = []; $records = [];
@@ -192,7 +196,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -230,8 +234,8 @@ class ExportDataGenerator
/** @var BillRepositoryInterface $repository */ /** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class); $repository = app(BillRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$bills = $repository->getBills(); $bills = $repository->getBills();
$header = [ $header = [
'user_id', 'user_id',
'bill_id', 'bill_id',
'created_at', 'created_at',
@@ -245,7 +249,7 @@ class ExportDataGenerator
'skip', 'skip',
'active', 'active',
]; ];
$records = []; $records = [];
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
@@ -266,7 +270,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -296,7 +300,7 @@ class ExportDataGenerator
*/ */
private function exportBudgets(): string private function exportBudgets(): string
{ {
$header = [ $header = [
'user_id', 'user_id',
'budget_id', 'budget_id',
'name', 'name',
@@ -310,9 +314,9 @@ class ExportDataGenerator
$budgetRepos = app(BudgetRepositoryInterface::class); $budgetRepos = app(BudgetRepositoryInterface::class);
$budgetRepos->setUser($this->user); $budgetRepos->setUser($this->user);
$limitRepos = app(BudgetLimitRepositoryInterface::class); $limitRepos = app(BudgetLimitRepositoryInterface::class);
$budgets = $budgetRepos->getBudgets(); $budgets = $budgetRepos->getBudgets();
$records = []; $records = [];
/** @var Budget $budget */ /** @var Budget $budget */
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
@@ -335,7 +339,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -365,10 +369,10 @@ class ExportDataGenerator
*/ */
private function exportCategories(): string private function exportCategories(): string
{ {
$header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name']; $header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name'];
/** @var CategoryRepositoryInterface $catRepos */ /** @var CategoryRepositoryInterface $catRepos */
$catRepos = app(CategoryRepositoryInterface::class); $catRepos = app(CategoryRepositoryInterface::class);
$catRepos->setUser($this->user); $catRepos->setUser($this->user);
$records = []; $records = [];
@@ -386,7 +390,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -417,14 +421,14 @@ class ExportDataGenerator
private function exportPiggies(): string private function exportPiggies(): string
{ {
/** @var PiggyBankRepositoryInterface $piggyRepos */ /** @var PiggyBankRepositoryInterface $piggyRepos */
$piggyRepos = app(PiggyBankRepositoryInterface::class); $piggyRepos = app(PiggyBankRepositoryInterface::class);
$piggyRepos->setUser($this->user); $piggyRepos->setUser($this->user);
/** @var AccountRepositoryInterface $accountRepos */ /** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class); $accountRepos = app(AccountRepositoryInterface::class);
$accountRepos->setUser($this->user); $accountRepos->setUser($this->user);
$header = [ $header = [
'user_id', 'user_id',
'piggy_bank_id', 'piggy_bank_id',
'created_at', 'created_at',
@@ -440,8 +444,8 @@ class ExportDataGenerator
'order', 'order',
'active', 'active',
]; ];
$records = []; $records = [];
$piggies = $piggyRepos->getPiggyBanks(); $piggies = $piggyRepos->getPiggyBanks();
/** @var PiggyBank $piggy */ /** @var PiggyBank $piggy */
foreach ($piggies as $piggy) { foreach ($piggies as $piggy) {
@@ -466,7 +470,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -499,7 +503,7 @@ class ExportDataGenerator
/** @var RecurringRepositoryInterface $recurringRepos */ /** @var RecurringRepositoryInterface $recurringRepos */
$recurringRepos = app(RecurringRepositoryInterface::class); $recurringRepos = app(RecurringRepositoryInterface::class);
$recurringRepos->setUser($this->user); $recurringRepos->setUser($this->user);
$header = [ $header = [
// recurrence: // recurrence:
'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active', 'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active',
@@ -508,8 +512,8 @@ class ExportDataGenerator
// transactions + meta: // transactions + meta:
'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags', 'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags',
]; ];
$records = []; $records = [];
$recurrences = $recurringRepos->getAll(); $recurrences = $recurringRepos->getAll();
/** @var Recurrence $recurrence */ /** @var Recurrence $recurrence */
foreach ($recurrences as $recurrence) { foreach ($recurrences as $recurrence) {
@@ -542,7 +546,7 @@ class ExportDataGenerator
$piggyBankId = $recurringRepos->getPiggyBank($transaction); $piggyBankId = $recurringRepos->getPiggyBank($transaction);
$tags = $recurringRepos->getTags($transaction); $tags = $recurringRepos->getTags($transaction);
$records[] = [ $records[] = [
// recurrence // recurrence
$this->user->id, $this->user->id,
$recurrence->id, $recurrence->id,
@@ -558,7 +562,7 @@ class ExportDataGenerator
} }
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -595,8 +599,8 @@ class ExportDataGenerator
'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing']; 'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing'];
$ruleRepos = app(RuleRepositoryInterface::class); $ruleRepos = app(RuleRepositoryInterface::class);
$ruleRepos->setUser($this->user); $ruleRepos->setUser($this->user);
$rules = $ruleRepos->getAll(); $rules = $ruleRepos->getAll();
$records = []; $records = [];
/** @var Rule $rule */ /** @var Rule $rule */
foreach ($rules as $rule) { foreach ($rules as $rule) {
@@ -635,7 +639,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -665,12 +669,12 @@ class ExportDataGenerator
*/ */
private function exportTags(): string private function exportTags(): string
{ {
$header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level']; $header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level'];
$tagRepos = app(TagRepositoryInterface::class); $tagRepos = app(TagRepositoryInterface::class);
$tagRepos->setUser($this->user); $tagRepos->setUser($this->user);
$tags = $tagRepos->get(); $tags = $tagRepos->get();
$records = []; $records = [];
/** @var Tag $tag */ /** @var Tag $tag */
foreach ($tags as $tag) { foreach ($tags as $tag) {
@@ -689,7 +693,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -728,33 +732,57 @@ class ExportDataGenerator
private function exportTransactions(): string private function exportTransactions(): string
{ {
// TODO better place for keys? // TODO better place for keys?
$header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'amount', 'foreign_amount', 'currency_code', 'foreign_currency_code', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes']; $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'currency_code', 'amount', 'foreign_currency_code', 'foreign_amount', 'native_currency_code', 'native_amount', 'native_foreign_amount', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes'];
$metaFields = config('firefly.journal_meta_fields'); $metaFields = config('firefly.journal_meta_fields');
$header = array_merge($header, $metaFields); $header = array_merge($header, $metaFields);
$default = Amount::getDefaultCurrency();
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user); $collector->setUser($this->user);
$collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation() $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation()->withBudgetInformation()->withTagInformation()->withNotes();
->withBudgetInformation()->withTagInformation()->withNotes()
;
if (0 !== $this->accounts->count()) { if (0 !== $this->accounts->count()) {
$collector->setAccounts($this->accounts); $collector->setAccounts($this->accounts);
} }
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
// get repository for meta data: // get repository for meta data:
$repository = app(TransactionGroupRepositoryInterface::class); $repository = app(TransactionGroupRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$records = []; $records = [];
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$metaData = $repository->getMetaFields($journal['transaction_journal_id'], $metaFields); $metaData = $repository->getMetaFields($journal['transaction_journal_id'], $metaFields);
$amount = Steam::bcround(Steam::negative($journal['amount']), $journal['currency_decimal_places']);
$foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['foreign_amount']), $journal['foreign_currency_decimal_places']);
$nativeAmount = null === $journal['native_amount'] ? null : Steam::bcround(Steam::negative($journal['native_amount'], $default->decimal_places));
$nativeForeignAmount = null === $journal['native_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['native_foreign_amount'], $default->decimal_places));
if (TransactionTypeEnum::WITHDRAWAL->value !== $journal['transaction_type_type']) {
$amount = Steam::bcround(Steam::positive($journal['amount']), $journal['currency_decimal_places']);
$foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::positive($journal['foreign_amount']), $journal['foreign_currency_decimal_places']);
$nativeAmount = null === $journal['native_amount'] ? null : Steam::bcround(Steam::positive($journal['native_amount'], $default->decimal_places));
$nativeForeignAmount = null === $journal['native_foreign_amount'] ? null : Steam::bcround(Steam::positive($journal['native_foreign_amount'], $default->decimal_places));
}
// opening balance depends on source account type.
if (TransactionTypeEnum::OPENING_BALANCE->value === $journal['transaction_type_type'] && AccountTypeEnum::ASSET->value === $journal['source_account_type']) {
$amount = Steam::bcround(Steam::negative($journal['amount']), $journal['currency_decimal_places']);
$foreignAmount = null === $journal['foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['foreign_amount']), $journal['foreign_currency_decimal_places']);
$nativeAmount = null === $journal['native_amount'] ? null : Steam::bcround(Steam::negative($journal['native_amount'], $default->decimal_places));
$nativeForeignAmount = null === $journal['native_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['native_foreign_amount'], $default->decimal_places));
}
$records[] = [ $records[] = [
$journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'], $journal['amount'], $journal['foreign_amount'], $journal['currency_code'], $journal['foreign_currency_code'], $journal['description'], $journal['date']->toAtomString(), $journal['source_account_name'], $journal['source_account_iban'], $journal['source_account_type'], $journal['destination_account_name'], $journal['destination_account_iban'], $journal['destination_account_type'], $journal['reconciled'], $journal['category_name'], $journal['budget_name'], $journal['bill_name'], $journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'],
// amounts and currencies
$journal['currency_code'], $amount, $journal['foreign_currency_code'], $foreignAmount, $default->code, $nativeAmount, $nativeForeignAmount,
// more fields
$journal['description'], $journal['date']->toAtomString(), $journal['source_account_name'], $journal['source_account_iban'], $journal['source_account_type'], $journal['destination_account_name'], $journal['destination_account_iban'], $journal['destination_account_type'], $journal['reconciled'], $journal['category_name'], $journal['budget_name'], $journal['bill_name'],
$this->mergeTags($journal['tags']), $this->mergeTags($journal['tags']),
$this->clearStringKeepNewlines($journal['notes']), $this->clearStringKeepNewlines($journal['notes']),
@@ -773,7 +801,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {