diff --git a/app/Classes/General/Eloquent/Filters/IsMapped.php b/app/Classes/General/Eloquent/Filters/IsMapped.php new file mode 100644 index 00000000..12282d3b --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsMapped.php @@ -0,0 +1,20 @@ +whereHas('owners') : $builder->whereDoesntHave('owners'); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/IsMappedWithMultiple.php b/app/Classes/General/Eloquent/Filters/IsMappedWithMultiple.php new file mode 100644 index 00000000..066fa6ef --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsMappedWithMultiple.php @@ -0,0 +1,20 @@ +has('owners', '>', 1) : $builder->has('owners', '=',1); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/MaxAmount.php b/app/Classes/General/Eloquent/Filters/MaxAmount.php new file mode 100644 index 00000000..3a0476c8 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/MaxAmount.php @@ -0,0 +1,20 @@ +where('amount', '<=', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/MinAmount.php b/app/Classes/General/Eloquent/Filters/MinAmount.php new file mode 100644 index 00000000..52b72636 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/MinAmount.php @@ -0,0 +1,20 @@ +where('amount', '>=', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionAccountId.php b/app/Classes/General/Eloquent/Filters/StatementTransactionAccountId.php new file mode 100644 index 00000000..b07bede9 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionAccountId.php @@ -0,0 +1,22 @@ +whereHas('account', function ($query) use ($value) { + $query->where('statement_accounts.id', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerStatusIn.php b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerStatusIn.php new file mode 100644 index 00000000..e309cd4d --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerStatusIn.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->whereIn('status', $value); + }); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerTypeIn.php b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerTypeIn.php new file mode 100644 index 00000000..e23d0bcc --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerTypeIn.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->whereIn('type', $value); + }); + } + +} diff --git a/app/Classes/Jobs/CreateBankStatementDetails.php b/app/Classes/Jobs/CreateBankStatementTransactionOwners.php similarity index 53% rename from app/Classes/Jobs/CreateBankStatementDetails.php rename to app/Classes/Jobs/CreateBankStatementTransactionOwners.php index 9d12c6ba..e7797210 100644 --- a/app/Classes/Jobs/CreateBankStatementDetails.php +++ b/app/Classes/Jobs/CreateBankStatementTransactionOwners.php @@ -2,33 +2,20 @@ namespace App\Classes\Jobs; -use App\Classes\Modules\Accounting\Processors\CreateBankStatementDetailsProcessor; +use App\Classes\Modules\Accounting\Processors\CreateBankStatementTransactionOwnersProcessor; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use App\Models\AccountStatement; -class CreateBankStatementDetails implements ShouldQueue +class CreateBankStatementTransactionOwners implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - /** @var AccountStatement $statement*/ - private $statement; - - /** - * CreateBankStatementDetails constructor. - * @param AccountStatement $statement - */ - public function __construct(AccountStatement $statement) - { - $this->statement = $statement; - } - public function handle() { - (App()->make(CreateBankStatementDetailsProcessor::class))->execute($this->statement); + (App()->make(CreateBankStatementTransactionOwnersProcessor::class))->execute(); } public function delay($delay) diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php new file mode 100644 index 00000000..b7f737da --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php @@ -0,0 +1,147 @@ + 'Import Bank Statement Transactions Details', + 'message' => 'You have successfully updated the Bank Statement Transactions Details' + ]; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws MalformedRequestException + */ + public function logic(Request $request) : JsonResponse + { + + $files = $request->file('files'); + + $object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports'); + foreach ($object->getFiles() as $file){ + $collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true); + + $sheet = $collection->first()->skip(1); + + $statementDetails = $sheet->first(); + + $accountNumber = $statementDetails[0]; + $accountType = $statementDetails[1]; + $accountName = $statementDetails[2]; + $accountCurrency = $statementDetails[3]; + $dateFrom = carbon::parse(str_replace(' MY (UTC+08:00)', '', $statementDetails[4])); + $dateTo = carbon::parse(str_replace(' MY (UTC+08:00)', '', $statementDetails[5])); + $totalDebit = $statementDetails[6]; + $totalCredit = $statementDetails[7]; + $beginBalance = $statementDetails[8]; + $endBalance = $statementDetails[9]; + $account = StatementAccount::updateOrCreate( + ['number' => $accountNumber], + [ + 'type' => $accountType, + 'name' => $accountName, + 'currency' => $accountCurrency, + ] + ); + + $statement = AccountStatement::where('date_from', $dateFrom) + ->where('date_to', $dateTo) + ->where('total_amount', $totalDebit ?: $totalCredit,) + ->where('begin_balance', $beginBalance) + ->where('end_balance', $endBalance)->first(); + + + if(!$statement){ + $statement = new AccountStatement([ + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + 'total_amount' => $totalDebit ?: $totalCredit, + 'begin_balance' => $beginBalance, + 'end_balance' => $endBalance, + ]); + } + + $account->statements()->save($statement); + + CreateBankStatementTransactionOwners::dispatch($statement); + + + $sheet->map(function ($row) use ($statement, $account) { + $transactionRef = $row[15]; + $amount = $row[17] !== '-' ? ((float) str_replace(',', '', $row[17])) : (-((float) str_replace(',', '', $row[16]))); + $transactionDate = $row[10] !== '-' ? carbon::parse(str_replace(' MY (UTC+08:00)', '', $row[10]) . $row[11]) : null; + $postingDate = carbon::createFromFormat('d/M/Y H:i', str_replace(' MY (UTC+08:00)', '', $row[12]) . str_replace(' MY (UTC+08:00)', '', $row[13])); + $transactionDescription = is_numeric($row[14]) ? (int) sprintf('%.2f', $row[14]) : $row[14]; + $tellerId = $row[19]; + $branchChannel = $row[20]; + $transactionCode = $row[21]; + $endBalance = $row[22]; + $description2 = $row[25]; + $description3 = $row[26]; + $description4 = $row[27]; + $description5 = $row[28]; + $transaction = new StatementTransaction([ + 'transaction_ref' => $transactionRef, + 'amount' => $amount, + 'transaction_date' => $transactionDate, + 'posting_date' => $postingDate, + 'transaction_description' => $transactionDescription, + 'teller_id' => $tellerId, + 'branch_channel' => $branchChannel, + 'transaction_code' => $transactionCode, + 'end_balance' => $endBalance, + 'transaction_description_2' => $description2, + 'transaction_description_3' => $description3, + 'transaction_description_4' => $description4, + 'transaction_description_5' => $description5, + ]); + + // Check if the transaction already exists for this statement + $existingTransaction = StatementTransaction::where('transaction_ref', $transactionRef) + ->where('posting_date', $postingDate) + ->where('amount', $amount) + ->where('transaction_description', $transactionDescription) + ->where('teller_id', $tellerId) + ->where('branch_channel', $branchChannel) + ->where('transaction_code', $transactionCode) + ->where('end_balance', $endBalance) + ->first(); + + if (!$existingTransaction) { + $statement->transactions()->save($transaction); + } + + return $transaction; + }); + } + + + + return $this->response([]); + + } + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementTransactionsLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementTransactionsLogic.php new file mode 100644 index 00000000..3d2b8273 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementTransactionsLogic.php @@ -0,0 +1,53 @@ + 'Retrieved Bank Statement Transactions', + 'message' => 'You have successfully retrieved a Bank Statement Transactions' + ]; + } + + + /** @var ListsBankStatementTransactions */ + private $listsBankStatementTransactions; + + /** + * @param ListsBankStatementTransactions $listsBankStatementTransactions + */ + public function __construct(ListsBankStatementTransactions $listsBankStatementTransactions) + { + $this->listsBankStatementTransactions = $listsBankStatementTransactions; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsBankStatementTransactions->execute($this->listsBankStatementTransactions->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(BankStatementTransactionResource::collection($query)); + } + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php index bbdeef35..f29bd627 100644 --- a/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php +++ b/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php @@ -56,8 +56,6 @@ class UpdateBankStatementDetailLogic extends AbstractControllerLogic { $object = new BankStatementDetailObject($request->input('pay_for'), $request->input('system_references')); - // $this->canUpdateCompany->passes($object); - $query = $this->fetchesBankStatementDetails->execute(['id' => $request->route('id')]); $query = $this->updatesBankStatementDetails->execute($query, $object); diff --git a/app/Classes/Modules/Accounting/Processors/CreateBankStatementDetailsProcessor.php b/app/Classes/Modules/Accounting/Processors/CreateBankStatementDetailsProcessor.php deleted file mode 100644 index a978fb33..00000000 --- a/app/Classes/Modules/Accounting/Processors/CreateBankStatementDetailsProcessor.php +++ /dev/null @@ -1,212 +0,0 @@ -transactions(); - $transactions = $transactions->get(); - - $headers = [ - 'Date', - 'Bank', - 'Description', - 'Credit', - 'Debit', - 'Pay For', - 'System', - 'System Reference', - 'Human Reference', - 'Multiple', - 'Match?', - 'System Amount' - ]; - - $branches = [ - 0 => 'MBB Cyber', - 1 => 'MBB SS2', - ]; - - $yes = 'Yes'; - $no = 'No'; - - $table = ''; - $count = 0; - - foreach ($transactions as $row) { - $existingRecord = StatementTransactionsDetail::where('statement_transaction_id', $row->id)->first(); - if ($existingRecord && $existingRecord->pay_for != "") { - continue; - } - - $count++; - $credit = 0.00; - $debit = 0.00; - - $date = new DateTime($row['posting_date']); - $description = $row['transaction_description_2']; - - if($row['amount'] < 0){ - $debit = (float) $row['amount']; - } - else{ - $credit = (float) $row['amount']; - } - - $creditTransactions = []; - $debitTransactions = []; - - $system = []; - $systemReference = null; - $systemAmount = null; - - if($credit){ - $creditTransactions = $this->getTransactions($date, $credit, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); - foreach ($creditTransactions as $transaction) { - $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no; - $systemAmount[] = $transaction->amount; - $system[] = 'EXCHANGE'; - } - - $creditTransactions = $this->getTransactions($date, $credit, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); - foreach ($creditTransactions as $transaction) { - $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no; - $systemAmount[] = $transaction->amount; - $system[] = 'EXCHANGE'; - } - - $creditTransactions = $this->getTransactionsFromShippingPortal($credit, $this->getDateRange($row['posting_date'])); - foreach ($creditTransactions as $transaction) { - $systemReference[] = $transaction['order']['reference']; - $systemAmount[] = $transaction['amount']; - $system[] = 'SHIPPING'; - } - } - - if($debit){ - $debitTransactions = $this->getTransactions($date, $debit, null, null, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], Group::class); - - if(!count($debitTransactions)) { - foreach (['YSN', 'HCK', 'ATVANTIC', 'HIGH HILL'] as $reference){ - if(str_contains($description, $reference)) { - $paymentDate = date('Y-m-d', strtotime('+1 day', strtotime($row['posting_date']))); //$date->addDays(1)->format('Y-m-d'); - - if($reference = 'ATVANTIC'){ - $paymentDate = date('Y-m-d', strtotime($row['posting_date']));//$date->format('Y-m-d'); - } - $issuer = Company::where('name', 'like', '%'.$reference.'%')->get()->pluck('id'); - $debitTransactions = Group::whereIn('issuer', $issuer)->whereDate('created_at', $paymentDate)->get(); - break; - } - } - - } - - foreach ($debitTransactions as $transaction) { - $systemReference[] = $transaction->reference; - $systemAmount[] = $transaction->amount; - $system[] = 'EXCHANGE'; - } - } - - - $multiple = count($creditTransactions) + count($debitTransactions) > 1 ? $yes : $no; - - $systemReference = $systemReference ? implode(',', $systemReference) : null; - $systemAmount = $systemAmount ? implode(',', $systemAmount) : null; - $system = $system ? implode(',', $system) : null; - - $matches = $systemReference == $row['remarkreferences'] ? $yes : $no; - - StatementTransactionsDetail::updateOrCreate( - ['statement_transaction_id' => $row->id], - [ - 'date' => $date, - 'pay_for' => is_null($system) ? "" : $system, - 'system_references' => is_null($systemReference) ? "" : $systemReference, - 'system_amounts'=> is_null($systemAmount) ? "" : $systemAmount, - 'remark_references'=> is_null($row['remarkreferences']) ? "" : $row['remarkreferences'], - ] - ); - - // if($count == 10){ - // break; - // } - } - } - - private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $model = Transaction::class) { - $query = $model::whereIn('status', $statuses) - ->where(function ($query) use ($ownerType, $paymentMethod, $type) { - if ($ownerType) { - $query->where('owner_type', $ownerType); - } - - if ($paymentMethod) { - $query->where('payment_method', '!=', $paymentMethod); - } - - if ($type) { - $query->where('type', $type); - } - }) - ->whereDate('created_at', $date->format('Y-m-d')) - ->where('amount', '>', ($amount - 0.01)) - ->where('amount', '<', ($amount + 0.01)); - - return $query->get(); - } - - private function getTransactionsFromShippingPortal($amount, $dateRange){ - try{ - - $client = new \GuzzleHttp\Client(); - $response = $client->request('GET', 'https://izyim.cief-malaysia.com/public/api/v1/list?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2],"type":2,"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).'}'); - $body = $response->getBody(); - $data = json_decode($body, true); - $payload = $data['payload']; - $transactions2 = $payload['data']; - return $transactions2; - }catch(\Exception $exception){ - Log::error($exception); - return []; - } - } - - private function getDateRange(string $dateStr) { - // Create a DateTime object from the input string - $date = strtotime($dateStr); - - // Get the first day of the month - $today = date('Y-m-d', $date); - - // Get the first day of the next month - $nextDay = date('Y-m-d', strtotime('+1 day', $date)); - - return [ - 'start_date' => $today, - 'end_date' => $nextDay, - ]; - } -} diff --git a/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php new file mode 100644 index 00000000..f3d61595 --- /dev/null +++ b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php @@ -0,0 +1,275 @@ +whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); +// })->get(); + + $transactions = StatementTransaction::whereDoesntHave('owners')->where('amount', '<', 0)->get(); + + foreach ($transactions as $transaction) { + + if($transaction->amount > 0){ + + // Exchange Sales + $creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $creditTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::SALES, + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id'=> $creditTransaction->id, + ]); + } + + + // Shipping Portal Sales + $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date)); + foreach ($creditTransactions as $creditTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::SALES, + 'system' => 'SHIPPING_PORTAL', + 'owner_type' => Transaction::class, + 'owner_id'=> $creditTransaction['id'], + ]); + } + + // Exchange Wallet Top Up + $creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $creditTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::WALLET_TOP_UP, + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id'=> $creditTransaction->id, + ]); + } + + // fpx charge refund + if($transaction->transaction_description === 'DUITNOW S/CHRG REFUND'){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::FPX_CHARGE_REFUND + ]); + } + + // Customer Refund + + // INTERNAL_BANK_TRANSFER_IN + if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_IN + ]); + } + + + } + + if($transaction->amount < 0){ + + // Supplier Purchase Order Payments + foreach (['YSN', 'HCK', 'ATVANTIC', 'HIGH HILL'] as $reference){ + if(str_contains($transaction->transaction_description.' '.$transaction->transaction_description_2.' '.$transaction->transaction_description_3.' '.$transaction->transaction_description_4.' '.$transaction->transaction_description_5 , $reference)) { + $paymentDateStart = $transaction->posting_date->startOfDay()->subDays(1); + $paymentDateEnd = $transaction->posting_date->endOfDay(); + + if($paymentDateStart->dayOfWeek === Carbon::SUNDAY){ + $paymentDateStart->subDays(2); + } + $issuer = Company::where('name', 'like', '%'.$reference.'%')->get()->pluck('id'); + + $debitTransactions = Group::whereIn('issuer', $issuer)->where('amount', '>=', (($transaction->amount * -1) - 0.01)) + ->where('amount', '<=', (($transaction->amount * -1) + 0.01))->whereDate('created_at', '>=', $paymentDateStart)->whereDate('created_at', '<=', $paymentDateEnd)->get(); + + foreach ($debitTransactions as $debitTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::SUPPLIER_PAYMENT, + 'system' => 'EXCHANGE', + 'owner_type' => Group::class, + 'owner_id'=> $debitTransaction->id, + ]); + } + + } + } + + // Exchange Wallet Withdrawal + $debitTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::DEBIT_NOTE, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($debitTransactions as $debitTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::WALLET_WITHDRAWAL, + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id'=> $debitTransaction->id, + ]); + } + + // SALARY + + // STATUTORY + if(str_contains($transaction->transaction_description_2, 'PEMBANGUNAN SUMBER') || str_contains($transaction->transaction_description_2, 'HASIL') || str_contains($transaction->transaction_description_2, 'PERTUBUHAN KESELAMAT') || str_contains($transaction->transaction_description_2, 'KUMPULAN WANG SIMPAN')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::STATUTORY + ]); + } + + // FPX_CHARGE + if($transaction->transaction_description === 'DR DUITNOW S/CHRG' || str_contains($transaction->transaction_description, 'Manual FPX') || str_contains($transaction->transaction_description, 'CMS - DR FPX CHG')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::FPX_CHARGE + ]); + } + + // BANK_CHARGE + if($transaction->transaction_description === 'CMS - DR CORP CHG' || $transaction->transaction_description === 'MONTHLY PROFIT DEBIT'){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::BANK_CHARGE + ]); + } + + // CREDIT_CARD_PAYMENT + if(str_contains($transaction->transaction_description_2, 'VISA CARD')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::CREDIT_CARD_PAYMENT + ]); + } + + // INTERNAL_BANK_TRANSFER_OUT + if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE') || str_contains($transaction->transaction_description_2, 'CIEF WORLWIDE') || str_contains($transaction->transaction_description_2, 'IZYIM GLOBAL')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_OUT + ]); + } + + // non-operational charges + if(str_contains($transaction->transaction_description_2, 'HIRE PURCHASE') || str_contains($transaction->transaction_description_2, 'TENAGA NASIONAL') || str_contains($transaction->transaction_description, 'CABLE CHARGE') || str_contains($transaction->transaction_description_2, 'CTOS DATA SYSTEMS') || str_contains($transaction->transaction_description_2, 'MAXIS')){ + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::NON_OPERATIONAL + ]); + } + } + } + } + + public function get(int $statementTransactionId){ + + $owners = [ + 'data' => [] + ]; + + $statementTransaction = StatementTransaction::find($statementTransactionId); + + if($statementTransaction->amount > 0) { + $creditTransactions = $this->getTransactions($statementTransaction->posting_date, $statementTransaction->amount, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $transaction) { + $owners['data'][] = [ + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id' => $transaction->id, + 'bill_no' => $transaction->bill_no + ]; + } + + $creditTransactions = $this->getTransactions($statementTransaction->posting_date, $statementTransaction->amount, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + foreach ($creditTransactions as $transaction) { + $owners[] = [ + 'system' => 'EXCHANGE', + 'owner_type' => Transaction::class, + 'owner_id' => $transaction->id, + 'bill_no' => $transaction->bill_no + ]; + } + + $creditTransactions = $this->getTransactionsFromShippingPortal($statementTransaction->amount, $this->getDateRange($statementTransaction->posting_date)); + foreach ($creditTransactions as $transaction) { + $owners[] = [ + 'system' => 'IZYIM', + 'owner_type' => Transaction::class, + 'owner_id' => $transaction->id, + 'bill_no' => $transaction->bill_no + ]; + } + } + + return $owners; + } + + private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $model = Transaction::class) { + $query = $model::whereIn('status', $statuses) + ->where(function ($query) use ($ownerType, $paymentMethod, $type) { + if ($ownerType) { + $query->where('owner_type', $ownerType); + } + + if ($paymentMethod) { + $query->where('payment_method', '!=', $paymentMethod); + } + + if ($type) { + $query->where('type', $type); + } + }) + ->whereDate('created_at', $date->format('Y-m-d')) + ->where('amount', '>', ($amount - 0.01)) + ->where('amount', '<', ($amount + 0.01)); + + return $query->get(); + } + + private function getTransactionsFromShippingPortal($amount, $dateRange){ + try{ + + $client = new \GuzzleHttp\Client(['verify' => false]); + $response = $client->request('GET', 'https://izyim.cief-malaysia.com/public/api/v1/list?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2],"type":2,"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).'}'); + $body = $response->getBody(); + $data = json_decode($body, true); + $payload = $data['payload']; + $transactions2 = $payload['data']; + return $transactions2; + }catch(\Exception $exception){ + Log::error($exception); + return []; + } + } + + private function getDateRange(string $dateStr) { + // Create a DateTime object from the input string + $date = strtotime($dateStr); + + // Get the first day of the month + $today = date('Y-m-d', strtotime('-1 day', $date)); + + // Get the first day of the next month + $nextDay = date('Y-m-d', strtotime('+1 day', $date)); + + return [ + 'start_date' => $today, + 'end_date' => $nextDay, + ]; + } +} diff --git a/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php index 37a3d785..22de5c39 100644 --- a/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php +++ b/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php @@ -5,20 +5,20 @@ namespace App\Classes\Modules\Accounting\Services; use App\Classes\General\Eloquent\AbstractFetchRecord; use Illuminate\Database\Eloquent\Builder; -use App\Models\StatementTransactionsDetail; +use App\Models\StatementTransactionOwner; class FetchesBankStatementDetails extends AbstractFetchRecord { - /** @var StatementTransactionsDetail */ + /** @var StatementTransactionOwner */ private $repository; /** * FetchesBankStatementDetails constructor. - * @param StatementTransactionsDetail $repository + * @param StatementTransactionOwner $repository */ - public function __construct(StatementTransactionsDetail $repository) + public function __construct(StatementTransactionOwner $repository) { $this->repository = $repository; } diff --git a/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php index 2f1290e5..6e085e44 100644 --- a/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php +++ b/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php @@ -4,19 +4,19 @@ namespace App\Classes\Modules\Accounting\Services; use App\Classes\General\Eloquent\AbstractListRecord; use Illuminate\Database\Eloquent\Builder; -use App\Models\StatementTransactionsDetail; +use App\Models\StatementTransactionOwner; class ListsBankStatementDetails extends AbstractListRecord { - /** @var StatementTransactionsDetail */ + /** @var StatementTransactionOwner */ private $repository; /** * ListsBankStatementDetails constructor. - * @param StatementTransactionsDetail $repository + * @param StatementTransactionOwner $repository */ - public function __construct(StatementTransactionsDetail $repository) + public function __construct(StatementTransactionOwner $repository) { $this->repository = $repository; } diff --git a/app/Classes/Modules/Accounting/Services/ListsBankStatementTransactions.php b/app/Classes/Modules/Accounting/Services/ListsBankStatementTransactions.php new file mode 100644 index 00000000..8d4fbf69 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/ListsBankStatementTransactions.php @@ -0,0 +1,33 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php index 2e4ad23c..e87121ac 100644 --- a/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php +++ b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php @@ -4,18 +4,18 @@ namespace App\Classes\Modules\Accounting\Services; use App\Classes\General\Eloquent\AbstractUpdateRecord; use App\Classes\Modules\Accounting\DataTransferObjects\BankStatementDetailObject; -use App\Models\StatementTransactionsDetail; +use App\Models\StatementTransactionOwner; class UpdatesBankStatementDetails extends AbstractUpdateRecord { /** - * @param StatementTransactionsDetail $model + * @param StatementTransactionOwner $model * @param BankStatementDetailsObject $object * @return \Illuminate\Database\Eloquent\Model * @throws \App\Classes\Exceptions\MalformedRequestException */ - public function execute(StatementTransactionsDetail $model, BankStatementDetailObject $object) + public function execute(StatementTransactionOwner $model, BankStatementDetailObject $object) { $model->system_references = $object->getSystemReferences(); $model->pay_for = $object->getPayFor(); diff --git a/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php b/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php index 736853fc..2a425f7d 100644 --- a/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php +++ b/app/Classes/Modules/Documents/DataTransferObjects/FileObject.php @@ -28,7 +28,7 @@ class FileObject implements DataTransferObject */ public function getData() { - return in_array($this->getExtension(), ['pdf', 'excel']) ? $this->data : (new imageManager())->make($this->data); + return in_array($this->getExtension(), ['pdf', 'excel', 'text']) ? $this->data : (new imageManager())->make($this->data); } /** @@ -66,7 +66,7 @@ class FileObject implements DataTransferObject */ public function getDecodedData(): string { - return in_array($this->getExtension(), ['pdf', 'excel']) ? + return in_array($this->getExtension(), ['pdf', 'excel', 'text']) ? base64_decode((explode('base64,', $this->getData()))[1]): $this->getData()->encode('data-url')->encoded; } @@ -83,4 +83,4 @@ class FileObject implements DataTransferObject -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php b/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php index 8741b823..0e0cf6b9 100644 --- a/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php +++ b/app/Classes/Modules/Documents/Services/ConvertsBase64ToFile.php @@ -36,7 +36,7 @@ class ConvertsBase64ToFile foreach ($files as $file) { $object = new FileObject($file); - in_array($object->getExtension(), ['pdf', 'excel']) ? $this->generatePDF($object) : $this->generateImage($object); + in_array($object->getExtension(), ['pdf', 'excel', 'text']) ? $this->generatePDF($object) : $this->generateImage($object); } @@ -122,4 +122,4 @@ class ConvertsBase64ToFile ]); } -} \ No newline at end of file +} diff --git a/app/Classes/ValueObjects/Constants/FileType.php b/app/Classes/ValueObjects/Constants/FileType.php index d5404e7a..fc6dbf1b 100644 --- a/app/Classes/ValueObjects/Constants/FileType.php +++ b/app/Classes/ValueObjects/Constants/FileType.php @@ -23,6 +23,7 @@ class FileType 'application/pdf' => 'pdf', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'excel', 'application/vnd.ms-excel' => 'excel', + 'text/plain' => 'text', ]; -} \ No newline at end of file +} diff --git a/app/Classes/ValueObjects/Constants/StatementTransactionOwnerType.php b/app/Classes/ValueObjects/Constants/StatementTransactionOwnerType.php new file mode 100644 index 00000000..24d31516 --- /dev/null +++ b/app/Classes/ValueObjects/Constants/StatementTransactionOwnerType.php @@ -0,0 +1,37 @@ +paginate(10); + $statements = $statementsQuery->orderBy('date_from')->paginate(10); return view('pages.accounting.bank-statements.index', compact('accounts', 'selectedAccount', 'search', 'statements')); } - public function import(Request $request) + public function indexv2(Request $request) { + $selectedAccount = $request->input('account'); + $search = $request->input('search'); - $request->validate([ - 'file' => 'required|mimes:csv,txt' - ]); + $accounts = StatementAccount::all(); - $file = $request->file('file'); + $statementsQuery = AccountStatement::query(); - $collection = Excel::toCollection(null, $file, null, null, true); - - $sheet = $collection->first()->skip(1); - - $statementDetails = $sheet->first(); - - $accountNumber = $statementDetails[0]; - $accountType = $statementDetails[1]; - $accountName = $statementDetails[2]; - $accountCurrency = $statementDetails[3]; - $dateFrom = carbon::parse(str_replace(' MY (UTC+08:00)', '', $statementDetails[4])); - $dateTo = carbon::parse(str_replace(' MY (UTC+08:00)', '', $statementDetails[5])); - $totalDebit = $statementDetails[6]; - $totalCredit = $statementDetails[7]; - $beginBalance = $statementDetails[8]; - $endBalance = $statementDetails[9]; - $account = StatementAccount::updateOrCreate( - ['number' => $accountNumber], - [ - 'type' => $accountType, - 'name' => $accountName, - 'currency' => $accountCurrency, - ] - ); - - $statement = AccountStatement::where('date_from', $dateFrom) - ->where('date_to', $dateTo) - ->where('total_amount', $totalDebit ?: $totalCredit,) - ->where('begin_balance', $beginBalance) - ->where('end_balance', $endBalance)->first(); - - - if(!$statement){ - $statement = new AccountStatement([ - 'date_from' => $dateFrom, - 'date_to' => $dateTo, - 'total_amount' => $totalDebit ?: $totalCredit, - 'begin_balance' => $beginBalance, - 'end_balance' => $endBalance, - ]); + if ($selectedAccount) { + $statementsQuery->where('statement_account_id', $selectedAccount); } - // $existingStatement = AccountStatement::where('date_from', $dateFrom) - // ->where('date_to', $dateTo) - // ->where('statement_account_id', $account->id) - // ->first(); + if ($search) { + $statementsQuery->where(function ($query) use ($search) { + $query->where('date_from', 'LIKE', "%$search%") + ->orWhere('date_to', 'LIKE', "%$search%") + ->orWhere('total_amount', 'LIKE', "%$search%") + ->orWhere('begin_balance', 'LIKE', "%$search%") + ->orWhere('end_balance', 'LIKE', "%$search%"); + }); + } - // // if ($existingStatement) { - // // return redirect()->back()->with('error', 'This statement has already been imported.'); - // // } + $statements = $statementsQuery->paginate(10); - $account->statements()->save($statement); - - CreateBankStatementDetails::dispatch($statement)->delay(30); - - - // $account = null; - $newTransactions = $sheet->map(function ($row) use ($statement, $account) { - $transactionRef = $row[15]; - $amount = $row[17] !== '-' ? ((float) str_replace(',', '', $row[17])) : (-((float) str_replace(',', '', $row[16]))); - $transactionDate = $row[10] !== '-' ? carbon::parse(str_replace(' MY (UTC+08:00)', '', $row[10]) . $row[11]) : null; - $postingDate = carbon::createFromFormat('d/M/Y H:i', str_replace(' MY (UTC+08:00)', '', $row[12]) . str_replace(' MY (UTC+08:00)', '', $row[13])); - $transactionDescription = is_numeric($row[14]) ? (int) sprintf('%.2f', $row[14]) : $row[14]; - $tellerId = $row[19]; - $branchChannel = $row[20]; - $transactionCode = $row[21]; - $endBalance = $row[22]; - $description2 = $row[25]; - $description3 = $row[26]; - $description4 = $row[27]; - $description5 = $row[28]; - $transaction = new StatementTransaction([ - 'transaction_ref' => $transactionRef, - 'amount' => $amount, - 'transaction_date' => $transactionDate, - 'posting_date' => $postingDate, - 'transaction_description' => $transactionDescription, - 'teller_id' => $tellerId, - 'branch_channel' => $branchChannel, - 'transaction_code' => $transactionCode, - 'end_balance' => $endBalance, - 'transaction_description_2' => $description2, - 'transaction_description_3' => $description3, - 'transaction_description_4' => $description4, - 'transaction_description_5' => $description5, - ]); - - // Check if the transaction already exists for this statement - $existingTransaction = StatementTransaction::where('transaction_ref', $transactionRef) - ->where('posting_date', $postingDate) - ->where('amount', $amount) - ->where('transaction_description', $transactionDescription) - ->where('teller_id', $tellerId) - ->where('branch_channel', $branchChannel) - ->where('transaction_code', $transactionCode) - ->where('end_balance', $endBalance) - ->first(); - - if (!$existingTransaction) { - $statement->transactions()->save($transaction); - } - - return $transaction; - }); - - - // dd(json_encode($newTransactions)); - return redirect()->back()->with('success', 'Statement imported successfully.')->with('newTransactions', $newTransactions); + return view('pages.accounting.bank-statements.indexv2', compact('accounts', 'selectedAccount', 'search', 'statements')); } - public function rerun(AccountStatement $statement, Request $request) + public function import(Request $request, ImportBankStatementLogic $logic): JsonResponse { - CreateBankStatementDetails::dispatch($statement)->delay(30); + return $logic->execute($request); + } + + public function rerun() + { + CreateBankStatementTransactionOwners::dispatch(); return redirect()->back()->with('success', 'Rerun triggered successfully'); } @@ -227,6 +134,11 @@ class BankStatementController extends Controller return $logic->execute($request); } + public function transactions(Request $request, ListBankStatementTransactionsLogic $logic): JsonResponse + { + return $logic->execute($request); + } + public function update(Request $request, UpdateBankStatementDetailLogic $logic): JsonResponse { return $logic->execute($request); @@ -264,7 +176,7 @@ class BankStatementController extends Controller $count = 0; foreach ($transactions as $row) { - $isExist = StatementTransactionsDetail::where('statement_transaction_id', $row->id)->first(); + $isExist = StatementTransactionOwner::where('statement_transaction_id', $row->id)->first(); if ($isExist) { continue; @@ -371,7 +283,7 @@ class BankStatementController extends Controller //AccountStatement // $row->statement()->first()->id) - $statementTransactionsDetail = new StatementTransactionsDetail([ + $statementTransactionsDetail = new StatementTransactionOwner([ 'date' => $date, 'statement_transaction_id' => $row->id, 'description' => is_null($description) ? "" : $description, diff --git a/app/Http/Resources/BankStatementDetailResource.php b/app/Http/Resources/BankStatementDetailResource.php deleted file mode 100644 index 5533728c..00000000 --- a/app/Http/Resources/BankStatementDetailResource.php +++ /dev/null @@ -1,37 +0,0 @@ - $this->id, - 'account_number' => $this->statementTransaction->statement->account->number, - 'account_type' => $this->statementTransaction->statement->account->type, - 'account_name' => $this->statementTransaction->statement->account->name, - 'account_statement_id' => $this->statementTransaction->statement->id, - 'account_statement_date_from' => $this->statementTransaction->statement->date_from, - 'account_statement_date_to' => $this->statementTransaction->statement->date_to, - 'date' => $this->date, - 'description' => $this->description, - 'amount' => $this->statementTransaction->amount, - 'pay_for' => $this->pay_for, - 'system_references' => $this->system_references, - 'transaction_description_1' => $this->statementTransaction->transaction_description, - 'transaction_description_2' => $this->statementTransaction->transaction_description_2, - 'transaction_description_3' => $this->statementTransaction->transaction_description_3, - 'transaction_description_4' => $this->statementTransaction->transaction_description_4, - 'transaction_description_5' => $this->statementTransaction->transaction_description_5, - ]; - } -} diff --git a/app/Http/Resources/BankStatementTransactionOwnerResource.php b/app/Http/Resources/BankStatementTransactionOwnerResource.php new file mode 100644 index 00000000..0066eafa --- /dev/null +++ b/app/Http/Resources/BankStatementTransactionOwnerResource.php @@ -0,0 +1,65 @@ +system === 'EXCHANGE') { + if($this->owner_type === Transaction::class){ + $transaction = Transaction::find($this->owner_id); + + if($transaction->owner_type === Booking::class){ + $reference = $transaction->owner->marking; + $referenceLink = route('booking.details', $transaction->owner->marking); + } + + if($transaction->owner_type === Wallet::class) { + $reference = $transaction->owner->owner->reference; + $referenceLink = route('wallet.details', $transaction->owner->owner->reference); + } + } + + if($this->owner_type === Group::class){ + $transaction = Group::find($this->owner_id); + $reference = $transaction->reference; +// $referenceLink = route('booking.details', $transaction->owner->marking); + } + } + + if($this->system === 'SHIPPING_PORTAL') { + if($this->owner_type === Transaction::class){ + $transaction = Transaction::find($this->owner_id); + + } + } + + return [ + 'id' => $this->id, + 'type' => $this->type, + 'system' => $this->system, + 'owner_type' => $this->owner_type, + 'owner_id' => $this->owner_id, + 'reference_link' => $referenceLink, + 'reference' => $reference, + 'invoice_reference' => $this->invoice_reference, + 'receipt_reference' => $this->receipt_reference, + 'status' => $this->status + ]; + } +} diff --git a/app/Http/Resources/BankStatementTransactionResource.php b/app/Http/Resources/BankStatementTransactionResource.php new file mode 100644 index 00000000..09768f51 --- /dev/null +++ b/app/Http/Resources/BankStatementTransactionResource.php @@ -0,0 +1,36 @@ + $this->id, + 'account_number' => $this->statement->account->number, + 'account_type' => $this->statement->account->type, + 'account_name' => $this->statement->account->name, + 'account_statement_id' => $this->statement->id, + 'account_statement_date_from' => $this->statement->date_from, + 'account_statement_date_to' => $this->statement->date_to, + 'posting_date' => $this->posting_date->format('d-m-Y g:i A'), + 'amount' => $this->amount, + 'transaction_description_1' => $this->transaction_description, + 'transaction_description_2' => $this->transaction_description_2, + 'transaction_description_3' => $this->transaction_description_3, + 'transaction_description_4' => $this->transaction_description_4, + 'transaction_description_5' => $this->transaction_description_5, + 'owners' => BankStatementTransactionOwnerResource::collection($this->owners) + ]; + } +} diff --git a/app/Models/StatementTransaction.php b/app/Models/StatementTransaction.php index 6b789f08..80d6759f 100644 --- a/app/Models/StatementTransaction.php +++ b/app/Models/StatementTransaction.php @@ -4,9 +4,12 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Staudenmeir\EloquentHasManyDeep\HasRelationships; class StatementTransaction extends Model { + use HasRelationships; + use HasFactory; protected $fillable = [ @@ -30,18 +33,18 @@ class StatementTransaction extends Model 'posting_date' => 'datetime', ]; + public function account() + { + return $this->hasOneDeep(StatementAccount::class, [AccountStatement::class], ['id', 'id'], ['account_statement_id', 'statement_account_id']); + } + public function statement() { return $this->belongsTo(AccountStatement::class, 'account_statement_id', 'id'); } - public function owner() + public function owners() { - return $this->morphTo()->nullable(); - } - - public function statementDetail() - { - return $this->hasMany(StatementTransactionsDetail::class); + return $this->hasMany(StatementTransactionOwner::class); } } diff --git a/app/Models/StatementTransactionsDetail.php b/app/Models/StatementTransactionOwner.php similarity index 50% rename from app/Models/StatementTransactionsDetail.php rename to app/Models/StatementTransactionOwner.php index 074f5304..1db5404d 100644 --- a/app/Models/StatementTransactionsDetail.php +++ b/app/Models/StatementTransactionOwner.php @@ -4,26 +4,24 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; -class StatementTransactionsDetail extends Model +class StatementTransactionOwner extends Model { use HasFactory; protected $fillable = [ - 'date', 'statement_transaction_id', - // 'description', - // 'credit', - // 'debit', - 'pay_for', - 'system_references', - // 'remark_references', - // 'is_multiple', - // 'is_matches', - 'system_amounts', + 'type', + 'system', + 'owner_type', + 'owner_id', + 'invoice_reference', + 'receipt_reference', + 'status', ]; - public function statementTransaction() + public function transaction(): BelongsTo { return $this->belongsTo(StatementTransaction::class, 'statement_transaction_id', 'id'); } diff --git a/database/migrations/2023_03_26_190934_create_statement_transactions_table.php b/database/migrations/2023_03_26_190934_create_statement_transactions_table.php index 95ad3571..29cacb80 100644 --- a/database/migrations/2023_03_26_190934_create_statement_transactions_table.php +++ b/database/migrations/2023_03_26_190934_create_statement_transactions_table.php @@ -29,11 +29,6 @@ class CreateStatementTransactionsTable extends Migration $table->string('branch_channel'); $table->string('transaction_code'); $table->string('end_balance')->nullable(); - $table->string('owner_system')->nullable(); - $table->string('owner_type')->nullable(); - $table->unsignedBigInteger('owner_id')->nullable(); - $table->string('invoice_reference')->nullable(); - $table->string('payment_reference')->nullable(); $table->timestamps(); $table->foreign('account_statement_id')->references('id')->on('account_statements'); diff --git a/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php b/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php new file mode 100644 index 00000000..50d3a9c8 --- /dev/null +++ b/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php @@ -0,0 +1,44 @@ +id(); + $table->foreignId('statement_transaction_id'); + $table->integer('type')->default(StatementTransactionOwnerType::UNKNOWN); + $table->string('system')->nullable(); + $table->string('owner_type')->nullable(); + $table->bigInteger('owner_id')->nullable(); + $table->string('invoice_reference')->nullable(); + $table->string('receipt_reference')->nullable(); + $table->string('is_auto_mapped')->default(true); + $table->integer('status')->default(ApprovalStatus::PENDING_VERIFICATION); + + $table->foreign('statement_transaction_id')->references('id')->on('statement_transactions'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('statement_transaction_owners'); + } +} diff --git a/database/migrations/2023_04_07_212512_create_statement_transactions_details_table.php b/database/migrations/2023_04_07_212512_create_statement_transactions_details_table.php deleted file mode 100644 index 3bc547fd..00000000 --- a/database/migrations/2023_04_07_212512_create_statement_transactions_details_table.php +++ /dev/null @@ -1,43 +0,0 @@ -id(); - $table->date('date'); - $table->unsignedBigInteger('statement_transaction_id'); - $table->foreign('statement_transaction_id')->references('id')->on('statement_transactions'); - // $table->string('description'); - // $table->decimal('credit', 8, 2); - // $table->decimal('debit', 8, 2); - $table->string('pay_for'); - $table->string('system_references'); - // $table->string('remark_references'); - // $table->boolean('is_multiple')->default(false); - // $table->boolean('is_matches')->default(false); - $table->string('system_amounts'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('statement_transactions_details'); - } -} diff --git a/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue new file mode 100644 index 00000000..e2072785 --- /dev/null +++ b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue @@ -0,0 +1,93 @@ + + + diff --git a/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue new file mode 100644 index 00000000..54db23df --- /dev/null +++ b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue @@ -0,0 +1,59 @@ + + diff --git a/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue new file mode 100644 index 00000000..3a4863a4 --- /dev/null +++ b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue @@ -0,0 +1,185 @@ + + diff --git a/resources/views/pages/accounting/bank-statements/bank_statement.blade.php b/resources/views/pages/accounting/bank-statements/bank_statement.blade.php new file mode 100644 index 00000000..87bbf321 --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/bank_statement.blade.php @@ -0,0 +1,12 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+ + + +
+
+@endsection diff --git a/resources/views/pages/accounting/bank-statements/index.blade.php b/resources/views/pages/accounting/bank-statements/index.blade.php index f41813db..e4071c26 100644 --- a/resources/views/pages/accounting/bank-statements/index.blade.php +++ b/resources/views/pages/accounting/bank-statements/index.blade.php @@ -1,102 +1,370 @@ @extends('layouts.base_portal') @section('inner_content') -
-
-
-
-
Import Bank Statement
+
+
+
-
- @if (session('success')) -
- {{ session('success') }} + {{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
Complete
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{-- --}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Bank Statements
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Transactions Mapping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Exceptional Transactions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Reports & Analysis
+
+
+
+
+
+
+
- @endif - @if (session('error')) -
- {{ session('error') }} -
- @endif +
+
+
+
+
+
+ +
+
+
+
+
'.implode('', $headers).'
+ + + + + + + + + @foreach($accounts as $account) + + + + + + @endforeach + +
AccountCurrency
{{$account->number}}
{{$account->type}}
{{$account->currency}} + Open +
+ + + +
+
+
+
+
+
+
+ + +
+ +
+
+
+ Rerun +
+
+
+
+ + +
+ + Clear +
+
+
-
- @csrf -
- - + + + + + + + + + + + + + + + @foreach($statements as $statement) + + + + + + + + + + @endforeach + +
AccountDate FromDate ToTotal AmountBegin BalanceEnd BalanceActions
{{ $statement->account->number }}{{ $statement->date_from->format('d-m-Y') }}{{ $statement->date_to->format('d-m-Y') }}{{ $statement->total_amount }}{{ $statement->begin_balance }}{{ $statement->end_balance }} + View +
+ {{ $statements->links() }} +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
Deposit Mapping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Withdrawal Mapping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + +
+
+ - - - - - -
-
- @if (session('message')) -
- {{ session('message') }}
- @endif -
Bank Statements - View All -
-
-
-
- - -
- - Clear -
- -
-
- - -
- -
- - - - - - - - - - - - - - - @foreach($statements as $statement) - - - - - - - - - - - @endforeach - -
AccountDate FromDate ToTotal AmountBegin BalanceEnd BalanceActions
{{ $statement->account->name }} ({{ $statement->account->number }}){{ $statement->date_from->format('d-m-Y') }}{{ $statement->date_to->format('d-m-Y') }}{{ $statement->total_amount }}{{ $statement->begin_balance }}{{ $statement->end_balance }} - View - - Rerun -
- {{ $statements->links() }}
@endsection +{{--@extends('layouts.base_portal')--}} +{{--@section('inner_content')--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
Import Bank Statement
--}} + +{{--
--}} +{{-- @if (session('success'))--}} +{{--
--}} +{{-- {{ session('success') }}--}} +{{--
--}} +{{-- @endif--}} +{{-- @if (session('error'))--}} +{{--
--}} +{{-- {{ session('error') }}--}} +{{--
--}} +{{-- @endif--}} + +{{--
--}} +{{-- @csrf--}} +{{--
--}} +{{-- --}} +{{-- --}} +{{--
--}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- @if (session('message'))--}} +{{--
--}} +{{-- {{ session('message') }}--}} +{{--
--}} +{{-- @endif--}} +{{--
Bank Statements--}} +{{-- View All--}} +{{--
--}} + +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--@endsection--}} diff --git a/resources/views/pages/accounting/bank-statements/indexv2.blade.php b/resources/views/pages/accounting/bank-statements/indexv2.blade.php new file mode 100644 index 00000000..f18a77ae --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/indexv2.blade.php @@ -0,0 +1,461 @@ +@extends('layouts.base_portal') +@section('inner_content') + + +
+
+
+

Accounting Dashboard

+
+
+
+
+
+
+
+
Import Bank Statement
+ +
+ @if (session('success')) +
+ {{ session('success') }} +
+ @endif + @if (session('error')) +
+ {{ session('error') }} +
+ @endif + +
+ @csrf +
+ + +
+ +
+
+
+
+
+
+
+
+
Transaction Filtering
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateTransaction TypeAccount NameDescriptionStatus
2023-04-11DepositBank Account 1Salary PaymentPending
2023-04-09WithdrawalBank Account 2Vendor PaymentReconciled
2023-04-07DepositBank Account 3Online Order PaymentException
+
+
+ + + +
+
+

Step 1: Start Mapping bank Transactions

+

This is the first step in the wizard.

+
+
+

Step 2: List of automatically mapped bank transactions

+

This is the second step in the wizard.

+
+
+

Step 3: List of bank transactions that require manual mapping

+

This is the third step in the wizard.

+
+
+

Step 4: Export invoices to accounting software

+

This is the fourth step in the wizard.

+
+
+

Step 5: Import invoices from accounting software

+

This is the fifth step in the wizard.

+
+
+

Step 6: Export payment receipts

+

This is the sixth step in the wizard.

+
+
+

Step 7: Import payment receipts

+

This is the final step in the wizard.

+
+
+ + +
    +
  • +
  • +
  • +
+ +
+
+

Tab 3 Content

+

Aliquam vitae magna sit amet tellus bibendum posuere. Integer nec leo quis arcu tincidunt eleifend. Suspendisse potenti. Proin ullamcorper sodales nisl vel tincidunt. Etiam malesuada, mauris sit amet tincidunt facilisis, nisl enim aliquet turpis, ac pretium lacus nulla a libero. Vivamus euismod ex vel sapien dignissim, a fringilla enim fringilla. Sed sit amet lobortis augue. Aenean ut neque ac elit interdum pretium vel vel purus. Duis ut magna at ante dignissim efficitur. Pellentesque molestie bibendum ipsum a malesuada. Nulla cursus vehicula felis vel dapibus. Suspendisse eget vulputate ex. In pulvinar tincidunt justo, eu viverra orci malesuada id. Sed posuere arcu ac diam finibus posuere.

+
+
+
+
+ + +
+ + Clear +
+ +
+
+ + +
+ +
+ + + + + + + + + + + + + + + @foreach($statements as $statement) + + + + + + + + + + @endforeach + +
AccountDate FromDate ToTotal AmountBegin BalanceEnd BalanceActions
{{ $statement->account->name }} ({{ $statement->account->number }}){{ $statement->date_from->format('d-m-Y') }}{{ $statement->date_to->format('d-m-Y') }}{{ $statement->total_amount }}{{ $statement->begin_balance }}{{ $statement->end_balance }} + View +
+ {{ $statements->links() }} +
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateTransaction TypeAccount NameDescriptionStatus
2023-04-11DepositBank Account 1Salary PaymentPending
2023-04-09WithdrawalBank Account 2Vendor PaymentReconciled
2023-04-07DepositBank Account 3Online Order PaymentException
+
+
+
+
+ + +
+ + Clear +
+ +
+
+ + +
+ +
+ + + + + + + + + + + + + + + @foreach($statements as $statement) + + + + + + + + + + @endforeach + +
AccountDate FromDate ToTotal AmountBegin BalanceEnd BalanceActions
{{ $statement->account->name }} ({{ $statement->account->number }}){{ $statement->date_from->format('d-m-Y') }}{{ $statement->date_to->format('d-m-Y') }}{{ $statement->total_amount }}{{ $statement->begin_balance }}{{ $statement->end_balance }} + View +
+ {{ $statements->links() }} +
+
+

Tab 3 Content

+

Aliquam vitae magna sit amet tellus bibendum posuere. Integer nec leo quis arcu tincidunt eleifend. Suspendisse potenti. Proin ullamcorper sodales nisl vel tincidunt. Etiam malesuada, mauris sit amet tincidunt facilisis, nisl enim aliquet turpis, ac pretium lacus nulla a libero. Vivamus euismod ex vel sapien dignissim, a fringilla enim fringilla. Sed sit amet lobortis augue. Aenean ut neque ac elit interdum pretium vel vel purus. Duis ut magna at ante dignissim efficitur. Pellentesque molestie bibendum ipsum a malesuada. Nulla cursus vehicula felis vel dapibus. Suspendisse eget vulputate ex. In pulvinar tincidunt justo, eu viverra orci malesuada id. Sed posuere arcu ac diam finibus posuere.

+
+
+
+
+
+
+
+
+
+ + + + ``` + Next, we add some custom CSS styles to the wizard to make it look more appealing. + php + Copy code + + Finally, we add some JavaScript code to handle the wizard functionality. We use the Bootstrap Wizard plugin to enable the wizard navigation buttons. + php + Copy code + +@endsection diff --git a/routes/accounting.php b/routes/accounting.php index acc75e35..807ba564 100644 --- a/routes/accounting.php +++ b/routes/accounting.php @@ -3,6 +3,8 @@ use Illuminate\Support\Facades\Route; Route::group(['prefix' => 'accounting', 'as' => 'accounting.', 'namespace' => 'Accounting'], function () { + Route::post('/import', 'BankStatementController@import')->name('statement.import'); + Route::get('/bank_account', 'BankStatementController@transactions')->name('bank.transaction'); Route::group(['prefix' => 'statements/{id}', 'as' => 'statement.'], function () { Route::get('/details', 'BankStatementController@fetch')->name('details'); Route::put('/details/update', 'BankStatementController@update')->name('details.update'); diff --git a/routes/web.php b/routes/web.php index d7117c89..e4018b8e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -556,12 +556,16 @@ Route::get('/currency-rate-history', function () { })->name('currency_rate.history'); Route::get('/statements', [BankStatementController::class, 'index'])->name('statements.index'); +Route::get('/statements/v2', [BankStatementController::class, 'indexv2'])->name('statements.indexv2'); Route::post('/statements/import', [BankStatementController::class, 'import'])->name('statements.import'); Route::get('/statements/{statement}/details', function ($statement) { return view('pages.accounting.bank-statements.details', ['statement' => $statement]); })->name('statements.transactions.details'); +Route::get('/statements/{account}/transactions', function ($account) { + return view('pages.accounting.bank-statements.bank_statement', ['account' => $account]); +})->name('statements.account.transactions'); Route::get('/statements/{statement}', [BankStatementController::class, 'show'])->name('statements.show'); -Route::get('/statements/{statement}/rerun', [BankStatementController::class, 'rerun'])->name('statements.rerun'); +Route::get('/statements/mapping/rerun', [BankStatementController::class, 'rerun'])->name('statements.rerun'); Route::get('/statements/{statement}/download', 'StatementController@download')->name('statements.download'); Route::get('/bank-record', 'Imports\ImportBankRecordController@import');