update on ambank csv file format

This commit is contained in:
Jia Sheng
2024-09-24 22:08:49 +08:00
parent 6cbc7539f4
commit c76587d08a
3 changed files with 118 additions and 145 deletions
@@ -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()));
}
}
@@ -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]
}
@@ -81,7 +81,7 @@
url: '#',
autoQueue: false,
processQueue: false,
acceptedFiles: 'image/*, application/*',
acceptedFiles: 'image/*, application/*, text/csv',
uploadMultiple: true,
clickable: '.select-btn',
previewTemplate: '<div class="row m-l-0 m-r-0 align-items-center m-t-5 m-b-5 bg-master-lightest text-left p-t-10 p-b-10 "> <div class="col-auto p-r-0"> <img data-dz-thumbnail style="width: 35px; height: 35px;" /> </div> <div class="col"> <div class="row m-b-5"> <div class="col"> <div class="dz-filename fs-8 bold"><span data-dz-name></span></div> </div> </div> <div class="row"> <div class="col"> <div class="dz-size muted light fs-10" data-dz-size></div> </div> </div> </div> <div class="col-auto"><i class="fs-16 fa fa-times-circle pointer hint-text" data-dz-remove></i></div> </div>'