From c76587d08ab8b1dab975875cdca5abf3bd6eb3f4 Mon Sep 17 00:00:00 2001 From: Jia Sheng Date: Tue, 24 Sep 2024 22:08:49 +0800 Subject: [PATCH] update on ambank csv file format --- .../ImportAmbankStatementLogic.php | 256 ++++++++---------- .../forms/ImportStatementFormComponent.vue | 5 +- .../general/forms/FileUploadComponent.vue | 2 +- 3 files changed, 118 insertions(+), 145 deletions(-) diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ImportAmbankStatementLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ImportAmbankStatementLogic.php index e5d7adf0..afaddf92 100644 --- a/app/Classes/Modules/Accounting/ControllersLogic/ImportAmbankStatementLogic.php +++ b/app/Classes/Modules/Accounting/ControllersLogic/ImportAmbankStatementLogic.php @@ -24,10 +24,11 @@ class ImportAmbankStatementLogic extends AbstractControllerLogic /** * @return array */ - protected function notification():array { + protected function notification(): array + { return [ 'title' => 'Import Ambank Statement Transactions Details', - 'message' => 'You have successfully updated the Ambank Statement Transactions Details' + 'message' => 'You have successfully imported the Ambank Statement Transactions Details' ]; } @@ -37,178 +38,147 @@ class ImportAmbankStatementLogic extends AbstractControllerLogic * @return JsonResponse * @throws MalformedRequestException */ - public function logic(Request $request) : JsonResponse + public function logic(Request $request): JsonResponse { + $requiredHeaders = ['Date', 'Time', 'Description', 'Recipient Reference', 'Other Payment Details', 'Transfer Type', 'Inward Amount', 'Outward Amount', 'Balance']; $object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports'); - foreach ($object->getFiles() as $file){ + foreach ($object->getFiles() as $file) { $collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true); - $statementDetailRows = $collection->first()->slice(0,13); + // check whether the csv is format we expect for + foreach ($collection as $sheet_no => $sheet) { + $header_mapping_result = []; + foreach ($sheet as $row_no => $row) { + if ($row[0] !== $requiredHeaders[0]) { + continue; + } - $dateInfoKey = $this->findInfoIndexFromExcelCollection($statementDetailRows, "STATEMENT DATE"); - $statementDateRange = trim(explode(':', $this->extractInfoFromExcelCollection($statementDetailRows[$dateInfoKey])[0])[1]); - $dateFrom = trim(explode('-', $statementDateRange)[0]); - $dateTo = trim(explode('-', $statementDateRange)[1]); - $dateFrom = carbon::createFromFormat('d/m/Y', $dateFrom); - $dateTo = carbon::createFromFormat('d/m/Y', $dateTo); - $dateTo = carbon::parse($dateTo); - $totalDebitKey = $this->findInfoIndexFromExcelCollection($statementDetailRows, "TOTAL DEBIT"); - $totalDebit = $this->extractInfoFromExcelCollection($statementDetailRows[$totalDebitKey])[1]; - $totalCreditKey = $this->findInfoIndexFromExcelCollection($statementDetailRows, "TOTAL CREDIT"); - $totalCredit = $this->extractInfoFromExcelCollection($statementDetailRows[$totalCreditKey])[1]; - $totalTransactions = $totalDebit + $totalCredit; - $beginBalanceKey = $this->findInfoIndexFromExcelCollection($statementDetailRows, "OPENING BALANCE"); - $beginBalance = ((float) str_replace(',', '', $this->extractInfoFromExcelCollection($statementDetailRows[$beginBalanceKey])[1])); - $endBalanceKey = $this->findInfoIndexFromExcelCollection($statementDetailRows, "CLOSING BALANCE"); - $endBalance = ((float) str_replace(',', '', $this->extractInfoFromExcelCollection($statementDetailRows[$endBalanceKey])[1])); + foreach ($requiredHeaders as $col_no => $header) { + if ($row[$col_no] !== $header) { + $header_mapping_result[$col_no] = false; + } else { + $header_mapping_result[$col_no] = true; + } + } + + if (count($header_mapping_result) === 9) { + break; + } + } + + if (count($header_mapping_result) === 0 || in_array(false, $header_mapping_result)) { + throw new MalformedRequestException("Sheet {$sheet_no} format is not correct"); + } + } + + $data_start_from = $sheet->search(function ($row, $key) { + return $row[0] === 'Date'; + }) + 1; + $data_end_at = $sheet->keys()->last(); + $descending = true; + + for ($i = $data_end_at; $i > 0; $i--) { + $parse_date = Carbon::createFromFormat('d/m/Y', $sheet[$i][0]); + if ($parse_date && $parse_date->format('d/m/Y') === $sheet[$i][0]) { + $data_end_at = $i; + break; + } + } + + if (Carbon::createFromFormat('d/m/Y', $sheet[$data_start_from][0]) > Carbon::createFromFormat('d/m/Y', $sheet[$data_end_at][0])) { + $descending = true; + } else { + $descending = false; + } + + $date_from = Carbon::createFromFormat('d/m/Y', $sheet[$descending ? $data_end_at : $data_start_from][0])->format('Y-m-d'); + $date_to = Carbon::createFromFormat('d/m/Y', $sheet[$descending ? $data_start_from : $data_end_at][0])->format('Y-m-d'); + $total_rows = abs($data_end_at - $data_start_from) + 1; + $begin_balance = floatval($sheet[$descending ? $data_end_at : $data_start_from][8]); + $end_balance = floatval($sheet[$descending ? $data_start_from : $data_end_at][8]); + $total_debit = 0; + $total_credit = 0; $account = StatementAccount::where('number', 8881040198515)->first(); if (!$account) { - throw new Exception("Statement Account for CIEF LITE not found."); + throw new Exception("Statement Account for AMBANK not found."); } - $statement = AccountStatement::whereDate('date_from', $dateFrom) - ->whereDate('date_to', $dateTo) - ->where('total_amount', $totalTransactions) - ->where('begin_balance', $beginBalance) - ->where('end_balance', $endBalance) + $statement = AccountStatement::whereDate('date_from', $date_from) + ->whereDate('date_to', $date_to) + ->where('total_rows', $total_rows) + ->where('begin_balance', $begin_balance) + ->where('end_balance', $end_balance) ->first(); - if(!$statement){ + if (!$statement) { $statement = new AccountStatement([ - 'date_from' => $dateFrom, - 'date_to' => $dateTo, - 'total_amount' => $totalTransactions, - 'begin_balance' => $beginBalance, - 'end_balance' => $endBalance, + 'statement_account_id' => $account->id, + 'date_from' => $date_from, + 'date_to' => $date_to, + 'total_rows' => $total_rows, + 'begin_balance' => $begin_balance, + 'end_balance' => $end_balance, ]); + $statement->save(); } - $account->statements()->save($statement); - - $collection->map(function ($sheet, $key) use ($statement) { - if ($key === 0) { - $headerColumnKey = $this->findInfoIndexFromExcelCollection($sheet->slice(11), "DATE"); - $dateColumnKey = $this->findInfoIndexFromExcelCollection($sheet->slice(11)[$headerColumnKey], "DATE"); - $descriptionColumnKey = $this->findInfoIndexFromExcelCollection($sheet->slice(11)[$headerColumnKey], "TRANSACTION"); - $debitColumnKey = $this->findInfoIndexFromExcelCollection($sheet->slice(11)[$headerColumnKey], "DEBIT"); - $creditColumnKey = $this->findInfoIndexFromExcelCollection($sheet->slice(11)[$headerColumnKey], "CREDIT"); - $balanceColumnKey = $this->findInfoIndexFromExcelCollection($sheet->slice(11)[$headerColumnKey], "BALANCE"); - } else { - if (strpos($sheet->first()->first(), "DATE") === false) { - return; - } - $headerColumnKey = $this->findInfoIndexFromExcelCollection($sheet, "DATE"); - $dateColumnKey = $this->findInfoIndexFromExcelCollection($sheet[$headerColumnKey], "DATE"); - $descriptionColumnKey = $this->findInfoIndexFromExcelCollection($sheet[$headerColumnKey], "TRANSACTION"); - $debitColumnKey = $this->findInfoIndexFromExcelCollection($sheet[$headerColumnKey], "DEBIT"); - $creditColumnKey = $this->findInfoIndexFromExcelCollection($sheet[$headerColumnKey], "CREDIT"); - $balanceColumnKey = $this->findInfoIndexFromExcelCollection($sheet[$headerColumnKey], "BALANCE"); - } - - $sheet->slice($headerColumnKey + 1)->map(function ($row, $index) use ( - $statement, - $sheet, - $dateColumnKey, - $descriptionColumnKey, - $debitColumnKey, - $creditColumnKey, - $balanceColumnKey, - $key) - { - // if date or balance is null or empty, skip the row - if (!$row[$dateColumnKey] || !$row[$balanceColumnKey]) { - return; + foreach ($collection as $sheet_no => $sheet) { + foreach ($sheet as $row_no => $row) { + if ($row_no < $data_start_from || $row_no > $data_end_at) { + continue; } - $postingDate = carbon::createFromFormat('dM', trim($row[$dateColumnKey])); + $posting_date = Carbon::createFromFormat('d/m/Y', $row[0])->format('Y-m-d'); + $posting_time = Carbon::parse($row[1])->format('H:i:s'); + $transaction_description = trim($row[2]); + $transaction_description_2 = trim($row[3]); + $transaction_description_3 = trim($row[4]); + $transaction_description_4 = trim($row[5]); + $inward_amount = floatval(trim($row[6])); + $outward_amount = floatval(trim($row[7])); + $amount = $inward_amount == 0 ? $outward_amount : $inward_amount; - $description = trim($row[$descriptionColumnKey]); - - $nextRow = $sheet[$index + 1]; - - if (!$nextRow[$dateColumnKey] && $nextRow[$descriptionColumnKey]) { - $nextRowInfoArr = $this->extractInfoFromExcelCollection($nextRow); - foreach ($nextRowInfoArr as $col) { - if ($col) { - $description .= ' '. $col; - } - } + if ($inward_amount == 0) { + $total_debit += 1; + } else { + $total_credit += 1; } - $descriptionArr = explode(',', $description); + $balance = floatval(trim($row[8])); - $transactionDescription = array_key_exists(0, $descriptionArr) ? trim($descriptionArr[0]) : ""; - $description2 = array_key_exists(1, $descriptionArr) ? trim($descriptionArr[1]) : ""; - $description3 = array_key_exists(2, $descriptionArr) ? trim($descriptionArr[2]) : ""; - $description4 = array_key_exists(3, $descriptionArr) ? trim($descriptionArr[3]) : ""; - $description5 = array_key_exists(4, $descriptionArr) ? trim($descriptionArr[4]) : ""; - - if (count($descriptionArr) > 5) { - for ($i = 5; $i < count($descriptionArr); $i++) { - $description5 = array_key_exists($i, $descriptionArr) ? $description5 . " " . trim($descriptionArr[$i]) : $description5; - } - } - - $amount = $row[$creditColumnKey] ? ((float) str_replace(',', '', trim($row[$creditColumnKey]))) : (-((float) str_replace(',', '', $row[$debitColumnKey]))); - $endBalance = ((float) str_replace(',', '', $row[$balanceColumnKey])); - $transaction = new StatementTransaction([ - 'posting_date' => $postingDate, - 'transaction_description' => $transactionDescription, - 'transaction_description_2' => $description2, - 'transaction_description_3' => $description3, - 'transaction_description_4' => $description4, - 'transaction_description_5' => $description5, - 'amount' => $amount, - 'end_balance' => $endBalance, - ]); - - // Check if the transaction already exists for this statement - $existingTransaction = StatementTransaction::whereDate('posting_date', $postingDate) + // Check if the transaction already exists for any statement + $existingTransaction = StatementTransaction::where('posting_date', ($posting_date . ' ' . $posting_time)) ->where('amount', $amount) - ->where('transaction_description', $transactionDescription) - ->whereRaw("CAST(REPLACE(end_balance,',','') AS DECIMAL(15,2)) = ?",[$endBalance]) + ->where('transaction_description', $transaction_description) + ->whereRaw("CAST(REPLACE(end_balance,',','') AS DECIMAL(15,2)) = ?", [$balance]) ->first(); - - if (!$existingTransaction) { - $statement->transactions()->save($transaction); - } - return $transaction; - }); - }); + if (!$existingTransaction) { + $transaction = new StatementTransaction([ + 'account_statement_id' => $statement->id, + 'posting_date' => $posting_date . ' ' . $posting_time, + 'transaction_description' => $transaction_description, + 'transaction_description_2' => $transaction_description_2, + 'transaction_description_3' => $transaction_description_3, + 'transaction_description_4' => $transaction_description_4, + 'amount' => $amount, + 'end_balance' => $balance, + ]); + + $transaction->save(); + } + } + + $statement->total_amount = $total_debit ?: $total_credit; + $statement->save(); + } } return $this->response([]); } - - private function findInfoIndexFromExcelCollection($collection, $keyword) - { - foreach ($collection as $key => $row) { - if ($row instanceof Collection) { - $infoArr = $this->extractInfoFromExcelCollection($row); - foreach ($infoArr as $info) { - if (str_contains(strtolower($info), strtolower($keyword))) { - return $key; - } - } - } else { - if (str_contains(strtolower($row), strtolower($keyword))) { - return $key; - } - } - - } - } - - // return an array with information of the row start from 0 index - private function extractInfoFromExcelCollection($collection) - { - return array_values(array_filter($collection->toArray())); - } - } diff --git a/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue index f4aa76a2..723abb9f 100644 --- a/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue +++ b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue @@ -70,7 +70,10 @@ export default { } else if (this.statementType.includes('LITE')) { this.submit(this.route('api.accounting.statement.import.cief.lite'), 'post', this.section, true, false) } - } + }, + errorHandler(error){ + this.formHandler(error.message); + }, }, mixins: [formHandler] } diff --git a/resources/assets/vue/components/general/forms/FileUploadComponent.vue b/resources/assets/vue/components/general/forms/FileUploadComponent.vue index a7eceffd..a24ff071 100644 --- a/resources/assets/vue/components/general/forms/FileUploadComponent.vue +++ b/resources/assets/vue/components/general/forms/FileUploadComponent.vue @@ -81,7 +81,7 @@ url: '#', autoQueue: false, processQueue: false, - acceptedFiles: 'image/*, application/*', + acceptedFiles: 'image/*, application/*, text/csv', uploadMultiple: true, clickable: '.select-btn', previewTemplate: '
'