1 | <?php |
||
2 | /** |
||
3 | * ExpandedProcessor.php |
||
4 | * Copyright (c) 2018 [email protected] |
||
5 | * |
||
6 | * This file is part of Firefly III. |
||
7 | * |
||
8 | * Firefly III is free software: you can redistribute it and/or modify |
||
9 | * it under the terms of the GNU General Public License as published by |
||
10 | * the Free Software Foundation, either version 3 of the License, or |
||
11 | * (at your option) any later version. |
||
12 | * |
||
13 | * Firefly III is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License |
||
19 | * along with Firefly III. If not, see <http://www.gnu.org/licenses/>. |
||
20 | */ |
||
21 | |||
22 | /** @noinspection PhpDynamicAsStaticMethodCallInspection */ |
||
23 | |||
24 | declare(strict_types=1); |
||
25 | |||
26 | namespace FireflyIII\Export; |
||
27 | |||
28 | use DB; |
||
29 | use FireflyIII\Exceptions\FireflyException; |
||
30 | use FireflyIII\Export\Collector\AttachmentCollector; |
||
31 | use FireflyIII\Export\Collector\UploadCollector; |
||
32 | use FireflyIII\Export\Entry\Entry; |
||
33 | use FireflyIII\Export\Exporter\ExporterInterface; |
||
34 | use FireflyIII\Helpers\Collector\TransactionCollectorInterface; |
||
35 | use FireflyIII\Helpers\Filter\InternalTransferFilter; |
||
36 | use FireflyIII\Models\AccountMeta; |
||
37 | use FireflyIII\Models\ExportJob; |
||
38 | use FireflyIII\Models\Note; |
||
39 | use FireflyIII\Models\Transaction; |
||
40 | use FireflyIII\Models\TransactionJournal; |
||
41 | use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; |
||
42 | use Illuminate\Support\Collection; |
||
43 | use Illuminate\Support\Facades\Storage; |
||
44 | use Log; |
||
45 | use ZipArchive; |
||
46 | |||
47 | /** |
||
48 | * Class ExpandedProcessor. |
||
49 | * |
||
50 | * @SuppressWarnings(PHPMD.CouplingBetweenObjects) // its doing a lot. |
||
51 | * |
||
52 | * @codeCoverageIgnore |
||
53 | * @deprecated |
||
54 | */ |
||
55 | class ExpandedProcessor implements ProcessorInterface |
||
0 ignored issues
–
show
|
|||
56 | { |
||
57 | /** @var Collection All accounts */ |
||
58 | public $accounts; |
||
59 | /** @var string The export format */ |
||
60 | public $exportFormat; |
||
61 | /** @var bool Should include attachments */ |
||
62 | public $includeAttachments; |
||
63 | /** @var bool Should include old uploads */ |
||
64 | public $includeOldUploads; |
||
65 | /** @var ExportJob The export job itself */ |
||
66 | public $job; |
||
67 | /** @var array The settings */ |
||
68 | public $settings; |
||
69 | /** @var Collection The entries to export. */ |
||
70 | private $exportEntries; |
||
71 | /** @var Collection The files to export */ |
||
72 | private $files; |
||
73 | /** @var Collection The journals. */ |
||
74 | private $journals; |
||
75 | |||
76 | /** |
||
77 | * Processor constructor. |
||
78 | */ |
||
79 | public function __construct() |
||
80 | { |
||
81 | $this->journals = new Collection; |
||
82 | $this->exportEntries = new Collection; |
||
83 | $this->files = new Collection; |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * Collect all attachments |
||
88 | * |
||
89 | * @return bool |
||
90 | */ |
||
91 | public function collectAttachments(): bool |
||
92 | { |
||
93 | /** @var AttachmentCollector $attachmentCollector */ |
||
94 | $attachmentCollector = app(AttachmentCollector::class); |
||
95 | $attachmentCollector->setJob($this->job); |
||
96 | $attachmentCollector->setDates($this->settings['startDate'], $this->settings['endDate']); |
||
97 | $attachmentCollector->run(); |
||
98 | $this->files = $this->files->merge($attachmentCollector->getEntries()); |
||
99 | |||
100 | return true; |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * Collects all transaction journals. |
||
105 | * |
||
106 | * @return bool |
||
107 | * @SuppressWarnings(PHPMD.ExcessiveMethodLength) |
||
108 | */ |
||
109 | public function collectJournals(): bool |
||
110 | { |
||
111 | // use journal collector thing. |
||
112 | /** @var TransactionCollectorInterface $collector */ |
||
113 | $collector = app(TransactionCollectorInterface::class); |
||
114 | $collector->setUser($this->job->user); |
||
115 | $collector->setAccounts($this->accounts)->setRange($this->settings['startDate'], $this->settings['endDate']) |
||
116 | ->withOpposingAccount()->withBudgetInformation()->withCategoryInformation() |
||
117 | ->removeFilter(InternalTransferFilter::class); |
||
118 | $transactions = $collector->getTransactions(); |
||
119 | // get some more meta data for each entry: |
||
120 | $ids = $transactions->pluck('journal_id')->toArray(); |
||
121 | $assetIds = $transactions->pluck('account_id')->toArray(); |
||
122 | $opposingIds = $transactions->pluck('opposing_account_id')->toArray(); |
||
123 | $notes = $this->getNotes($ids); |
||
124 | $tags = $this->getTags($ids); |
||
125 | /** @var array $ibans */ |
||
126 | $ibans = array_merge($this->getIbans($assetIds), $this->getIbans($opposingIds)); |
||
127 | $currencies = $this->getAccountCurrencies($ibans); |
||
128 | $transactions->each( |
||
129 | function (Transaction $transaction) use ($notes, $tags, $ibans, $currencies) { |
||
130 | $journalId = (int)$transaction->journal_id; |
||
131 | $accountId = (int)$transaction->account_id; |
||
132 | $opposingId = (int)$transaction->opposing_account_id; |
||
133 | $currencyId = (int)($ibans[$accountId]['currency_id'] ?? 0.0); |
||
134 | $opposingCurrencyId = (int)($ibans[$opposingId]['currency_id'] ?? 0.0); |
||
135 | $transaction->notes = $notes[$journalId] ?? ''; |
||
136 | $transaction->tags = implode(',', $tags[$journalId] ?? []); |
||
137 | $transaction->account_number = $ibans[$accountId]['accountNumber'] ?? ''; |
||
138 | $transaction->account_bic = $ibans[$accountId]['BIC'] ?? ''; |
||
139 | $transaction->account_currency_code = $currencies[$currencyId] ?? ''; |
||
140 | $transaction->opposing_account_number = $ibans[$opposingId]['accountNumber'] ?? ''; |
||
141 | $transaction->opposing_account_bic = $ibans[$opposingId]['BIC'] ?? ''; |
||
142 | $transaction->opposing_currency_code = $currencies[$opposingCurrencyId] ?? ''; |
||
143 | } |
||
144 | ); |
||
145 | |||
146 | $this->journals = $transactions; |
||
147 | |||
148 | return true; |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Get old oploads. |
||
153 | * |
||
154 | * @return bool |
||
155 | */ |
||
156 | public function collectOldUploads(): bool |
||
157 | { |
||
158 | /** @var UploadCollector $uploadCollector */ |
||
159 | $uploadCollector = app(UploadCollector::class); |
||
160 | $uploadCollector->setJob($this->job); |
||
161 | $uploadCollector->run(); |
||
162 | |||
163 | $this->files = $this->files->merge($uploadCollector->getEntries()); |
||
164 | |||
165 | return true; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Convert journals to export objects. |
||
170 | * |
||
171 | * @return bool |
||
172 | */ |
||
173 | public function convertJournals(): bool |
||
174 | { |
||
175 | $this->journals->each( |
||
176 | function (Transaction $transaction) { |
||
177 | $this->exportEntries->push(Entry::fromTransaction($transaction)); |
||
178 | } |
||
179 | ); |
||
180 | Log::debug(sprintf('Count %d entries in exportEntries (convertJournals)', $this->exportEntries->count())); |
||
181 | |||
182 | return true; |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * Create a ZIP file locally (!) in storage_path('export'). |
||
187 | * |
||
188 | * @return bool |
||
189 | * |
||
190 | * @throws FireflyException |
||
191 | * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException |
||
192 | */ |
||
193 | public function createZipFile(): bool |
||
194 | { |
||
195 | $zip = new ZipArchive; |
||
196 | $file = $this->job->key . '.zip'; |
||
197 | $localPath = storage_path('export') . '/' . $file; |
||
198 | |||
199 | if (true !== $zip->open($localPath, ZipArchive::CREATE)) { |
||
200 | throw new FireflyException('Cannot store zip file.'); |
||
201 | } |
||
202 | // for each file in the collection, add it to the zip file. |
||
203 | $disk = Storage::disk('export'); |
||
204 | foreach ($this->getFiles() as $entry) { |
||
205 | // is part of this job? |
||
206 | $zipFileName = str_replace($this->job->key . '-', '', $entry); |
||
207 | $zip->addFromString($zipFileName, $disk->get($entry)); |
||
208 | } |
||
209 | |||
210 | $zip->close(); |
||
211 | |||
212 | // delete the files: |
||
213 | $this->deleteFiles(); |
||
214 | |||
215 | return true; |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * Export the journals. |
||
220 | * |
||
221 | * @return bool |
||
222 | */ |
||
223 | public function exportJournals(): bool |
||
224 | { |
||
225 | $exporterClass = config('firefly.export_formats.' . $this->exportFormat); |
||
226 | /** @var ExporterInterface $exporter */ |
||
227 | $exporter = app($exporterClass); |
||
228 | $exporter->setJob($this->job); |
||
229 | $exporter->setEntries($this->exportEntries); |
||
230 | $exporter->run(); |
||
231 | $this->files->push($exporter->getFileName()); |
||
232 | |||
233 | return true; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * Get files. |
||
238 | * |
||
239 | * @return Collection |
||
240 | */ |
||
241 | public function getFiles(): Collection |
||
242 | { |
||
243 | return $this->files; |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Save export job settings to class. |
||
248 | * |
||
249 | * @param array $settings |
||
250 | */ |
||
251 | public function setSettings(array $settings): void |
||
252 | { |
||
253 | // save settings |
||
254 | $this->settings = $settings; |
||
255 | $this->accounts = $settings['accounts']; |
||
256 | $this->exportFormat = $settings['exportFormat']; |
||
257 | $this->includeAttachments = $settings['includeAttachments']; |
||
258 | $this->includeOldUploads = $settings['includeOldUploads']; |
||
259 | $this->job = $settings['job']; |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Delete files. |
||
264 | */ |
||
265 | private function deleteFiles(): void |
||
266 | { |
||
267 | $disk = Storage::disk('export'); |
||
268 | foreach ($this->getFiles() as $file) { |
||
269 | $disk->delete($file); |
||
270 | } |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Get currencies. |
||
275 | * |
||
276 | * @param array $array |
||
277 | * |
||
278 | * @return array |
||
279 | */ |
||
280 | private function getAccountCurrencies(array $array): array |
||
281 | { |
||
282 | /** @var CurrencyRepositoryInterface $repository */ |
||
283 | $repository = app(CurrencyRepositoryInterface::class); |
||
284 | $return = []; |
||
285 | $ids = []; |
||
286 | $repository->setUser($this->job->user); |
||
287 | foreach ($array as $value) { |
||
288 | $ids[] = (int)($value['currency_id'] ?? 0.0); |
||
289 | } |
||
290 | $ids = array_unique($ids); |
||
291 | $result = $repository->getByIds($ids); |
||
292 | |||
293 | foreach ($result as $currency) { |
||
294 | $return[$currency->id] = $currency->code; |
||
295 | } |
||
296 | |||
297 | return $return; |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Get all IBAN / SWIFT / account numbers. |
||
302 | * |
||
303 | * @param array $array |
||
304 | * |
||
305 | * @return array |
||
306 | */ |
||
307 | private function getIbans(array $array): array |
||
308 | { |
||
309 | $array = array_unique($array); |
||
310 | $return = []; |
||
311 | $set = AccountMeta::whereIn('account_id', $array) |
||
312 | ->leftJoin('accounts', 'accounts.id', 'account_meta.account_id') |
||
313 | ->where('accounts.user_id', $this->job->user_id) |
||
314 | ->whereIn('account_meta.name', ['accountNumber', 'BIC', 'currency_id']) |
||
315 | ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data']); |
||
316 | /** @var AccountMeta $meta */ |
||
317 | foreach ($set as $meta) { |
||
318 | $id = (int)$meta->account_id; |
||
319 | $return[$id][$meta->name] = $meta->data; |
||
320 | } |
||
321 | |||
322 | return $return; |
||
323 | } |
||
324 | |||
325 | /** |
||
326 | * Returns, if present, for the given journal ID's the notes. |
||
327 | * |
||
328 | * @param array $array |
||
329 | * |
||
330 | * @return array |
||
331 | */ |
||
332 | private function getNotes(array $array): array |
||
333 | { |
||
334 | $array = array_unique($array); |
||
335 | $notes = Note::where('notes.noteable_type', TransactionJournal::class) |
||
336 | ->whereIn('notes.noteable_id', $array) |
||
337 | ->get(['notes.*']); |
||
338 | $return = []; |
||
339 | /** @var Note $note */ |
||
340 | foreach ($notes as $note) { |
||
341 | if ('' !== trim((string)$note->text)) { |
||
342 | $id = (int)$note->noteable_id; |
||
343 | $return[$id] = $note->text; |
||
344 | } |
||
345 | } |
||
346 | |||
347 | return $return; |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Returns a comma joined list of all the users tags linked to these journals. |
||
352 | * |
||
353 | * @param array $array |
||
354 | * |
||
355 | * @return array |
||
356 | * @throws \Illuminate\Contracts\Encryption\DecryptException |
||
357 | */ |
||
358 | private function getTags(array $array): array |
||
359 | { |
||
360 | $set = DB::table('tag_transaction_journal') |
||
361 | ->whereIn('tag_transaction_journal.transaction_journal_id', $array) |
||
362 | ->leftJoin('tags', 'tag_transaction_journal.tag_id', '=', 'tags.id') |
||
363 | ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'tag_transaction_journal.transaction_journal_id') |
||
364 | ->where('transaction_journals.user_id', $this->job->user_id) |
||
365 | ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']); |
||
366 | $result = []; |
||
367 | foreach ($set as $entry) { |
||
368 | $id = (int)$entry->transaction_journal_id; |
||
369 | $result[$id] = $result[$id] ?? []; |
||
370 | $result[$id][] = $entry->tag; |
||
371 | } |
||
372 | |||
373 | return $result; |
||
374 | } |
||
375 | } |
||
376 |
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.