Record.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  1. <?php
  2. namespace App\Http\Livewire;
  3. use Livewire\Component;
  4. use DateInterval;
  5. use DatePeriod;
  6. use DateTime;
  7. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  8. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  9. use Illuminate\Support\Facades\Storage;
  10. use Illuminate\Support\Facades\Log;
  11. class Record extends Component
  12. {
  13. public $records, $dataId, $totals;
  14. public $in;
  15. public $out;
  16. public $payments = [];
  17. public $fromDate;
  18. public $toDate;
  19. public $appliedFromDate;
  20. public $appliedToDate;
  21. public $exportFromDate;
  22. public $exportToDate;
  23. public $isExporting = false;
  24. public $selectedPeriod = 'OGGI';
  25. public $filterCausals = null;
  26. public $filterMember = null;
  27. public $isFiltering = false;
  28. public array $recordDatas = [];
  29. public array $labels = [];
  30. public array $causals = [];
  31. public $members = array();
  32. public function hydrate()
  33. {
  34. $this->emit('load-select');
  35. }
  36. public function mount()
  37. {
  38. $this->fromDate = date("Y-m-d");
  39. $this->toDate = date("Y-m-d");
  40. $this->appliedFromDate = date("Y-m-d");
  41. $this->appliedToDate = date("Y-m-d");
  42. $this->exportFromDate = date("Y-m-d");
  43. $this->exportToDate = date("Y-m-d");
  44. $this->getCausals(\App\Models\Causal::select('id', 'name')->where('parent_id', null)->get(), 0);
  45. $this->members = \App\Models\Member::select(['id', 'first_name', 'last_name', 'fiscal_code'])->orderBy('last_name')->orderBy('first_name')->get();
  46. $this->payments = \App\Models\PaymentMethod::select('id', 'name', 'type')->where('enabled', true)->where('money', false)->get();
  47. }
  48. private function generateExportData($fromDate, $toDate)
  49. {
  50. $exportRecords = array();
  51. $exportTotals = array();
  52. $exclude_from_records = \App\Models\Member::where('exclude_from_records', true)->pluck('id')->toArray();
  53. $datas = \App\Models\Record::with('member', 'supplier', 'payment_method')
  54. ->select('records.*', 'records_rows.*')
  55. ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
  56. ->whereBetween('date', [$fromDate, $toDate])
  57. ->where(function ($query) {
  58. $query->where('type', 'OUT')
  59. ->orWhere(function ($query) {
  60. $query->where('records.corrispettivo_fiscale', true)
  61. ->orWhere('records.commercial', false);
  62. });
  63. })
  64. ->where(function ($query) use ($exclude_from_records) {
  65. $query->where('type', 'OUT')
  66. ->orWhere(function ($subquery) use ($exclude_from_records) {
  67. $subquery->whereNotIn('member_id', $exclude_from_records);
  68. });
  69. });
  70. if ($this->filterCausals != null && sizeof($this->filterCausals) > 0) {
  71. $causals = array();
  72. foreach ($this->filterCausals as $z) {
  73. $causals[] = $z;
  74. $childs = \App\Models\Causal::where('parent_id', $z)->get();
  75. foreach ($childs as $c) {
  76. $causals[] = $c->id;
  77. $childsX = \App\Models\Causal::where('parent_id', $c->id)->get();
  78. foreach ($childsX as $cX) {
  79. $causals[] = $cX->id;
  80. }
  81. }
  82. }
  83. $datas->whereIn('causal_id', $causals);
  84. }
  85. if ($this->filterMember != null && $this->filterMember > 0) {
  86. $datas->where('member_id', $this->filterMember);
  87. }
  88. $datas = $datas->orderBy('date', 'ASC')
  89. ->orderBy('records.created_at', 'ASC')
  90. ->orderBy('records_rows.id', 'ASC')
  91. ->get();
  92. $groupedData = [];
  93. $causalsCount = [];
  94. foreach ($datas as $idx => $data) {
  95. $causalCheck = \App\Models\Causal::findOrFail($data->causal_id);
  96. $paymentCheck = $data->payment_method->money;
  97. if (!$paymentCheck && ($causalCheck->no_first == null || !$causalCheck->no_first)) {
  98. if (!$data->deleted) {
  99. $amount = $data->amount;
  100. $amount += getVatValue($amount, $data->vat_id);
  101. } else {
  102. $amount = $data->amount;
  103. }
  104. $typeLabel = $data->commercial ? 'Commerciale' : 'Non Commerciale';
  105. $nominativo = '';
  106. if ($data->type == "IN") {
  107. if ($data->member) {
  108. $nominativo = $data->member->last_name . " " . $data->member->first_name;
  109. }
  110. } else {
  111. if ($data->supplier) {
  112. $nominativo = $data->supplier->name;
  113. }
  114. }
  115. $groupKey = $data->date . '|' . $typeLabel . '|' . $data->payment_method->name . '|' . $data->type . '|' . $nominativo;
  116. if (!isset($groupedData[$groupKey])) {
  117. $groupedData[$groupKey] = [
  118. 'date' => $data->date,
  119. 'type_label' => $typeLabel,
  120. 'payment_method' => $data->payment_method->name,
  121. 'transaction_type' => $data->type,
  122. 'nominativo' => $nominativo,
  123. 'amount' => 0,
  124. 'deleted' => false,
  125. 'causals' => [],
  126. 'notes' => []
  127. ];
  128. $causalsCount[$groupKey] = [];
  129. }
  130. $groupedData[$groupKey]['amount'] += $amount;
  131. $causalsCount[$groupKey][$causalCheck->getTree()] = true;
  132. if (!empty($data->note)) {
  133. $groupedData[$groupKey]['notes'][] = $data->note;
  134. }
  135. if ($data->deleted) {
  136. $groupedData[$groupKey]['deleted'] = true;
  137. }
  138. }
  139. }
  140. foreach ($groupedData as $groupKey => $group) {
  141. $causalsInGroup = array_keys($causalsCount[$groupKey]);
  142. $causalDisplay = $group['type_label'];
  143. if (count($causalsInGroup) > 1) {
  144. $detailDisplay = 'Varie|' . implode('|', $causalsInGroup);
  145. } else {
  146. $detailDisplay = $causalsInGroup[0];
  147. }
  148. $recordKey = $group['date'] . "§" . $causalDisplay . "§" . $group['nominativo'] . "§" . $detailDisplay . "§" . ($group['deleted'] ? 'DELETED' : '') . "§";
  149. if (!isset($exportRecords[$recordKey][$group['payment_method']][$group['transaction_type']])) {
  150. $exportRecords[$recordKey][$group['payment_method']][$group['transaction_type']] = 0;
  151. }
  152. $exportRecords[$recordKey][$group['payment_method']][$group['transaction_type']] += $group['amount'];
  153. if (!isset($exportTotals[$group['payment_method']])) {
  154. $exportTotals[$group['payment_method']]["IN"] = 0;
  155. $exportTotals[$group['payment_method']]["OUT"] = 0;
  156. }
  157. if (!$group['deleted'])
  158. $exportTotals[$group['payment_method']][$group['transaction_type']] += $group['amount'];
  159. }
  160. return $exportRecords;
  161. }
  162. private function generateExportTotals($fromDate, $toDate)
  163. {
  164. $exportTotals = array();
  165. $exclude_from_records = \App\Models\Member::where('exclude_from_records', true)->pluck('id')->toArray();
  166. $datas = \App\Models\Record::with('member', 'supplier', 'payment_method')
  167. ->select('records.*', 'records_rows.*')
  168. ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
  169. ->whereBetween('date', [$fromDate, $toDate])
  170. ->where(function ($query) {
  171. $query->where('type', 'OUT')
  172. ->orWhere(function ($query) {
  173. $query->where('records.corrispettivo_fiscale', true)
  174. ->orWhere('records.commercial', false);
  175. });
  176. })
  177. ->where(function ($query) use ($exclude_from_records) {
  178. $query->where('type', 'OUT')
  179. ->orWhere(function ($subquery) use ($exclude_from_records) {
  180. $subquery->whereNotIn('member_id', $exclude_from_records);
  181. });
  182. });
  183. if ($this->filterCausals != null && sizeof($this->filterCausals) > 0) {
  184. $causals = array();
  185. foreach ($this->filterCausals as $z) {
  186. $causals[] = $z;
  187. $childs = \App\Models\Causal::where('parent_id', $z)->get();
  188. foreach ($childs as $c) {
  189. $causals[] = $c->id;
  190. $childsX = \App\Models\Causal::where('parent_id', $c->id)->get();
  191. foreach ($childsX as $cX) {
  192. $causals[] = $cX->id;
  193. }
  194. }
  195. }
  196. $datas->whereIn('causal_id', $causals);
  197. }
  198. if ($this->filterMember != null && $this->filterMember > 0) {
  199. $datas->where('member_id', $this->filterMember);
  200. }
  201. $datas = $datas->orderBy('date', 'ASC')
  202. ->orderBy('records.created_at', 'ASC')
  203. ->orderBy('records_rows.id', 'ASC')
  204. ->get();
  205. foreach ($datas as $data) {
  206. $causalCheck = \App\Models\Causal::findOrFail($data->causal_id);
  207. $paymentCheck = $data->payment_method->money;
  208. if (!$paymentCheck && ($causalCheck->no_first == null || !$causalCheck->no_first)) {
  209. if (!$data->deleted) {
  210. $amount = $data->amount;
  211. $amount += getVatValue($amount, $data->vat_id);
  212. } else {
  213. $amount = $data->amount;
  214. }
  215. if (!isset($exportTotals[$data->payment_method->name])) {
  216. $exportTotals[$data->payment_method->name]["IN"] = 0;
  217. $exportTotals[$data->payment_method->name]["OUT"] = 0;
  218. }
  219. if (!$data->deleted)
  220. $exportTotals[$data->payment_method->name][$data->type] += $amount;
  221. }
  222. }
  223. return $exportTotals;
  224. }
  225. public function resetFilters()
  226. {
  227. $this->selectedPeriod = 'OGGI';
  228. $this->filterCausals = [];
  229. $this->filterMember = null;
  230. $today = date("Y-m-d");
  231. $this->fromDate = $today;
  232. $this->toDate = $today;
  233. $this->appliedFromDate = $today;
  234. $this->appliedToDate = $today;
  235. $this->emit('filters-reset');
  236. }
  237. public function applyFilters()
  238. {
  239. $this->isFiltering = true;
  240. $this->setPeriodDates();
  241. $this->appliedFromDate = $this->fromDate;
  242. $this->appliedToDate = $this->toDate;
  243. $this->render();
  244. $this->isFiltering = false;
  245. $this->emit('filters-applied');
  246. }
  247. private function setPeriodDates()
  248. {
  249. $today = now();
  250. switch ($this->selectedPeriod) {
  251. case 'OGGI':
  252. $this->fromDate = $today->format('Y-m-d');
  253. $this->toDate = $today->format('Y-m-d');
  254. break;
  255. case 'IERI':
  256. $yesterday = $today->copy()->subDay();
  257. $this->fromDate = $yesterday->format('Y-m-d');
  258. $this->toDate = $yesterday->format('Y-m-d');
  259. break;
  260. case 'MESE CORRENTE':
  261. $this->fromDate = $today->copy()->startOfMonth()->format('Y-m-d');
  262. $this->toDate = $today->copy()->endOfMonth()->format('Y-m-d');
  263. break;
  264. case 'MESE PRECEDENTE':
  265. $lastMonth = $today->copy()->subMonth();
  266. $this->fromDate = $lastMonth->startOfMonth()->format('Y-m-d');
  267. $this->toDate = $lastMonth->endOfMonth()->format('Y-m-d');
  268. break;
  269. case 'ULTIMO TRIMESTRE':
  270. $this->fromDate = $today->copy()->subMonths(3)->startOfMonth()->format('Y-m-d');
  271. $this->toDate = $today->copy()->subMonth()->endOfMonth()->format('Y-m-d');
  272. break;
  273. case 'ULTIMO QUADRIMESTRE':
  274. $this->fromDate = $today->copy()->subMonths(4)->startOfMonth()->format('Y-m-d');
  275. $this->toDate = $today->copy()->subMonth()->endOfMonth()->format('Y-m-d');
  276. break;
  277. }
  278. }
  279. public function getCausals($records, $indentation)
  280. {
  281. foreach ($records as $record) {
  282. $this->causals[] = array('id' => $record->id, 'name' => $record->getTree(), 'text' => $record->getTree(), 'level' => $indentation);
  283. if (count($record->childs))
  284. $this->getCausals($record->childs, $indentation + 1);
  285. }
  286. }
  287. public function getMonth($m)
  288. {
  289. $ret = '';
  290. switch ($m) {
  291. case 1:
  292. $ret = 'Gennaio';
  293. break;
  294. case 2:
  295. $ret = 'Febbraio';
  296. break;
  297. case 3:
  298. $ret = 'Marzo';
  299. break;
  300. case 4:
  301. $ret = 'Aprile';
  302. break;
  303. case 5:
  304. $ret = 'Maggio';
  305. break;
  306. case 6:
  307. $ret = 'Giugno';
  308. break;
  309. case 7:
  310. $ret = 'Luglio';
  311. break;
  312. case 8:
  313. $ret = 'Agosto';
  314. break;
  315. case 9:
  316. $ret = 'Settembre';
  317. break;
  318. case 10:
  319. $ret = 'Ottobre';
  320. break;
  321. case 11:
  322. $ret = 'Novembre';
  323. break;
  324. case 12:
  325. $ret = 'Dicembre';
  326. break;
  327. default:
  328. $ret = '';
  329. break;
  330. }
  331. return $ret;
  332. }
  333. public function render()
  334. {
  335. $month = 0;
  336. $year = 0;
  337. $this->records = array();
  338. $this->totals = array();
  339. $exclude_from_records = \App\Models\Member::where('exclude_from_records', true)->pluck('id')->toArray();
  340. $datas = \App\Models\Record::with('member', 'supplier', 'payment_method')
  341. ->select('records.*', 'records_rows.*')
  342. ->join('records_rows', 'records.id', '=', 'records_rows.record_id')
  343. ->whereBetween('date', [$this->appliedFromDate, $this->appliedToDate])
  344. ->where(function ($query) {
  345. $query->where('type', 'OUT')
  346. ->orWhere(function ($query) {
  347. $query->where('records.corrispettivo_fiscale', true)
  348. ->orWhere('records.commercial', false);
  349. });
  350. })
  351. ->where(function ($query) use ($exclude_from_records) {
  352. $query->where('type', 'OUT')
  353. ->orWhere(function ($subquery) use ($exclude_from_records) {
  354. $subquery->whereNotIn('member_id', $exclude_from_records);
  355. });
  356. });
  357. if ($this->filterCausals != null && sizeof($this->filterCausals) > 0) {
  358. $causals = array();
  359. foreach ($this->filterCausals as $z) {
  360. $causals[] = $z;
  361. $childs = \App\Models\Causal::where('parent_id', $z)->get();
  362. foreach ($childs as $c) {
  363. $causals[] = $c->id;
  364. $childsX = \App\Models\Causal::where('parent_id', $c->id)->get();
  365. foreach ($childsX as $cX) {
  366. $causals[] = $cX->id;
  367. }
  368. }
  369. }
  370. $datas->whereIn('causal_id', $causals);
  371. }
  372. if ($this->filterMember != null && $this->filterMember > 0) {
  373. $datas->where('member_id', $this->filterMember);
  374. }
  375. $datas = $datas->orderBy('date', 'ASC')
  376. ->orderBy('records.created_at', 'ASC')
  377. ->orderBy('records_rows.id', 'ASC')
  378. ->get();
  379. $groupedData = [];
  380. $causalsCount = [];
  381. $nominativi = [];
  382. foreach ($datas as $idx => $data) {
  383. $causalCheck = \App\Models\Causal::findOrFail($data->causal_id);
  384. $paymentCheck = $data->payment_method->money;
  385. if (!$paymentCheck && ($causalCheck->no_first == null || !$causalCheck->no_first)) {
  386. if (!$data->deleted) {
  387. $amount = $data->amount;
  388. $amount += getVatValue($amount, $data->vat_id);
  389. } else {
  390. $amount = $data->amount;
  391. }
  392. $typeLabel = $data->commercial ? 'Commerciale' : 'Non Commerciale';
  393. $nominativo = '';
  394. if ($data->type == "IN") {
  395. if ($data->member) {
  396. $nominativo = $data->member->last_name . " " . $data->member->first_name;
  397. }
  398. } else {
  399. if ($data->supplier) {
  400. $nominativo = $data->supplier->name;
  401. }
  402. }
  403. $groupKey = $data->date . '|' . $typeLabel . '|' . $data->payment_method->name . '|' . $data->type . '|' . $nominativo;
  404. if (!isset($groupedData[$groupKey])) {
  405. $groupedData[$groupKey] = [
  406. 'date' => $data->date,
  407. 'type_label' => $typeLabel,
  408. 'payment_method' => $data->payment_method->name,
  409. 'transaction_type' => $data->type,
  410. 'nominativo' => $nominativo,
  411. 'amount' => 0,
  412. 'deleted' => false,
  413. 'causals' => [],
  414. 'notes' => []
  415. ];
  416. $causalsCount[$groupKey] = [];
  417. $nominativi[$groupKey] = $nominativo;
  418. }
  419. $groupedData[$groupKey]['amount'] += $amount;
  420. $causalsCount[$groupKey][$causalCheck->getTree()] = true;
  421. if (!empty($data->note)) {
  422. $groupedData[$groupKey]['notes'][] = $data->note;
  423. }
  424. if ($data->deleted) {
  425. $groupedData[$groupKey]['deleted'] = true;
  426. }
  427. }
  428. }
  429. foreach ($groupedData as $groupKey => $group) {
  430. $causalsInGroup = array_keys($causalsCount[$groupKey]);
  431. $causalDisplay = $group['type_label'];
  432. if (count($causalsInGroup) > 1) {
  433. $detailDisplay = 'Varie|' . implode('|', $causalsInGroup);
  434. } else {
  435. $detailDisplay = $causalsInGroup[0];
  436. }
  437. $recordKey = $group['date'] . "§" . $causalDisplay . "§" . $group['nominativo'] . "§" . $detailDisplay . "§" . ($group['deleted'] ? 'DELETED' : '') . "§";
  438. if (!isset($this->records[$recordKey][$group['payment_method']][$group['transaction_type']])) {
  439. $this->records[$recordKey][$group['payment_method']][$group['transaction_type']] = 0;
  440. }
  441. $this->records[$recordKey][$group['payment_method']][$group['transaction_type']] += $group['amount'];
  442. if (!isset($this->totals[$group['payment_method']])) {
  443. $this->totals[$group['payment_method']]["IN"] = 0;
  444. $this->totals[$group['payment_method']]["OUT"] = 0;
  445. }
  446. if (!$group['deleted'])
  447. $this->totals[$group['payment_method']][$group['transaction_type']] += $group['amount'];
  448. }
  449. return view('livewire.records');
  450. }
  451. private function getLabels($fromDate, $toDate)
  452. {
  453. $begin = new DateTime($fromDate);
  454. $end = new DateTime($toDate);
  455. $interval = DateInterval::createFromDateString('1 day');
  456. $date_range = new DatePeriod($begin, $interval, $end);
  457. foreach ($date_range as $date) {
  458. $labels[] = $date->format('d/M');
  459. }
  460. return $labels;
  461. }
  462. private function getRecordData($type, $fromDate, $toDate)
  463. {
  464. $data = [];
  465. $begin = new DateTime($fromDate);
  466. $end = new DateTime($toDate);
  467. $interval = DateInterval::createFromDateString('1 day');
  468. $date_range = new DatePeriod($begin, $interval, $end);
  469. foreach ($date_range as $date) {
  470. if ($type == 'IN') {
  471. $found = false;
  472. foreach ($this->in as $in) {
  473. if (date("Y-m-d", strtotime($in->date)) == $date->format('Y-m-d')) {
  474. $data[] = number_format($in->total, 0, "", "");
  475. $found = true;
  476. }
  477. }
  478. if (!$found)
  479. $data[] = 0;
  480. }
  481. if ($type == 'OUT') {
  482. $found = false;
  483. foreach ($this->out as $out) {
  484. if (date("Y-m-d", strtotime($out->date)) == $date->format('Y-m-d')) {
  485. $data[] = number_format($out->total, 0, "", "");
  486. $found = true;
  487. }
  488. }
  489. if (!$found)
  490. $data[] = 0;
  491. }
  492. }
  493. return $data;
  494. }
  495. public function openExportModal()
  496. {
  497. $this->exportFromDate = $this->appliedFromDate;
  498. $this->exportToDate = $this->appliedToDate;
  499. $this->emit('show-export-modal');
  500. }
  501. public function exportWithDateRange()
  502. {
  503. $this->isExporting = true;
  504. $exportRecords = $this->generateExportData($this->exportFromDate, $this->exportToDate);
  505. $exportTotals = $this->generateExportTotals($this->exportFromDate, $this->exportToDate);
  506. $result = $this->exportWithData($exportRecords, $exportTotals);
  507. $this->isExporting = false;
  508. $this->emit('hide-export-modal');
  509. }
  510. function export()
  511. {
  512. $exportRecords = $this->generateExportData($this->appliedFromDate, $this->appliedToDate);
  513. $exportTotals = $this->generateExportTotals($this->appliedFromDate, $this->appliedToDate);
  514. return $this->exportWithData($exportRecords, $exportTotals);
  515. }
  516. private function exportWithData($exportRecords, $exportTotals)
  517. {
  518. ini_set('memory_limit', '512M');
  519. gc_enable();
  520. $letters = array('F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'AA');
  521. $spreadsheet = new Spreadsheet();
  522. $activeWorksheet = $spreadsheet->getActiveSheet();
  523. $activeWorksheet->setCellValue('A1', "Data");
  524. $activeWorksheet->setCellValue('B1', "Causale");
  525. $activeWorksheet->setCellValue('C1', "Dettaglio");
  526. $activeWorksheet->setCellValue('D1', "Nominativo");
  527. $activeWorksheet->setCellValue('E1', "Stato");
  528. $idx = 0;
  529. foreach ($this->payments as $p) {
  530. if ($idx >= count($letters)) {
  531. break;
  532. }
  533. $activeWorksheet->setCellValue($letters[$idx] . '1', $p->name);
  534. $idx++;
  535. if ($idx >= count($letters)) {
  536. break;
  537. }
  538. $activeWorksheet->mergeCells($letters[$idx] . '1:' . $letters[$idx] . '1');
  539. $idx++;
  540. }
  541. $idx = 0;
  542. $activeWorksheet->setCellValue('A2', "");
  543. $activeWorksheet->setCellValue('B2', "");
  544. $activeWorksheet->setCellValue('C2', "");
  545. $activeWorksheet->setCellValue('D2', "");
  546. $activeWorksheet->setCellValue('E2', "");
  547. foreach ($this->payments as $p) {
  548. if ($p->type == 'ALL') {
  549. if ($idx >= count($letters)) {
  550. break;
  551. }
  552. $activeWorksheet->setCellValue($letters[$idx] . '2', "Entrate");
  553. $idx++;
  554. $activeWorksheet->setCellValue($letters[$idx] . '2', "Uscite");
  555. $idx++;
  556. } elseif ($p->type == 'IN') {
  557. if ($idx >= count($letters)) {
  558. break;
  559. }
  560. $activeWorksheet->setCellValue($letters[$idx] . '2', "Entrate");
  561. $idx++;
  562. $activeWorksheet->setCellValue($letters[$idx] . '2', "");
  563. $idx++;
  564. } elseif ($p->type == 'OUT') {
  565. if ($idx >= count($letters)) {
  566. break;
  567. }
  568. $activeWorksheet->setCellValue($letters[$idx] . '2', "");
  569. $idx++;
  570. $activeWorksheet->setCellValue($letters[$idx] . '2', "Uscite");
  571. $idx++;
  572. }
  573. }
  574. $activeWorksheet->getStyle('A1:Q1')->getFont()->setBold(true);
  575. $activeWorksheet->getStyle('A2:Q2')->getFont()->setBold(true);
  576. $count = 3;
  577. $batchSize = 1000;
  578. $recordsProcessed = 0;
  579. $totalRecords = count($exportRecords);
  580. $recordsArray = array_chunk($exportRecords, $batchSize, true);
  581. foreach ($recordsArray as $recordsBatch) {
  582. foreach ($recordsBatch as $causal => $record) {
  583. $check = $causal;
  584. $parts = explode("§", $check);
  585. $d = $parts[0] ?? '';
  586. $c = $parts[1] ?? '';
  587. $j = $parts[2] ?? '';
  588. $det = $parts[3] ?? '';
  589. $deleted = $parts[4] ?? '';
  590. $detailParts = explode('|', $det);
  591. $exportDetail = count($detailParts) > 1 ? implode(', ', array_slice($detailParts, 1)) : $det;
  592. $activeWorksheet->setCellValue('A' . $count, !empty($d) ? date("d/m/Y", strtotime($d)) : '');
  593. $activeWorksheet->setCellValue('B' . $count, $c);
  594. $activeWorksheet->setCellValue('C' . $count, $exportDetail);
  595. $activeWorksheet->setCellValue('D' . $count, $j);
  596. $stato = ($deleted === 'DELETED') ? 'ANNULLATA' : '';
  597. $activeWorksheet->setCellValue('E' . $count, $stato);
  598. if ($stato === 'ANNULLATA') {
  599. $activeWorksheet->getStyle('E' . $count)->getFont()->getColor()->setARGB('FFFF0000');
  600. }
  601. $idx = 0;
  602. foreach ($this->payments as $p) {
  603. if ($idx >= count($letters) - 1) {
  604. break;
  605. }
  606. if (isset($record[$p->name])) {
  607. $inValue = isset($record[$p->name]["IN"]) ? formatPrice($record[$p->name]["IN"]) : "";
  608. $outValue = isset($record[$p->name]["OUT"]) ? formatPrice($record[$p->name]["OUT"]) : "";
  609. $activeWorksheet->setCellValue($letters[$idx] . $count, $inValue);
  610. $idx++;
  611. $activeWorksheet->setCellValue($letters[$idx] . $count, $outValue);
  612. $idx++;
  613. } else {
  614. $activeWorksheet->setCellValue($letters[$idx] . $count, "");
  615. $idx++;
  616. $activeWorksheet->setCellValue($letters[$idx] . $count, "");
  617. $idx++;
  618. }
  619. }
  620. $count++;
  621. $recordsProcessed++;
  622. if ($recordsProcessed % 500 === 0) {
  623. gc_collect_cycles();
  624. }
  625. }
  626. unset($recordsBatch);
  627. gc_collect_cycles();
  628. }
  629. $count++;
  630. $idx = 0;
  631. $activeWorksheet->setCellValue('A' . $count, 'Totale');
  632. $activeWorksheet->setCellValue('B' . $count, '');
  633. $activeWorksheet->setCellValue('C' . $count, '');
  634. $activeWorksheet->setCellValue('D' . $count, '');
  635. $activeWorksheet->setCellValue('E' . $count, '');
  636. foreach ($this->payments as $p) {
  637. if ($idx >= count($letters) - 1) {
  638. break;
  639. }
  640. if (isset($exportTotals[$p->name])) {
  641. if ($p->type == 'ALL') {
  642. $activeWorksheet->setCellValue($letters[$idx] . $count, formatPrice($exportTotals[$p->name]["IN"] ?? 0));
  643. $idx++;
  644. $activeWorksheet->setCellValue($letters[$idx] . $count, formatPrice($exportTotals[$p->name]["OUT"] ?? 0));
  645. $idx++;
  646. } elseif ($p->type == 'IN') {
  647. $activeWorksheet->setCellValue($letters[$idx] . $count, formatPrice($exportTotals[$p->name]["IN"] ?? 0));
  648. $idx++;
  649. $activeWorksheet->setCellValue($letters[$idx] . $count, "");
  650. $idx++;
  651. } elseif ($p->type == 'OUT') {
  652. $activeWorksheet->setCellValue($letters[$idx] . $count, "");
  653. $idx++;
  654. $activeWorksheet->setCellValue($letters[$idx] . $count, formatPrice($exportTotals[$p->name]["OUT"] ?? 0));
  655. $idx++;
  656. }
  657. } else {
  658. if ($p->type == 'ALL') {
  659. $activeWorksheet->setCellValue($letters[$idx] . $count, "0");
  660. $idx++;
  661. $activeWorksheet->setCellValue($letters[$idx] . $count, "0");
  662. $idx++;
  663. } elseif ($p->type == 'IN') {
  664. $activeWorksheet->setCellValue($letters[$idx] . $count, "0");
  665. $idx++;
  666. $activeWorksheet->setCellValue($letters[$idx] . $count, "");
  667. $idx++;
  668. } elseif ($p->type == 'OUT') {
  669. $activeWorksheet->setCellValue($letters[$idx] . $count, "");
  670. $idx++;
  671. $activeWorksheet->setCellValue($letters[$idx] . $count, "0");
  672. $idx++;
  673. }
  674. }
  675. }
  676. $activeWorksheet->getStyle('A' . $count . ':Q' . $count)->getFont()->setBold(true);
  677. $activeWorksheet->getColumnDimension('A')->setWidth(20);
  678. $activeWorksheet->getColumnDimension('B')->setWidth(40);
  679. $activeWorksheet->getColumnDimension('C')->setWidth(40);
  680. $activeWorksheet->getColumnDimension('D')->setWidth(40);
  681. $activeWorksheet->getColumnDimension('E')->setWidth(20);
  682. foreach ($letters as $l) {
  683. $activeWorksheet->getColumnDimension($l)->setWidth(20);
  684. }
  685. $filename = 'prima_nota_' . date("YmdHis") . '.xlsx';
  686. try {
  687. $currentClient = session('currentClient', 'default');
  688. $tempPath = sys_get_temp_dir() . '/' . $filename;
  689. $writer = new Xlsx($spreadsheet);
  690. $writer->save($tempPath);
  691. unset($spreadsheet, $activeWorksheet, $writer);
  692. gc_collect_cycles();
  693. $disk = Storage::disk('s3');
  694. $s3Path = $currentClient . '/prima_nota/' . $filename;
  695. $primaNotaFolderPath = $currentClient . '/prima_nota/.gitkeep';
  696. if (!$disk->exists($primaNotaFolderPath)) {
  697. $disk->put($primaNotaFolderPath, '');
  698. Log::info("Created prima_nota folder for client: {$currentClient}");
  699. }
  700. $fileContent = file_get_contents($tempPath);
  701. $uploaded = $disk->put($s3Path, $fileContent, 'private');
  702. if (!$uploaded) {
  703. throw new \Exception('Failed to upload file to Wasabi S3');
  704. }
  705. Log::info("Prima Nota exported to Wasabi", [
  706. 'client' => $currentClient,
  707. 'path' => $s3Path,
  708. 'size' => filesize($tempPath),
  709. 'records_processed' => $recordsProcessed
  710. ]);
  711. if (file_exists($tempPath)) {
  712. unlink($tempPath);
  713. }
  714. $downloadUrl = $disk->temporaryUrl($s3Path, now()->addHour());
  715. return redirect($downloadUrl);
  716. } catch (\Exception $e) {
  717. Log::error('Error exporting Prima Nota to Wasabi S3', [
  718. 'error' => $e->getMessage(),
  719. 'client' => session('currentClient', 'unknown'),
  720. 'filename' => $filename,
  721. 'records_processed' => $recordsProcessed ?? 0
  722. ]);
  723. $currentClient = session('currentClient', 'default');
  724. $clientFolder = storage_path('app/prima_nota/' . $currentClient);
  725. if (!is_dir($clientFolder)) {
  726. mkdir($clientFolder, 0755, true);
  727. Log::info("Created local client prima_nota folder: {$clientFolder}");
  728. }
  729. $localPath = $clientFolder . '/' . $filename;
  730. if (isset($tempPath) && file_exists($tempPath)) {
  731. rename($tempPath, $localPath);
  732. } else {
  733. $writer = new Xlsx($spreadsheet);
  734. $writer->save($localPath);
  735. unset($spreadsheet, $activeWorksheet, $writer);
  736. }
  737. gc_collect_cycles();
  738. Log::warning("Prima Nota saved locally due to S3 error", [
  739. 'client' => $currentClient,
  740. 'local_path' => $localPath,
  741. 'error' => $e->getMessage()
  742. ]);
  743. session()->flash('warning', 'File salvato localmente a causa di un errore del cloud storage.');
  744. return response()->download($localPath)->deleteFileAfterSend();
  745. }
  746. }
  747. }