diff --git a/app/Classes/General/Eloquent/Filters/DateIn.php b/app/Classes/General/Eloquent/Filters/DateIn.php new file mode 100644 index 00000000..b1704eb4 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/DateIn.php @@ -0,0 +1,19 @@ +whereIn('date', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/HasAccountStatementId.php b/app/Classes/General/Eloquent/Filters/HasAccountStatementId.php new file mode 100644 index 00000000..05cfe9d8 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/HasAccountStatementId.php @@ -0,0 +1,22 @@ +whereHas('statementTransaction', function ($query) use ($value) { + $query->where('account_statement_id', $value); + }); + } +} 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..c9c92438 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IsMappedWithMultiple.php @@ -0,0 +1,24 @@ +withCount(['owners' => function ($query){ + $query->where('status', ApprovalStatus::PENDING_VERIFICATION); + }]); + return $value ? $query->having('owners_count', '>', 1) : $query->having('owners_count', '=', 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/OwnerId.php b/app/Classes/General/Eloquent/Filters/OwnerId.php new file mode 100644 index 00000000..eac7e32d --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/OwnerId.php @@ -0,0 +1,20 @@ +where('owner_id', $value); + } + +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/PayFor.php b/app/Classes/General/Eloquent/Filters/PayFor.php new file mode 100644 index 00000000..b8a2ba75 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/PayFor.php @@ -0,0 +1,19 @@ +where('pay_for', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/PayForIn.php b/app/Classes/General/Eloquent/Filters/PayForIn.php new file mode 100644 index 00000000..3fe98f00 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/PayForIn.php @@ -0,0 +1,19 @@ +whereIn('pay_for', $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..f2dfe546 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/StatementTransactionOwnerStatusIn.php @@ -0,0 +1,22 @@ +whereHas('owners', function ($query) use ($value) { + return $query->whereIn('statement_transaction_owners.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/CreateBankStatementTransactionOwners.php b/app/Classes/Jobs/CreateBankStatementTransactionOwners.php new file mode 100644 index 00000000..e7797210 --- /dev/null +++ b/app/Classes/Jobs/CreateBankStatementTransactionOwners.php @@ -0,0 +1,27 @@ +make(CreateBankStatementTransactionOwnersProcessor::class))->execute(); + } + + public function delay($delay) + { + // Add delay in seconds to the job + $this->delay = $delay; + return $this; + } +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ApproveDuplicateBankStatementDetailsStatusLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ApproveDuplicateBankStatementDetailsStatusLogic.php new file mode 100644 index 00000000..e7d1a7c9 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ApproveDuplicateBankStatementDetailsStatusLogic.php @@ -0,0 +1,283 @@ + 'Update Duplicate Bank Statement Details Status', + 'message' => 'You have successfully updated the Statement Transation Status' + ]; + } + + /** @var UpdatesBankStatementTransactionOwnerStatus */ + private $updatesBankStatementTransactionOwnerStatus; + + /** + * @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus + */ + public function __construct(UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus) + { + $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus; + } + +// /** +// * Perform logic for approving or rejecting a statement transaction owner and handle matching records. +// * +// * @param Request $request The request object containing route parameters and data. +// * @return JsonResponse The JSON response indicating the result of the logic. +// */ +// public function logic(Request $request): JsonResponse +// { +// // Determine the status based on the 'status' route parameter +// $status = $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED; +// +// // Find the statement transaction owner based on the 'id' route parameter +// $owner = StatementTransactionOwner::find($request->route('id')); +// +// // Execute an update action to change the status of the owner +// $this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status); +// +// // If the status is 'approved', reject all other owners with the same system, owner type, and owner ID +// if ($status === ApprovalStatus::APPROVED) { +// StatementTransactionOwner::where('system', $owner->system) +// ->where('owner_type', $owner->owner_type) +// ->where('owner_id', $owner->owner_id) +// ->where('id', '!=', $owner->id) +// ->update(['status' => ApprovalStatus::REJECTED]); +// } +// +// // Find all siblings (owners with the same statement transaction ID) +// $siblings = StatementTransactionOwner::where('statement_transaction_id', $owner->statement_transaction_id) +// ->where('id', '!=', $owner->id) +// ->get(); +// +// // Process each sibling +// foreach ($siblings as $sibling) { +// +// // If the status is 'approved', reject the sibling and save the changes +// if ($status === ApprovalStatus::APPROVED) { +// $sibling->status = ApprovalStatus::REJECTED; +// $sibling->save(); +// } +// +// // Find all twins (owners with the same system, owner type, and owner ID) +// $twins = StatementTransactionOwner::where('system', $sibling->system) +// ->where('owner_type', $sibling->owner_type) +// ->where('owner_id', $sibling->owner_id) +// ->where('id', '!=', $sibling->id) +// ->get(); +// +// // Process each twin +// foreach ($twins as $twin) { +// // Find all owners with the same statement transaction ID as the twin +// $owners = StatementTransactionOwner::where('statement_transaction_id', $twin->statement_transaction_id) +// ->where('id', '!=', $twin->id) +// ->get(); +// +// // If there is only one owner (the twin itself), execute an update action to change its status to 'approved' +// if (count($owners) === 1) { +// $this->updatesBankStatementTransactionOwnerStatus->execute($twin, ApprovalStatus::APPROVED); +// } +// } +// } +// +// // Find all remaining matching owners for the related transaction +// $remainingMatches = StatementTransactionOwner::where('system', $owner->system) +// ->where('owner_type', $owner->owner_type) +// ->where('owner_id', $owner->owner_id) +// ->where('status', ApprovalStatus::PENDING_VERIFICATION) // Consider only pending owners +// ->get(); +// +// // Process each remaining match +// foreach ($remainingMatches as $match) { +// // Find all owners with the same statement transaction ID as the match +// $owners = StatementTransactionOwner::where('statement_transaction_id', $match->statement_transaction_id) +// ->where('id', '!=', $match->id) +// ->get(); +// +// // If there is only one owner (the match itself), execute an update action to change its status to 'approved' +// if (count($owners) === 1) { +// $this->updatesBankStatementTransactionOwnerStatus->execute($match, ApprovalStatus::APPROVED); +// } +// } +// +// // Return an empty response +// return $this->response([]); +// } + + /** + * Perform logic for approving or rejecting a statement transaction owner and handle matching records. + * + * @param Request $request The request object containing route parameters and data. + * @return JsonResponse The JSON response indicating the result of the logic. + */ + public function logic(Request $request): JsonResponse + { + // Determine the approval status + $status = $this->getApprovalStatus($request); + + // Find the statement transaction owner + $owner = $this->getOwner($request); + + // Update owner status + $this->updateOwnerStatus($owner, $status); + + // If the status is 'approved', handle the approval process + if ($status === ApprovalStatus::APPROVED) { + $this->handleApprovedStatus($owner); + } + + // Check and approve remaining matches if any + $this->checkAndApproveRemainingMatches($owner); + + // Return an empty response + return $this->response([]); + } + + private function getApprovalStatus(Request $request): int + { + return $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED; + } + + private function getOwner(Request $request): StatementTransactionOwner + { + return StatementTransactionOwner::find($request->route('id')); + } + + private function updateOwnerStatus(StatementTransactionOwner $owner, int $status): void + { + $this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status); + } + + private function handleApprovedStatus(StatementTransactionOwner $owner): void + { + // Reject all other owners with the same system, owner type, and owner ID + $this->rejectOtherOwners($owner); + + // Find all siblings and process them + $siblings = $this->getSiblings($owner); + $this->processSiblings($siblings); + } + + private function rejectOtherOwners(StatementTransactionOwner $owner): void + { + StatementTransactionOwner::where('system', $owner->system) + ->where('owner_type', $owner->owner_type) + ->where('owner_id', $owner->owner_id) + ->where('id', '!=', $owner->id) + ->update(['status' => ApprovalStatus::REJECTED]); + } + + private function getSiblings(StatementTransactionOwner $owner): Collection + { + return StatementTransactionOwner::where('statement_transaction_id', $owner->statement_transaction_id) + ->where('id', '!=', $owner->id) + ->get(); + } + + private function processSiblings(Collection $siblings): void + { + foreach ($siblings as $sibling) { + $this->processSibling($sibling); + } + } + + private function processSibling(StatementTransactionOwner $sibling): void + { + // Reject the sibling and save the changes + $sibling->status = ApprovalStatus::REJECTED; + $sibling->save(); + + // Find all twins and process them + $twins = $this->getTwins($sibling); + $this->processTwins($twins); + } + + private function getTwins(StatementTransactionOwner $sibling): Collection + { + return StatementTransactionOwner::where('system', $sibling->system) + ->where('owner_type', $sibling->owner_type) + ->where('owner_id', $sibling->owner_id) + ->where('id', '!=', $sibling->id) + ->get(); + } + + private function processTwins(Collection $twins): void + { + foreach ($twins as $twin) { + $this->processTwin($twin); + } + } + + private function processTwin(StatementTransactionOwner $twin): void + { + // Find all owners with the same statement transaction ID as the twin + $owners = $this->getOwners($twin); + + // If there is only one owner (the twin itself), approve it + if ($owners->count() === 1) { + $this->updateOwnerStatus($twin, ApprovalStatus::APPROVED); + } + } + + private function getOwners(StatementTransactionOwner $transactionOwner): Collection + { + return StatementTransactionOwner::where('statement_transaction_id', $transactionOwner->statement_transaction_id) + ->where('id', '!=', $transactionOwner->id) + ->get(); + } + + private function checkAndApproveRemainingMatches(StatementTransactionOwner $owner): void + { + // Find all remaining matching owners for the related transaction + $remainingMatches = $this->getRemainingMatches($owner); + + // Process each remaining match + foreach ($remainingMatches as $match) { + $this->processRemainingMatch($match); + } + } + + private function getRemainingMatches(StatementTransactionOwner $owner): Collection + { + return StatementTransactionOwner::where('system', $owner->system) + ->where('owner_type', $owner->owner_type) + ->where('owner_id', $owner->owner_id) + ->where('status', ApprovalStatus::PENDING_VERIFICATION) + ->get(); + } + + private function processRemainingMatch(StatementTransactionOwner $match): void + { + // Find all owners with the same statement transaction ID as the match + $owners = $this->getOwners($match); + + // If there is only one owner (the match itself), approve it + if ($owners->count() === 1) { + $this->updateOwnerStatus($match, ApprovalStatus::APPROVED); + } + + } + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/GroupApproveStatementTransactionLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/GroupApproveStatementTransactionLogic.php new file mode 100644 index 00000000..3b220146 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/GroupApproveStatementTransactionLogic.php @@ -0,0 +1,86 @@ + 'Group Approve Statement Transaction', + 'message' => 'You have successfully approveed a group of Statement Transation' + ]; + } + + /** @var ListsBankStatementTransactions */ + private $listsBankStatementTransactions; + + /** @var UpdatesBankStatementTransactionOwnerStatus */ + private $updatesBankStatementTransactionOwnerStatus; + + /** @var UpdatesTransactionStatus */ + private $updatesTransactionStatus; + + /** + * @param ListsBankStatementTransactions $listsBankStatementTransactions + * @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus + * @param UpdatesTransactionStatus $updatesTransactionStatus + */ + public function __construct(ListsBankStatementTransactions $listsBankStatementTransactions, UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus, UpdatesTransactionStatus $updatesTransactionStatus) + { + $this->listsBankStatementTransactions = $listsBankStatementTransactions; + $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus; + $this->updatesTransactionStatus = $updatesTransactionStatus; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws MalformedRequestException + */ + public function logic(Request $request): JsonResponse + { + + $filters = [ + "min_amount" => 0, + "is_mapped" => true, + "is_mapped_with_multiple" => false, + "statement_transaction_owner_type_in" => [1, 2], + "statement_transaction_owner_status_in" => [1] + ]; + $statementTransactions = $this->listsBankStatementTransactions->execute($filters); + + foreach ($statementTransactions as $statementTransaction) { + $owners = $statementTransaction->owners; + + if (count($owners)) { + $this->updatesBankStatementTransactionOwnerStatus->execute($owners->first(), ApprovalStatus::APPROVED); + + // automatically approve payment if pending verification + +// if ($statementTrasactionOwner->owner->type !== StatementTransactionOwnerType::SALES) continue; +// if ($statementTrasactionOwner->owner->status === ApprovalStatus::PENDING_VERIFICATION ) { +// $this->updatesTransactionStatus->execute($statementTrasactionOwner->owner, ApprovalStatus::APPROVED); +// } + } + } + + return $this->response([]); + } +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ImportBankStatementLogic.php new file mode 100644 index 00000000..0355c554 --- /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/ListBankStatementDetailsLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementDetailsLogic.php new file mode 100644 index 00000000..10cffba1 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/ListBankStatementDetailsLogic.php @@ -0,0 +1,52 @@ + 'Retrieved Bank Statement Details', + 'message' => 'You have successfully retrieved a Bank Statement Details' + ]; + } + + + /** @var ListsBankStatementDetails */ + private $listsBankStatementDetails; + + /** + * ListBankStatementDetailsLogic constructor. + * @param ListsBankStatementDetails $listsBankStatementDetails + */ + public function __construct(ListsBankStatementDetails $listsBankStatementDetails) + { + $this->listsBankStatementDetails = $listsBankStatementDetails; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws ErrorException + */ + public function logic(Request $request) : JsonResponse + { + $query = $this->listsBankStatementDetails->execute($this->listsBankStatementDetails->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(BankStatementDetailResource::collection($query)); + } + +} 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 new file mode 100644 index 00000000..ff7806d4 --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/UpdateBankStatementDetailLogic.php @@ -0,0 +1,137 @@ + 'Updated Bank Statement Transactions Details', + 'message' => 'You have successfully updated the Bank Statement Transactions Details' + ]; + } + + /** @var FetchesBankStatementTransaction */ + private $fetchesBankStatementTransaction; + + + /** @var ChecksBillNumber */ + private $checksBillNumber; + + /** + * @param FetchesBankStatementTransaction $fetchesBankStatementTransaction + * @param ChecksBillNumber $checksBillNumber + */ + public function __construct(FetchesBankStatementTransaction $fetchesBankStatementTransaction, ChecksBillNumber $checksBillNumber) + { + $this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction; + $this->checksBillNumber = $checksBillNumber; + } + + public function logic(Request $request): JsonResponse + { + $bankStatementTransaction = $this->fetchesBankStatementTransaction->execute(['id' => $request->route('id')]); + + $transactionReference = $request->input('transaction_reference'); + $systemReference = $request->input('system_references'); + + $owner_type = $owner_id = $owner_reference = $statementTransactionOwnerType = $system = null; + + $salesSystems = ['lite', 'cntr', 'probashi', 'pets']; + $allowedSystems = array_merge($salesSystems, ['exchange', 'izyim']); + + if (!in_array($systemReference, $allowedSystems)) { + throw new MalformedRequestException('System Reference not allowed'); + } + + switch ($request->input('pay_for')) { + case 'sales': + $owner_reference = $transactionReference; + $statementTransactionOwnerType = StatementTransactionOwnerType::SALES; + break; + + case 'top_up': + $owner_reference = $transactionReference; + $statementTransactionOwnerType = StatementTransactionOwnerType::WALLET_TOP_UP; + break; + + case 'internal_bank_transfer': + $statementTransactionOwnerType = StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_IN; + break; + + case 'others': + $owner_reference = $transactionReference; + $statementTransactionOwnerType = StatementTransactionOwnerType::NON_OPERATIONAL; + break; + + default: + throw new MalformedRequestException('Transaction Type Not Allowed'); + } + + if (in_array($systemReference, ['exchange', 'izyim'])) { + $transaction = $this->checksBillNumber->execute($transactionReference, $systemReference); + + if($systemReference === 'exchange') { + $owner_type = Transaction::class; + $owner_id = $transaction->id; + if($transaction->type === TransactionType::TOP_UP) { + $owner_reference = $transaction->owner->owner->reference; + } + + if($transaction->type === TransactionType::PAYMENT && $transaction->owner_type === Booking::class) { + $owner_reference = $transaction->owner->marking; + } + + } + + if($systemReference === 'izyim') { + $transaction = $transaction[0]; + $owner_type = Transaction::class; + $owner_id = $transaction['owner_id']; + $owner_reference = $transaction['owner_reference']; + + } + } + + $system = $systemReference != null ? SystemType::SYSTEM_NAMES[$systemReference] : ''; + + $this->createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference); + + return $this->response([]); + } + + private function createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference) + { + $ownerData = [ + 'type' => $statementTransactionOwnerType, + 'system' => $system, + 'owner_type' => $owner_type, + 'owner_id' => $owner_id, + 'owner_reference' => $owner_reference, + ]; + + $bankStatementTransaction->owners()->firstOrCreate($ownerData); + + } + + +} diff --git a/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php b/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php new file mode 100644 index 00000000..3a006e9e --- /dev/null +++ b/app/Classes/Modules/Accounting/ControllersLogic/UpdateStatementTransactionStatusLogic.php @@ -0,0 +1,81 @@ + 'Update Statement Transaction Status', + 'message' => 'You have successfully updated the Statement Transation Status' + ]; + } + + /** @var FetchesBankStatementTransaction */ + private $fetchesBankStatementTransaction; + + /** @var UpdatesBankStatementTransactionOwnerStatus */ + private $updatesBankStatementTransactionOwnerStatus; + + /** @var UpdatesTransactionStatus */ + private $updatesTransactionStatus; + + /** + * UpdateAnnouncementLogic constructor. + * @param FetchesBankStatementTransaction $fetchesBankStatementTransaction + * @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus + * @param UpdatesTransactionStatus $updatesTransactionStatus + */ + public function __construct( + FetchesBankStatementTransaction $fetchesBankStatementTransaction, + UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus, + UpdatesTransactionStatus $updatesTransactionStatus + ) { + $this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction; + $this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus; + $this->updatesTransactionStatus = $updatesTransactionStatus; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request): JsonResponse + { + $statementTrasaction = $this->fetchesBankStatementTransaction->execute(['id' => $request->route('id')]); + + $statementTrasactionOwner = $statementTrasaction->owners->first(); + + $this->updatesBankStatementTransactionOwnerStatus->execute($statementTrasactionOwner, $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED); + + // todo-new: approve payments status, need to check the owner(if system is shipping, need to api with shipping portal) + // if ($request->route('status') == 'approve') { + // if ($statementTrasactionOwner->transaction->type === StatementTransactionOwnerType::SALES) { + // if ($statementTrasactionOwner->owner->status === ApprovalStatus::PENDING_VERIFICATION) { + // $this->updatesTransactionStatus->execute($statementTrasactionOwner->owner, ApprovalStatus::APPROVED); + // } + // } + // } + + return $this->resourceResponse(new BankStatementTransactionResource($statementTrasaction)); + } +} diff --git a/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTransactionObject.php b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTransactionObject.php new file mode 100644 index 00000000..30bac5d3 --- /dev/null +++ b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTransactionObject.php @@ -0,0 +1,137 @@ +statement_transaction_id = $statement_transaction_id; + $this->type = $type; + $this->system = $system; + $this->owner_type = $owner_type; + $this->owner_id = $owner_id; + $this->invoice_reference = $invoice_reference; + $this->receipt_reference = $receipt_reference; + $this->is_auto_mapped = $is_auto_mapped; + $this->status = $status; + } + + /** + * @return int + */ + public function getStatementTransactionId(): int + { + return $this->statement_transaction_id; + } + + /** + * @return int + */ + public function getType(): int + { + return $this->type; + } + + /** + * @return string + */ + public function getOwnerType(): string + { + return $this->owner_type; + } + + /** + * @return int + */ + public function getOwnerId(): int + { + return $this->owner_id; + } + + /** + * @return string + */ + public function getSystem(): string + { + return $this->system; + } + + /** + * @return string + */ + public function getInvoiceReference(): string + { + return $this->invoice_reference; + } + + /** + * @return string + */ + public function getReceipteReference(): string + { + return $this->receipt_reference; + } + + /** + * @return Boolean + */ + public function getIsAutoMapped(): Boolean + { + return $this->is_auto_mapped; + } + + /** + * @return int + */ + public function getStatus(): int + { + return $this->status; + } +} diff --git a/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTrasactionOwnerObject.php b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTrasactionOwnerObject.php new file mode 100644 index 00000000..07fe876e --- /dev/null +++ b/app/Classes/Modules/Accounting/DataTransferObjects/BankStatementTrasactionOwnerObject.php @@ -0,0 +1,46 @@ +pay_for = $pay_for; + $this->system_references = $system_references; + } + + /** + * @return string + */ + public function getPayFor(): string + { + return $this->pay_for; + } + + /** + * @return string + */ + public function getSystemReferences(): string + { + return $this->system_references; + } +} diff --git a/app/Classes/Modules/Accounting/Processors/ChecksBillNumber.php b/app/Classes/Modules/Accounting/Processors/ChecksBillNumber.php new file mode 100644 index 00000000..0ac72cd2 --- /dev/null +++ b/app/Classes/Modules/Accounting/Processors/ChecksBillNumber.php @@ -0,0 +1,41 @@ + false]); + $response = $client->request('GET', $url . '?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"bill_no":"' . $bill_no . '"}'); + $body = $response->getBody(); + $data = json_decode($body, true); + $payload = $data['payload']; + return $payload['data']; + } catch (\Exception $exception) { + dd($exception->getMessage()); +// preg_match('/\{.*\}/s', $exception->getMessage(), $matches); +// $jsonError = json_decode($matches[0]); + // Retrieved Transactions failed +// throw new MalformedRequestException($jsonError->title); + } + } + + if ($system_reference == 'exchange') { + $transaction = Transaction::where('bill_no', $bill_no)->first(); + if ($transaction) { + return $transaction; + } + } + + // if not found + throw new MalformedRequestException('Bill Number Not Found.'); + } +} diff --git a/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php new file mode 100644 index 00000000..90dd7048 --- /dev/null +++ b/app/Classes/Modules/Accounting/Processors/CreateBankStatementTransactionOwnersProcessor.php @@ -0,0 +1,255 @@ +whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + })->orderBy('posting_date')->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, + 'owner_reference'=> $creditTransaction->owner->marking, + ]); + } + + + // Shipping Portal Sales + $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date), 2); + foreach ($creditTransactions as $creditTransaction) { + if($creditTransaction['owner_type'] === Wallet::class) continue; + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::SALES, + 'system' => 'SHIPPING_PORTAL', + 'owner_type' => $creditTransaction['owner_type'], + 'owner_id'=> $creditTransaction['owner_id'], + 'owner_reference'=> $creditTransaction['owner_reference'], + ]); + } + + // 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, + 'owner_reference'=> $creditTransaction->owner->owner->reference, + ]); + } + + $creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date), 5); + foreach ($creditTransactions as $creditTransaction) { + $transaction->owners()->firstOrCreate([ + 'type' => StatementTransactionOwnerType::WALLET_TOP_UP, + 'system' => 'SHIPPING_PORTAL', + 'owner_type' => $creditTransaction['owner_type'], + 'owner_id'=> $creditTransaction['owner_id'], + 'owner_reference'=> $creditTransaction['owner_reference'], + ]); + } + + // 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, + 'owner_reference'=> $debitTransaction->reference + ]); + } + + } + } + + // 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, + 'owner_reference'=> $debitTransaction->owner->owner->marking, + ]); + } + + // 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 + ]); + } + } + } + } + + 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, $type){ + $url = 'https://izyim.cief-malaysia.com/public/api/v1/transactions/mappable/query'; + return $this->getFromShippingPortal($amount, $dateRange, $url, $type); + } + + private function getGroupsFromShippingPortal($amount, $dateRange, $type){ + $url = 'https://izyim.cief-malaysia.com/public/api/v1/groups/query'; + return $this->getFromShippingPortal($amount, $dateRange, $url, $type); + } + + private function getFromShippingPortal($amount, $dateRange, $url, $type){ + try{ + + $client = new \GuzzleHttp\Client(['verify' => false]); + $response = $client->request('GET', $url.'?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).',"type_in:"['.$type.']}'); + $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/CreatesBankStatementTransactionOwner.php b/app/Classes/Modules/Accounting/Services/CreatesBankStatementTransactionOwner.php new file mode 100644 index 00000000..92a3ea33 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/CreatesBankStatementTransactionOwner.php @@ -0,0 +1,34 @@ +statement_transaction_id = $object->getStatementTransactionId(); + $model->type = $object->getType(); + $model->system = $object->getSystem(); + $model->owner_type = $object->getOwnerType(); + $model->owner_id = $object->getOwnerId(); + $model->invoice_reference = $object->getInvoiceReference(); + $model->receipt_reference = $object->getReceipteReference(); + $model->is_auto_mapped = $object->getIsAutoMapped(); + $model->status = $object->getStatus(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php new file mode 100644 index 00000000..22de5c39 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/FetchesBankStatementDetails.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransaction.php b/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransaction.php new file mode 100644 index 00000000..d2bff6f9 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/FetchesBankStatementTransaction.php @@ -0,0 +1,32 @@ +repository = $repository; + } + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php b/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php new file mode 100644 index 00000000..6e085e44 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/ListsBankStatementDetails.php @@ -0,0 +1,32 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} 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 new file mode 100644 index 00000000..e87121ac --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementDetails.php @@ -0,0 +1,25 @@ +system_references = $object->getSystemReferences(); + $model->pay_for = $object->getPayFor(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwner.php b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwner.php new file mode 100644 index 00000000..6af3d3d6 --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwner.php @@ -0,0 +1,32 @@ +statement_transaction_id = $object->getStatementTransactionId(); + $model->type = $object->getType(); + $model->system = $object->getSystem(); + $model->owner_type = $object->getOwnerType(); + $model->owner_id = $object->getOwnerId(); + $model->invoice_reference = $object->getInvoiceReference(); + $model->receipt_reference = $object->getReceipteReference(); + $model->is_auto_mapped = $object->getIsAutoMapped(); + $model->status = $object->getStatus(); + + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwnerStatus.php b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwnerStatus.php new file mode 100644 index 00000000..abcda20a --- /dev/null +++ b/app/Classes/Modules/Accounting/Services/UpdatesBankStatementTransactionOwnerStatus.php @@ -0,0 +1,22 @@ +status = $status; + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php b/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php index 0e920fe9..803dec47 100644 --- a/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php +++ b/app/Classes/Modules/Accounts/ControllersLogic/CreateCustomerLogic.php @@ -19,12 +19,15 @@ use App\Classes\ValueObjects\Constants\BusinessType; use App\Classes\ValueObjects\Constants\CompanyType; use App\Classes\ValueObjects\Constants\RoleTypes; use App\Classes\Jobs\CreatePerfexCRMCustomer; +use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject; use App\Models\Company; +use App\Models\Segment; use App\Models\User; +use Carbon\Carbon; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\App; - +use App\Classes\Modules\Segments\Services\CreatesSeasonalSegment; class CreateCustomerLogic extends AbstractControllerLogic { @@ -63,6 +66,9 @@ class CreateCustomerLogic extends AbstractControllerLogic /** @var CreatePerfexCRMLeadProcessor */ private $createPerfexCRMLeadProcessor; + /** @var CreatesSeasonalSegment */ + private $createsSeasonalSegment; + /** * CreateCustomerLogic constructor. * @param CreateUserProcessor $createUserProcessor @@ -73,9 +79,10 @@ class CreateCustomerLogic extends AbstractControllerLogic * @param AuthenticationProcessor $authenticationProcessor * @param GenerateEmailVerificationAttemptProcessor $generateEmailVerificationAttemptProcessor * @param CreatePerfexCRMLeadProcessor $createPerfexCRMLeadProcessor + * @param CreatesSeasonalSegment $createsSeasonalSegment */ public function __construct(CreateUserProcessor $createUserProcessor, CreateCompanyProcessor $createCompanyProcessor, CreateContactProcessor $createContactProcessor, AssignEmployeeProcessor $assignEmployeeProcessor, AssignSegmentProcessor $assignSegmentProcessor, AuthenticationProcessor $authenticationProcessor, GenerateEmailVerificationAttemptProcessor $generateEmailVerificationAttemptProcessor, - CreatePerfexCRMLeadProcessor $createPerfexCRMLeadProcessor) + CreatePerfexCRMLeadProcessor $createPerfexCRMLeadProcessor, CreatesSeasonalSegment $createsSeasonalSegment) { $this->createUserProcessor = $createUserProcessor; $this->createCompanyProcessor = $createCompanyProcessor; @@ -85,6 +92,7 @@ class CreateCustomerLogic extends AbstractControllerLogic $this->authenticationProcessor = $authenticationProcessor; $this->generateEmailVerificationAttemptProcessor = $generateEmailVerificationAttemptProcessor; $this->createPerfexCRMLeadProcessor = $createPerfexCRMLeadProcessor; + $this->createsSeasonalSegment = $createsSeasonalSegment; } /** @@ -109,8 +117,20 @@ class CreateCustomerLogic extends AbstractControllerLogic $Object = new EmploymentObject($company, $user); $this->assignEmployeeProcessor->execute($Object); + // assign STANDARD_SEGMENT to all new customers $this->assignSegmentProcessor->execute($company); + // assign other standard segment to all new customers + $otherDefaultSegmentToAdd = Segment::whereIn('name', ['HONEY TRAP NEW REGISTRATION'])->get(); + $start_date = Carbon::now(); + $end_date = $start_date->addDays(30); + foreach ($otherDefaultSegmentToAdd as $segment) { + $seasonalSegmentObject = new SeasonalSegmentObject($company->id, $segment->id, $start_date, $end_date ?? null); + + $this->createsSeasonalSegment->execute($seasonalSegmentObject); + $this->assignSegmentProcessor->execute($company, $segment->id); + } + if(config('perfexcrm.is_enabled') == 'true'){ //$this->createPerfexCRMLeadProcessor->execute($request); $createLeadPerfexCRMObject = new CreateLeadPerfexCRMObject( 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/Modules/Imports/Services/BankStatementImport.php b/app/Classes/Modules/Imports/Services/BankStatementImport.php new file mode 100644 index 00000000..a552b1b0 --- /dev/null +++ b/app/Classes/Modules/Imports/Services/BankStatementImport.php @@ -0,0 +1,82 @@ +has('account_number')) { + // If the row has an account number, create a new statement account + $accountNumber = $row->get('account_number'); + $accountType = $row->get('account_type'); + $accountName = $row->get('account_name'); + $accountCurrency = $row->get('account_currency'); + + $account = StatementAccount::updateOrCreate( + ['number' => $accountNumber], + [ + 'type' => $accountType, + 'name' => $accountName, + 'currency' => $accountCurrency, + ] + ); + } else { + // Otherwise, create a new statement transaction for the current statement account + $dateFrom = $row->get('date_from'); + $dateTo = $row->get('date_to'); + $totalAmount = $row->get('total_amount'); + $beginBalance = $row->get('begin_balance'); + $endBalance = $row->get('end_balance'); + + $statement = AccountStatement::updateOrCreate( + [ + 'account_id' => $account->id, + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + ], + [ + 'total_amount' => $totalAmount, + 'begin_balance' => $beginBalance, + 'end_balance' => $endBalance, + ] + ); + + $transactionDate = $row->get('transaction_date'); + $transactionTime = $row->get('transaction_time'); + $postingDate = $row->get('posting_date'); + $transactionDescription = $row->get('transaction_description'); + $transactionRef = $row->get('transaction_ref'); + $amount = $row->get('amount'); + $tellerId = $row->get('teller_id'); + $branchChannel = $row->get('branch_channel'); + $transactionCode = $row->get('transaction_code'); + + $transaction = new StatementTransaction([ + 'statement_id' => $statement->id, + 'transaction_date' => $transactionDate, + 'transaction_time' => $transactionTime, + 'posting_date' => $postingDate, + 'transaction_description' => $transactionDescription, + 'transaction_ref' => $transactionRef, + 'amount' => $amount, + 'teller_id' => $tellerId, + 'branch_channel' => $branchChannel, + 'transaction_code' => $transactionCode, + ]); + + $transaction->save(); + } + } + } +} 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 @@ + self::EXCHANGE, + 'shipping_portal' => self::SHIPPING_PORTAL, + 'izyim' => self::SHIPPING_PORTAL, + ]; +} diff --git a/app/Http/Controllers/Accounting/ApproveDuplicateBankStatementDetailsStatusController.php b/app/Http/Controllers/Accounting/ApproveDuplicateBankStatementDetailsStatusController.php new file mode 100644 index 00000000..0c026835 --- /dev/null +++ b/app/Http/Controllers/Accounting/ApproveDuplicateBankStatementDetailsStatusController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Accounting/BankStatementController.php b/app/Http/Controllers/Accounting/BankStatementController.php new file mode 100644 index 00000000..ff1324b4 --- /dev/null +++ b/app/Http/Controllers/Accounting/BankStatementController.php @@ -0,0 +1,365 @@ +input('account'); + $search = $request->input('search'); + + $accounts = StatementAccount::all(); + + $statementsQuery = AccountStatement::query(); + + if ($selectedAccount) { + $statementsQuery->where('statement_account_id', $selectedAccount); + } + + 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%"); + }); + } + + $statements = $statementsQuery->orderBy('date_from')->paginate(10); + + return view('pages.accounting.bank-statements.index', compact('accounts', 'selectedAccount', 'search', 'statements')); + } + + public function indexv2(Request $request) + { + $selectedAccount = $request->input('account'); + $search = $request->input('search'); + + $accounts = StatementAccount::all(); + + $statementsQuery = AccountStatement::query(); + + if ($selectedAccount) { + $statementsQuery->where('statement_account_id', $selectedAccount); + } + + 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%"); + }); + } + + $statements = $statementsQuery->paginate(10); + + return view('pages.accounting.bank-statements.indexv2', compact('accounts', 'selectedAccount', 'search', 'statements')); + } + + public function import(Request $request, ImportBankStatementLogic $logic): JsonResponse + { + return $logic->execute($request); + } + + public function rerun() + { + CreateBankStatementTransactionOwners::dispatch(); + return redirect()->back()->with('success', 'Rerun triggered successfully'); + } + + public function show(AccountStatement $statement, Request $request) + { + $transactions = $statement->transactions(); + // $account = $statement->account(); + // dd(json_encode($account->where('id', '>=', 1)->first())); + // dd(json_encode($transactions->where('id', '>=', 1)->first())); + if ($request->get('transaction_filter')) { + $transactionFilter = $request->get('transaction_filter'); + $transactions = $transactions->where('transaction_description', 'LIKE', "%$transactionFilter%"); + } + + if ($request->get('from_amount_filter')) { + $fromAmountFilter = $request->get('from_amount_filter'); + $transactions = $transactions->where('amount', '>=', $fromAmountFilter); + } + + if ($request->get('to_amount_filter')) { + $toAmountFilter = $request->get('to_amount_filter'); + $transactions = $transactions->where('amount', '<=', $toAmountFilter); + } + + // $transactions = $transactions->paginate(100); + $transactions = $transactions->get(); + echo $this->process3_merged($transactions); + + //return view('pages.accounting.bank-statements.show', compact('statement', 'transactions')); + } + + public function download(AccountStatement $statement) + { + $transactions = $statement->transactions; + + $csvExporter = new \Laracsv\Export(); + $csvExporter->build($transactions, ['transaction_date', 'transaction_time', 'posting_date', 'transaction_description', 'transaction_ref', 'debit', 'credit']) + ->download($statement->date_from->format('Y-m-d') . '_' . $statement->date_to->format('Y-m-d') . '_statement.csv'); + } + + public function fetch(Request $request, ListBankStatementDetailsLogic $logic): JsonResponse + { + 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); + } + + private function process3_merged($transactions){ + + // $statement = $transactions[0]->statement(); + // dd(json_encode($statement->first())); + + $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) { + $isExist = StatementTransactionOwner::where('statement_transaction_id', $row->id)->first(); + + if ($isExist) { + continue; + } + + $count++; + $credit = 0.00; + $debit = 0.00; + + // dd(json_encode($row['posting_date'])); + $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; + + $matches = $systemReference == $row['remarkreferences'] ? $yes : $no; + + $table .= ' + + + + + + + + + + + + + '; + + //AccountStatement + // $row->statement()->first()->id) + + $statementTransactionsDetail = new StatementTransactionOwner([ + 'date' => $date, + 'statement_transaction_id' => $row->id, + 'description' => is_null($description) ? "" : $description, + 'credit' => $credit, + 'debit' => $debit, + 'pay_for' => $system, + 'system_references' => is_null($systemReference) ? "" : $systemReference, + 'remark_references' => is_null($row['remarkreferences']) ? "" : $row['remarkreferences'], + 'is_multiple' => $multiple == "Yes" ? 1 : 0, + 'is_matches' => $matches == "Yes" ? 1 : 0, + 'system_amounts'=> is_null($systemAmount) ? "" : $systemAmount, + ]); + + $statementTransactionsDetail->save(); + + if($count == 10){ + break; + } + } + + $table .= '
'.implode('', $headers).'
'.$date->format('d-m-Y').'branch'.$description.''.$credit.''.$debit.''.$row['pay_for'].''.$system.''.$systemReference.''.$row['remarkreferences'].''.$multiple.''.$matches.''.$systemAmount.'
'; + + return $table; + } + + 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 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, + ]; + } + + private function getTransactionsFromShippingPortal($amount, $dateRange){ + + $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']; + // $filters = [ + // ['field' => 'created_at', 'value' => '2023-03-01 08:07:00'], + // ]; + // $transactions2 = $this->getTransactions3($transactions2, $filters); + return $transactions2; + } + +} diff --git a/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php b/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php new file mode 100644 index 00000000..9f242704 --- /dev/null +++ b/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php b/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php new file mode 100644 index 00000000..dd45e571 --- /dev/null +++ b/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/Imports/ImportStatementInvoiceController.php b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php new file mode 100644 index 00000000..a59f9463 --- /dev/null +++ b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php @@ -0,0 +1,99 @@ +input('files'), '', ApprovalStatus::APPROVED, 'imports'); + $file = json_decode($object->getFiles()[0])->file_info->original->file; + + $import = new GenericImport(); + Excel::import($import, $file); + $excelRows = $import->rows; + $excelRows = $excelRows->toArray(); + + foreach ($excelRows as $row) { + dd($row); + // $row['debtor_code'] + + // attempt 1 - try map by amount and date + // $transactionDate = $this->changeExcelDate($row['date']); + // $transaction = Transaction::where('original_amount', $row['total'])->whereDate('created_at', $transactionDate)->get(); + // if ($transaction) { + // // check company + // // $company = Company::where('debtor', $row['debtor_code'])->first(); + // // dd($company); + // // try to verify is it the correct transaction + // } + + // Shipping Info + // TOPUP -> map with transaction.bill_no + if (str_starts_with($row['shipping_info'], 'TOPUP')) { + // find in exchange first, if cannont then find in izyim + // (App()->make(ChecksBillNumber::class))->execute($bill_no, 'exchange'); + } + + // if 5 digits -> exchange booking reference + // find transation + // find statement_transaction_owners, and fill up the details + + // if <5 digits, find the transaction id (order number in izyim), find the payment in izyim + // find transation + // find statement_transaction_owners, and fill up the details + + // dd([ + // 'type' => $statementTransactionOwnerType, + // 'system' => $system, + // // 'owner_type' => Transaction::class, + // // todo-new: make sure owner_type is a class + // 'owner_type' => $owner_type, + // 'owner_id' => $owner_id, + // 'owner_reference' => $owner_reference + // ]); + + // $bankStatementTransaction->owners()->firstOrCreate([ + // 'type' => $statementTransactionOwnerType, + // 'system' => $system, + // // 'owner_type' => Transaction::class, + // // todo-new: make sure owner_type is a class + // 'owner_type' => $owner_type, + // 'owner_id' => $owner_id, + // 'owner_reference' => $owner_reference + // ]); + + + } + } + + public function changeExcelDate($date) + { + $unixTime = (($date - 25569) * 86400); + $date = new DateTime("@$unixTime"); + return $date->format('Y-m-d'); // Change the format to 'Y-m-d' + } +} diff --git a/app/Http/Controllers/Imports/ImportStatementReceiptsController.php b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php new file mode 100644 index 00000000..22111881 --- /dev/null +++ b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php @@ -0,0 +1,50 @@ +input('files'), '', ApprovalStatus::APPROVED, 'imports'); + $file = json_decode($object->getFiles()[0])->file_info->original->file; + + $import = new GenericImport(); + Excel::import($import, $file); + $excelRows = $import->rows; + $excelRows = $excelRows->toArray(); + + foreach ($excelRows as $row) { + // if has date column + // $transactionDate = $this->changeExcelDate($row['date']); + } + } + + public function changeExcelDate($date) + { + $unixTime = (($date - 25569) * 86400); + $date = new DateTime("@$unixTime"); + return $date->format('Y-m-d'); // Change the format to 'Y-m-d' + } +} diff --git a/app/Http/Resources/BankStatementTransactionOwnerResource.php b/app/Http/Resources/BankStatementTransactionOwnerResource.php new file mode 100644 index 00000000..468d9ec5 --- /dev/null +++ b/app/Http/Resources/BankStatementTransactionOwnerResource.php @@ -0,0 +1,60 @@ +system === 'EXCHANGE') { + if($this->owner_type === Transaction::class){ + if($this->type === StatementTransactionOwnerType::SALES){ + $referenceLink = route('booking.details', $this->owner_reference); + } + + if($this->type === StatementTransactionOwnerType::WALLET_TOP_UP){ + $referenceLink = route('booking.details', $this->owner_reference); + } + } + } + + if($this->system === 'SHIPPING_PORTAL') { + if($this->owner_type === Transaction::class){ + if($this->type === StatementTransactionOwnerType::SALES){ + $referenceLink = 'https://izyim.cief-malaysia.com/order/show/'. $this->owner_reference; + } + if($this->type === StatementTransactionOwnerType::WALLET_TOP_UP){ + $referenceLink = 'https://izyim.cief-malaysia.com/wallet/'. $this->owner_reference .'/details'; + } + } + } + + return [ + 'id' => $this->id, + 'type' => $this->type, + 'system' => $this->system, + 'owner_type' => $this->owner_type, + 'owner_id' => $this->owner_id, + 'reference' => $this->owner_reference, + 'reference_link' => $referenceLink, + '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..993b8c93 --- /dev/null +++ b/app/Http/Resources/BankStatementTransactionResource.php @@ -0,0 +1,41 @@ + $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' => [ + 'approved' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::APPROVED])->get()), + 'pending_verification' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION])->get()), + 'rejected' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::REJECTED])->get()) + ] + ]; + } +} diff --git a/app/Models/AccountStatement.php b/app/Models/AccountStatement.php new file mode 100644 index 00000000..d9e53d60 --- /dev/null +++ b/app/Models/AccountStatement.php @@ -0,0 +1,35 @@ + 'date', + 'date_to' => 'date', + ]; + + public function account() + { + return $this->belongsTo(StatementAccount::class, 'statement_account_id', 'id'); + } + + public function transactions() + { + return $this->hasMany(StatementTransaction::class); + } +} diff --git a/app/Models/StatementAccount.php b/app/Models/StatementAccount.php new file mode 100644 index 00000000..741d2c5f --- /dev/null +++ b/app/Models/StatementAccount.php @@ -0,0 +1,23 @@ +hasMany(AccountStatement::class); + } +} diff --git a/app/Models/StatementTransaction.php b/app/Models/StatementTransaction.php new file mode 100644 index 00000000..80d6759f --- /dev/null +++ b/app/Models/StatementTransaction.php @@ -0,0 +1,50 @@ + '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 owners() + { + return $this->hasMany(StatementTransactionOwner::class); + } +} diff --git a/app/Models/StatementTransactionOwner.php b/app/Models/StatementTransactionOwner.php new file mode 100644 index 00000000..52e8d1fc --- /dev/null +++ b/app/Models/StatementTransactionOwner.php @@ -0,0 +1,29 @@ +belongsTo(StatementTransaction::class, 'statement_transaction_id', 'id'); + } +} diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index cf2dddb9..eba18ee5 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -26,8 +26,7 @@ class Transaction extends AbstractModel implements Documentable, Transactionable use LogData; protected $casts = [ - 'type' => 'int', - 'status' => 'int', + 'type' => 'int' ]; protected $table = 'transactions'; diff --git a/config/perfexcrm.php b/config/perfexcrm.php index f67040f5..7644a489 100644 --- a/config/perfexcrm.php +++ b/config/perfexcrm.php @@ -1,7 +1,7 @@ env('PERFEXCRM_BASE_URL', 'http://192.168.1.100:8084'), //cief todo: Update crm api domain here + 'base_url' => env('PERFEXCRM_BASE_URL', 'http://192.168.1.101:8084'), //cief todo: Update crm api domain here 'api_key' => env('PERFEXCRM_API_KEY', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZXhjaGFuZ2Utc2hpcHBpbmciLCJuYW1lIjoiRXhjaGFuZ2UgYW5kIFNoaXBwaW5nIFBvcnRhbCIsIkFQSV9USU1FIjoxNjc1MDg2Mzc4fQ.SGAHWl5stcxQwp55TBGeMRVTdlLeWQIbsvJh5glyVvs'), 'is_enabled' => env('PERFEXCRM_IS_ENABLED', 'true'), ]; diff --git a/database/migrations/2023_03_26_190633_create_statement_accounts_table.php b/database/migrations/2023_03_26_190633_create_statement_accounts_table.php new file mode 100644 index 00000000..348c5ec0 --- /dev/null +++ b/database/migrations/2023_03_26_190633_create_statement_accounts_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('number')->unique(); + $table->string('type'); + $table->string('name'); + $table->string('currency'); + $table->timestamps(); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('statement_accounts'); + } +} diff --git a/database/migrations/2023_03_26_190827_create_account_statements_table.php b/database/migrations/2023_03_26_190827_create_account_statements_table.php new file mode 100644 index 00000000..43945692 --- /dev/null +++ b/database/migrations/2023_03_26_190827_create_account_statements_table.php @@ -0,0 +1,39 @@ +id(); + $table->unsignedBigInteger('statement_account_id'); + $table->date('date_from'); + $table->date('date_to'); + $table->float('total_amount'); + $table->float('begin_balance'); + $table->float('end_balance'); + $table->timestamps(); + + $table->foreign('statement_account_id')->references('id')->on('statement_accounts'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('account_statements'); + } +} 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 new file mode 100644 index 00000000..29cacb80 --- /dev/null +++ b/database/migrations/2023_03_26_190934_create_statement_transactions_table.php @@ -0,0 +1,47 @@ +id(); + $table->unsignedBigInteger('account_statement_id'); + $table->dateTime('transaction_date')->nullable(); + $table->dateTime('posting_date'); + $table->string('transaction_description')->nullable(); + $table->string('transaction_description_2')->nullable(); + $table->string('transaction_description_3')->nullable(); + $table->string('transaction_description_4')->nullable(); + $table->string('transaction_description_5')->nullable(); + $table->string('transaction_ref')->nullable(); + $table->float('amount', 15, 2)->unsigned(false); + $table->string('teller_id')->nullable(); + $table->string('branch_channel'); + $table->string('transaction_code'); + $table->string('end_balance')->nullable(); + $table->timestamps(); + + $table->foreign('account_statement_id')->references('id')->on('account_statements'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('statement_transactions'); + } +} 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..5d22c520 --- /dev/null +++ b/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php @@ -0,0 +1,45 @@ +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('owner_reference')->nullable(); + $table->string('invoice_reference')->nullable(); + $table->string('receipt_reference')->nullable(); + $table->string('is_auto_mapped')->default(false); + $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/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue b/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue new file mode 100644 index 00000000..ed3f8388 --- /dev/null +++ b/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue @@ -0,0 +1,141 @@ + + + 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..db2fc252 --- /dev/null +++ b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue @@ -0,0 +1,161 @@ + + + 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/StatementTransactionsDetailsComponent.vue b/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue new file mode 100644 index 00000000..9a5e0058 --- /dev/null +++ b/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue @@ -0,0 +1,232 @@ + + 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..6e9e7982 --- /dev/null +++ b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue @@ -0,0 +1,268 @@ + + diff --git a/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue b/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue new file mode 100644 index 00000000..a236bf8a --- /dev/null +++ b/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue @@ -0,0 +1,134 @@ + + + \ No newline at end of file diff --git a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue index 702745c7..79dddba0 100644 --- a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue +++ b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue @@ -33,6 +33,11 @@
{{ item.status === 2 ? 'Received' : item.status === 4 ? 'Rejected' : 'Submitted'}} On: {{item.updated_at}}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -86,6 +91,11 @@
{{ item.transaction_bill.status === 1 ? 'Paid On: ' + item.updated_at : 'Transferred On:' + item.transaction_bill.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
diff --git a/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue b/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue index e670a450..0fc1cd49 100644 --- a/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue +++ b/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue @@ -28,7 +28,7 @@ v-bind:key="currency.id">
+ @click="updateCurrency(currency)">
@@ -59,6 +59,19 @@ diff --git a/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue b/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue index f827134d..39f84398 100644 --- a/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue +++ b/resources/assets/vue/components/wallets/elements/WalletTopUpHistoryComponent.vue @@ -19,13 +19,18 @@
MYR {{(Math.round((item.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
-
+
Submitted On: {{ item.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -66,6 +71,11 @@
Paid On: {{ item.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -110,6 +120,11 @@
Rejected On: {{ item.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
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..ed1f0db0 --- /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/details.blade.php b/resources/views/pages/accounting/bank-statements/details.blade.php new file mode 100644 index 00000000..ac83c27b --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/details.blade.php @@ -0,0 +1,8 @@ +@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 new file mode 100644 index 00000000..cb38d912 --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/index.blade.php @@ -0,0 +1,370 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+
+ + {{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
Complete
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{-- --}} +{{-- --}} +{{-- --}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +{{--
--}} +
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Bank Statements
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Transactions Mapping
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Miscellaneous Transactions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
Reports & Analysis
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + + + + + + + + + @foreach($accounts as $account) + + + + + + @endforeach + +
AccountCurrency
{{$account->number}}
{{$account->type}}
{{$account->currency}} + Open +
+
+
+
+
+ + + + + + + + + + + + + + + + + @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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+@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/resources/views/pages/accounting/bank-statements/show.blade.php b/resources/views/pages/accounting/bank-statements/show.blade.php new file mode 100644 index 00000000..31a50f2d --- /dev/null +++ b/resources/views/pages/accounting/bank-statements/show.blade.php @@ -0,0 +1,123 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+
+
Statement: {{ $statement->date_from->format('M d, Y') }} - {{ $statement->date_to->format('M d, Y') }}
+

Account: {{ $statement->account->name }} ({{ $statement->account->number }})

+{{--

Total Debit: {{ number_format($statement->total_debit, 2) }}

--}} +{{--

Total Credit: {{ number_format($statement->total_credit, 2) }}

--}} +

Beginning Balance: {{ number_format($statement->begin_balance, 2) }}

+

Ending Balance: {{ number_format($statement->end_balance, 2) }}

+
+
+ +
+
+
Transactions
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + @foreach ($transactions as $transaction) + @php + $from = $transaction->transaction_description_2; + $type = ''; + $paymentMethod = ''; + + if(!$transaction->transaction_description_2){ + $from = $transaction->transaction_description; + } + + if(str_contains($transaction->transaction_description_3, 'FPX')){ + $paymentMethod = 'FPX'; + } + + if(str_contains($transaction->transaction_description_3, 'A/C')){ + $paymentMethod = 'Bank Transfer'; + } + + if(str_contains($transaction->transaction_description_3, 'IBFT')){ + $paymentMethod = 'Bank Transfer'; + } + + if(str_contains($transaction->transaction_description_3, 'FOREIGN TT')){ + $paymentMethod = 'International Transfer'; + } + + if($transaction->amount > 0) { + $type = 'Sale'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description_3, 'ESI')) { + $type = 'Statutory Payment'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description_3, 'ESI')) { + $type = 'Statutory Payment'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description, 'DR DUITNOW S/CHRG')) { + $type = 'Bank Charge'; + $paymentMethod = 'Bank Deduction'; + + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description, 'CMS - DR CORP CHG')) { + $type = 'Card Payment'; + $paymentMethod = 'Card'; + } + + if($transaction->amount < 0 && str_contains($transaction->transaction_description_3, 'ELECTRONIC')) { + $type = 'Electric Bill'; + } + + @endphp + + + + + + + + + + @endforeach + +
DatetimeFromTypePayment MethodDebitCredit
{{ $transaction->posting_date->format('d-m-Y') }}{{ $transaction->posting_date->format('g:i A') }}{{ $from }}{{ $type }}{{ $paymentMethod }}{{ $transaction->amount < 0 ? number_format($transaction->amount, 2) : 0.00}}{{ $transaction->amount > 0 ? number_format($transaction->amount, 2) : 0.00}}
+
+
+
+ {{ $transactions->appends(request()->query())->links() }} +
+
+@endsection + diff --git a/resources/views/pages/bookings/filter.blade.php b/resources/views/pages/bookings/filter.blade.php new file mode 100644 index 00000000..f187597c --- /dev/null +++ b/resources/views/pages/bookings/filter.blade.php @@ -0,0 +1,17 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+
+
+
+
+ Transfer Filters +
+
+ +
+
+
+
+@endsection \ No newline at end of file diff --git a/routes/accounting.php b/routes/accounting.php new file mode 100644 index 00000000..c7112947 --- /dev/null +++ b/routes/accounting.php @@ -0,0 +1,19 @@ + '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'); + }); + + Route::post('bankStatement/{id}/details/{status}', 'ApproveDuplicateBankStatementDetailsStatusController@update')->where('status', 'approve|reject')->name('bankStatement.details.status.update'); + + Route::group(['prefix' => 'statement_transaction', 'as' => 'statement_transaction.'], function () { + Route::post('/owner/group-approve', 'GroupApproveStatementTransactionController@approve')->name('owner.groupApprove'); + Route::post('/{id}/owner/{status}', 'UpdateStatementTransactionStatusController@update')->where('status', 'approve|reject')->name('owner.status.update'); + }); +}); diff --git a/routes/api.php b/routes/api.php index b3e0002c..f22a6c7b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -30,6 +30,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function Route::get('/storage/{fileName}/fetch', 'Documents\RenderDocumentController@fileStorageServe')->where(['fileName' => '.*'])->name('storage.document.file'); Route::post('/import/update-debtor/f614e339d7058904a831aad742e24d55', 'Imports\ImportUpdateDebtorController@import')->name('debtor.import'); Route::post('/import/upload-honey-trap', 'Imports\ImportHoneyTrapController@import')->name('honey_trap.upload'); + Route::post('/import/upload-import-invoices', 'Imports\ImportStatementInvoiceController@import')->name('import_invoices.upload'); + Route::post('/import/upload-import-receipt', 'Imports\ImportStatementReceiptsController@import')->name('import_receipts.upload'); require __DIR__ . '/company.php'; @@ -55,6 +57,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function require __DIR__ . '/wallet.php'; + require __DIR__ . '/accounting.php'; + // require __DIR__ . '/rate.php'; // require __DIR__ . '/receipt.php'; diff --git a/routes/web.php b/routes/web.php index 2547d087..93a87218 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,6 @@ name('bookings'); +Route::get('/list-transfer', function () { + return view('pages.bookings.filter'); +})->name('list_transfer'); + Route::get('/bookings/urgent', function () { return view('pages.urgent_list'); })->name('bookings.urgent'); @@ -557,6 +562,18 @@ Route::get('/currency-rate-history', function () { return view('pages.rate_histories')->with('paymentMethods', $paymentMethods); })->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/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'); Route::get('/po/outsource/check', function(){ @@ -600,4 +617,4 @@ Route::get('/po/outsource/check', function(){ Route::get('/upload-honey-trap', function () { return view('pages.honey_trap'); -})->name('upload_honey_trap'); \ No newline at end of file +})->name('upload_honey_trap');