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/Jobs/UpdatePerfexCRM.php b/app/Classes/Jobs/UpdatePerfexCRM.php
index 059779e1..833fb365 100644
--- a/app/Classes/Jobs/UpdatePerfexCRM.php
+++ b/app/Classes/Jobs/UpdatePerfexCRM.php
@@ -10,7 +10,8 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Classes\Modules\PerfexCRM\DataTransferObjects\UpdatePerfexCRMObject;
use App\Classes\Modules\PerfexCRM\DataTransferObjects\UpdatePerfexCRMInvoiceObject;
-
+use Illuminate\Support\Facades\Log;
+use PhpOffice\PhpSpreadsheet\Calculation\Logical\Boolean;
class UpdatePerfexCRM implements ShouldQueue
{
@@ -19,38 +20,39 @@ class UpdatePerfexCRM implements ShouldQueue
/** @var UpdatePerfexCRMObject */
private $updatePerfexCRMObject;
- /** @var UpdatePerfexCRMInvoice */
- private $nextJob;
-
- /** @var */
+ /** @var */
private $transaction;
+ /** @var Boolean|null */
+ private $shouldCreateInvoice;
+
/**
- * UpdatePerfexCRM constructor.
* @param UpdatePerfexCRMObject $updatePerfexCRMObject
- * @param UpdatePerfexCRMInvoice $nextJob
* @param $transaction
+ * @param bool|null $shouldCreateInvoice
*/
- public function __construct(UpdatePerfexCRMObject $updatePerfexCRMObject, $transaction, $nextJob)
+ public function __construct(UpdatePerfexCRMObject $updatePerfexCRMObject, $transaction, ?bool $shouldCreateInvoice = false)
{
$this->updatePerfexCRMObject = $updatePerfexCRMObject;
- $this->nextJob = $nextJob;
$this->transaction = $transaction;
+ $this->shouldCreateInvoice = $shouldCreateInvoice;
}
+
public function handle()
{
$result = (App()->make(UpdatePerfexCRMProcessor::class))->execute($this->updatePerfexCRMObject);
- /** @var UpdatePerfexCRMInvoice $nextJob*/
- if($this->nextJob != null && $this->transaction != null){
- $updatePerfexCRMInvoiceOject = new UpdatePerfexCRMInvoiceObject(
+ if($this->transaction != null && $this->shouldCreateInvoice){
+ $updatePerfexCRMInvoiceObject = new UpdatePerfexCRMInvoiceObject(
$this->updatePerfexCRMObject->getContactEmail(),
$this->updatePerfexCRMObject->getProjectName(),
$result->projectId,
$this->transaction,
- true,
+ true
);
- $this->nextJob::dispatch($updatePerfexCRMInvoiceOject);
+
+ UpdatePerfexCRMInvoice::dispatch($updatePerfexCRMInvoiceObject);
+
}
}
}
diff --git a/app/Classes/Jobs/UpdatePerfexCRMInvoice.php b/app/Classes/Jobs/UpdatePerfexCRMInvoice.php
index 8c047eb5..ee395033 100644
--- a/app/Classes/Jobs/UpdatePerfexCRMInvoice.php
+++ b/app/Classes/Jobs/UpdatePerfexCRMInvoice.php
@@ -30,7 +30,7 @@ class UpdatePerfexCRMInvoice implements ShouldQueue
/**
* CreatePerfexCRMSingleTask constructor.
- * @param UpdatePerfexCRMInvoiceObject $createTaskPerfexCRMObject
+ * @param UpdatePerfexCRMInvoiceObject $updatePerfexCRMInvoiceObject
*/
public function __construct(UpdatePerfexCRMInvoiceObject $updatePerfexCRMInvoiceObject)
{
diff --git a/app/Classes/Jobs/UpdatePerfexCRMPrelude.php b/app/Classes/Jobs/UpdatePerfexCRMPrelude.php
index 25afdc4d..3b05ac63 100644
--- a/app/Classes/Jobs/UpdatePerfexCRMPrelude.php
+++ b/app/Classes/Jobs/UpdatePerfexCRMPrelude.php
@@ -21,50 +21,28 @@ class UpdatePerfexCRMPrelude implements ShouldQueue
/** @var UpdatePerfexCRMObject */
private $updatePerfexCRMObject;
- /** @var UpdatePerfexCRM */
- private $nextJob1;
-
- /** @var UpdatePerfexCRMInvoice */
- private $nextJob2;
+ /** @var Boolean|null */
+ private $shouldCreateInvoice;
/**
- * UpdatePerfexCRMPrelude constructor.
* @param Transaction $transaction
* @param UpdatePerfexCRMObject $updatePerfexCRMObject
- * @param UpdatePerfexCRM $nextJob1
- * @param UpdatePerfexCRMInvoice $nextJob2
+ * @param bool|null $shouldCreateInvoice
*/
- public function __construct(Transaction $transaction, UpdatePerfexCRMObject $updatePerfexCRMObject, $nextJob1, $nextJob2)
+ public function __construct(Transaction $transaction, UpdatePerfexCRMObject $updatePerfexCRMObject, ?bool $shouldCreateInvoice = false)
{
$this->transaction = $transaction;
$this->updatePerfexCRMObject = $updatePerfexCRMObject;
- $this->nextJob1 = $nextJob1;
- $this->nextJob2 = $nextJob2;
+ $this->shouldCreateInvoice = $shouldCreateInvoice;
}
+
public function handle()
{
$serviceTypeName = $this->transaction->owner->company->services()->where('id', $this->transaction->owner->service_id)->first()->name;
$booking = $this->transaction->booking;
- $bank = $booking->bank;
- $bankDetails = $this->transaction->owner->service_id == 4 ?
- [
- '1688_username' => $bank->account_no,
- '1688_password' => $bank->holder_name,
- 'payment_pin' => $bank->bank_branch,
- ]:
- [
- 'id' => $bank->id,
- 'type' => $bank->type,
- 'reference' => $bank->reference,
- 'bank_name' => $bank->bank_name,
- 'bank_branch' => $bank->bank_branch,
- 'holder_name' => $bank->holder_name,
- 'account_no' => $bank->account_no,
- 'country_id' => $bank->country_id,
- 'default' => $bank->default,
- 'status' => $bank->status,
- ];
+ $bankDetails = $this->generateBankDetails($booking->bank);
+
$data = [
'amount' => number_format($this->transaction->amount, 2, '.', ''),
'currency' => $this->transaction->currency_id == 1 ? "MYR" : "CNY",
@@ -75,14 +53,36 @@ class UpdatePerfexCRMPrelude implements ShouldQueue
'link_autocount_or' => 'https://docs.google.com/spreadsheets/d/1Q3rJBGQ9Bo04HZp5WR9zbLp7pIW2kfDYsabxG7JON-4/edit#gid=2013983170',
];
-
$result = $this->replacePlaceholders($this->updatePerfexCRMObject->getTasks(), $data);
$this->updatePerfexCRMObject->setTasks($result);
- /** @var UpdatePerfexCRM $nextJob1 */
- /** @var UpdatePerfexCRMInvoice $nextJob2 */
- $this->nextJob1::dispatch($this->updatePerfexCRMObject, $this->transaction, $this->nextJob2);
+
+ UpdatePerfexCRM::dispatch($this->updatePerfexCRMObject, $this->transaction, $this->shouldCreateInvoice);
}
+
+ private function generateBankDetails($bank): array
+ {
+ return $this->transaction->owner->service_id == 4 ?
+ [
+ '1688_username' => $bank->account_no,
+ '1688_password' => $bank->holder_name,
+ 'payment_pin' => $bank->bank_branch,
+ ]:
+ [
+ 'id' => $bank->id,
+ 'type' => $bank->type,
+ 'reference' => $bank->reference,
+ 'bank_name' => $bank->bank_name,
+ 'bank_branch' => $bank->bank_branch,
+ 'holder_name' => $bank->holder_name,
+ 'account_no' => $bank->account_no,
+ 'country_id' => $bank->country_id,
+ 'default' => $bank->default,
+ 'status' => $bank->status,
+ ];
+ }
+
+
function replacePlaceholders($template, $data, $prefix = '')
{
foreach ($template as $key => $value) {
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/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php
index 617691e8..8eb47681 100644
--- a/app/Classes/Modules/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php
+++ b/app/Classes/Modules/Bookings/ControllersLogic/AutoPurchaseOrderFillLogic.php
@@ -65,7 +65,7 @@ class AutoPurchaseOrderFillLogic extends AbstractControllerLogic
public function logic(Request $request) : JsonResponse
{
$bookings = Booking::where('service_id', '!=', 4)->where(function($query){
- return $query->whereMonth('created_at', '=', 04)->whereYear('created_at', 2023);
+ return $query->whereMonth('created_at', '=', 03)->whereYear('created_at', 2023);
})->whereDoesntHave('transactions', function($q){
$q->where('type', TransactionType::PURCHASE_ORDER);
$q->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED]);
diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php
index 0fbf2519..a93a3654 100644
--- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php
+++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingLogic.php
@@ -17,6 +17,7 @@ use App\Classes\Modules\PerfexCRM\Processors\BookingToPerfexCRMProcessor;
use App\Http\Resources\BookingResource;
+use App\Models\Booking;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -82,6 +83,7 @@ class CreateBookingLogic extends AbstractControllerLogic
$this->canCreateBooking->passes($object);
+ /** @var Booking $booking */
$booking = $this->createsBooking->execute($company, $object);
if(config('perfexcrm.is_enabled') == 'true'){
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/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php
index 76b01825..930d3765 100644
--- a/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php
+++ b/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php
@@ -2,6 +2,7 @@
namespace App\Classes\Modules\PerfexCRM\Processors;
+use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\PerfexCRM\DataTransferObjects\UpdatePerfexCRMObject;
use App\Classes\Jobs\UpdatePerfexCRM;
use App\Models\Booking;
@@ -11,10 +12,10 @@ class BookingToPerfexCRMProcessor
{
/**
* @param Booking $booking
- * @return \Illuminate\Database\Eloquent\Model
- * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @return bool
+ * @throws MalformedRequestException
*/
- public function execute(Booking $booking)
+ public function execute(Booking $booking): bool
{
$companyReference = $booking->company->reference;
$companyName = $booking->company->name;
@@ -37,6 +38,7 @@ class BookingToPerfexCRMProcessor
[],
[]
);
+
UpdatePerfexCRM::dispatch($updatePerfexCRMObject, null, null);
return true;
diff --git a/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessorV2.php b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessorV2.php
new file mode 100644
index 00000000..cd5990f1
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessorV2.php
@@ -0,0 +1,246 @@
+type, [TransactionType::PAYMENT, TransactionType::BILL, TransactionType::PURCHASE_ORDER])) {
+ $transaction = ($model->type === TransactionType::BILL ? $model->owner : $model);
+ $bookingInfo = $this->extractBookingInfo($transaction);
+ $projectName = 'Exchange | ' . $bookingInfo['serviceTypeName'] . ' | ' . $bookingInfo['bookingMarking'];
+ $tasks = $this->defineTasks($model, $status);
+
+ if(count($tasks) > 0) {
+ $updatePerfexCRMObject = new UpdatePerfexCRMObject(
+ $bookingInfo['companyName'],
+ $bookingInfo['companyReference'],
+ $bookingInfo['contactName'],
+ $bookingInfo['contactEmail'],
+ $bookingInfo['bookingMarking'],
+ $projectName,
+ PerfexCRMProjectStatus::IN_PROGRESS,
+ [],
+ $tasks
+ );
+ $this->dispatchUpdateJob($transaction, $status, $updatePerfexCRMObject);
+ }
+ }
+ } catch (\Exception $exception) {
+ Log::error('TransactionToPerfexCRMProcessor debug:');
+ Log::error($exception);
+ }
+ return true;
+ }
+
+ private function extractBookingInfo(Transaction $model): array
+ {
+ $bookingInfo = [];
+ $bookingInfo['companyReference'] = $model->owner->company->reference;
+ $bookingInfo['companyName'] = $model->owner->company->name;
+ $bookingInfo['contactEmail'] = $model->owner->company->employees()->first()->email;
+ $bookingInfo['contactName'] = $model->owner->company->employees()->first()->name;
+ $bookingInfo['bookingMarking'] = $model->owner->marking;
+ $bookingInfo['serviceTypeName'] = $model->owner->company->services()->where('id', $model->owner->service_id)->first()->name;
+
+ return $bookingInfo;
+ }
+
+ private function defineTasks(Transaction $model, int $status): array
+ {
+ $tasks = [];
+ $ownerServiceId = $model->type === TransactionType::BILL ? $model->owner->owner->service_id : $model->owner->service_id;
+
+ if ($status === ApprovalStatus::PENDING_VERIFICATION) {
+ $tasks = $this->handlePendingVerificationStatus($model, $ownerServiceId);
+ } elseif ($status === ApprovalStatus::APPROVED) {
+ $tasks = $this->handleApprovedStatus($model, $ownerServiceId);
+ }
+
+ return $tasks;
+ }
+
+ private function handlePendingVerificationStatus(Transaction $model, int $serviceId): array
+ {
+ $tasks = [];
+ switch($model->type) {
+ case TransactionType::PAYMENT:
+ $tasks = $this->definePaymentTasks($model);
+ break;
+ case TransactionType::PURCHASE_ORDER:
+ if ($serviceId === 1 || $serviceId === 3) {
+ $task = PerfexCRMTasks::TASK_PURCHASE_ORDER_1;
+ $task['status'] = PerfexCRMTaskStatus::IN_PROGRESS;
+ $tasks = [$task];
+ }
+ break;
+ case TransactionType::BILL:
+ $tasks = $this->handleBillPendingStatus($serviceId);
+ break;
+ }
+
+ return $tasks;
+ }
+
+ private function handleApprovedStatus(Transaction $model, int $serviceId): array
+ {
+ $tasks = [];
+ switch($model->type) {
+ case TransactionType::PAYMENT:
+ $tasks = $this->handlePaymentApprovedStatus($serviceId);
+ break;
+ case TransactionType::BILL:
+ if ($serviceId === 4) {
+ $tasks = $this->completeTask(PerfexCRMTasks::TASK_1688_PAYMENT_9, PerfexCRMTasks::TASK_1688_PAYMENT_10);
+ }
+ break;
+ }
+
+ return $tasks;
+ }
+
+ private function handlePaymentApprovedStatus(int $serviceId): array
+ {
+ $tasks = [];
+ switch ($serviceId) {
+ case 1:
+ $tasks = $this->completeTask(PerfexCRMTasks::TASK_1_DAY_TRANSFER_2, PerfexCRMTasks::TASK_1_DAY_TRANSFER_5);
+ break;
+ case 3:
+ $tasks = $this->completeTask(PerfexCRMTasks::TASK_3_DAY_TRANSFER_2, PerfexCRMTasks::TASK_3_DAY_TRANSFER_5);
+ break;
+ case 4:
+ $tasks = $this->completeTask(PerfexCRMTasks::TASK_1688_PAYMENT_2, PerfexCRMTasks::TASK_1688_PAYMENT_5);
+ break;
+ }
+
+ return $tasks;
+ }
+
+ private function handleBillPendingStatus(int $serviceId): array
+ {
+ $tasks = [];
+ switch ($serviceId) {
+ case 1:
+ $tasks = $this->completeTask(PerfexCRMTasks::TASK_1_DAY_TRANSFER_5, PerfexCRMTasks::TASK_1_DAY_TRANSFER_6);
+ break;
+ case 3:
+ $tasks = $this->completeTask(PerfexCRMTasks::TASK_3_DAY_TRANSFER_5, PerfexCRMTasks::TASK_3_DAY_TRANSFER_6);
+ break;
+ case 4:
+ $tasks = $this->completeTask(PerfexCRMTasks::TASK_1688_PAYMENT_5, PerfexCRMTasks::TASK_1688_PAYMENT_7);
+ break;
+ }
+
+ return $tasks;
+ }
+
+ private function definePaymentTasks(Transaction $model): array
+ {
+ $tasks = [
+// PerfexCRMTasks::TASK_1
+ ];
+
+ if ($model->owner->service_id === 1) {
+ $tasks = array_merge($tasks, $this->oneDayTransferTasks());
+ } elseif ($model->owner->service_id === 3) {
+ $tasks = array_merge($tasks, $this->threeDayTransferTasks());
+ } elseif ($model->owner->service_id === 4) {
+ $tasks = array_merge($tasks, $this->payment1688Tasks());
+ }
+
+ if ($model->owner->service_id === 1 || $model->owner->service_id === 3) {
+ $purchaseOrder = $model->booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->complete()->first();
+ if(is_null($purchaseOrder)){
+ $tasks = array_merge($tasks, $this->purchaseOrderTasks());
+ }
+ }
+
+ return $tasks;
+ }
+
+ private function oneDayTransferTasks(): array
+ {
+ return [
+// PerfexCRMTasks::TASK_1_DAY_TRANSFER_1,
+ PerfexCRMTasks::TASK_1_DAY_TRANSFER_2,
+// PerfexCRMTasks::TASK_1_DAY_TRANSFER_3,
+// PerfexCRMTasks::TASK_1_DAY_TRANSFER_3_1,
+// PerfexCRMTasks::TASK_1_DAY_TRANSFER_4,
+ PerfexCRMTasks::TASK_1_DAY_TRANSFER_5,
+ PerfexCRMTasks::TASK_1_DAY_TRANSFER_6
+ ];
+ }
+
+ private function threeDayTransferTasks(): array
+ {
+ return [
+// PerfexCRMTasks::TASK_3_DAY_TRANSFER_1,
+ PerfexCRMTasks::TASK_3_DAY_TRANSFER_2,
+// PerfexCRMTasks::TASK_3_DAY_TRANSFER_3,
+// PerfexCRMTasks::TASK_3_DAY_TRANSFER_3_1,
+// PerfexCRMTasks::TASK_3_DAY_TRANSFER_4,
+ PerfexCRMTasks::TASK_3_DAY_TRANSFER_5,
+ PerfexCRMTasks::TASK_3_DAY_TRANSFER_6
+ ];
+ }
+
+ private function payment1688Tasks(): array
+ {
+ return [
+// PerfexCRMTasks::TASK_1688_PAYMENT_1,
+ PerfexCRMTasks::TASK_1688_PAYMENT_2,
+// PerfexCRMTasks::TASK_1688_PAYMENT_3,
+// PerfexCRMTasks::TASK_1688_PAYMENT_3_1,
+// PerfexCRMTasks::TASK_1688_PAYMENT_4,
+ PerfexCRMTasks::TASK_1688_PAYMENT_5,
+// PerfexCRMTasks::TASK_1688_PAYMENT_6,
+ PerfexCRMTasks::TASK_1688_PAYMENT_7,
+ PerfexCRMTasks::TASK_1688_PAYMENT_8,
+ PerfexCRMTasks::TASK_1688_PAYMENT_9,
+ PerfexCRMTasks::TASK_1688_PAYMENT_10,
+ PerfexCRMTasks::TASK_1688_PAYMENT_11,
+ PerfexCRMTasks::TASK_1688_PAYMENT_12,
+// PerfexCRMTasks::TASK_1688_PAYMENT_13
+ ];
+ }
+
+ private function purchaseOrderTasks(): array
+ {
+ $potask1 = PerfexCRMTasks::TASK_PURCHASE_ORDER_1;
+ $potask1['status'] = PerfexCRMTaskStatus::NOT_STARTED;
+
+ return [
+ $potask1,
+// PerfexCRMTasks::TASK_PURCHASE_ORDER_2
+ ];
+ }
+
+ private function dispatchUpdateJob(Transaction $model, int $status, UpdatePerfexCRMObject $updatePerfexCRMObject)
+ {
+ $withInvoice = ($model->type === TransactionType::PAYMENT && $model->owner === Booking::class && $status === ApprovalStatus::APPROVED);
+ UpdatePerfexCRMPrelude::dispatch($model, $updatePerfexCRMObject, $withInvoice);
+ }
+
+ private function completeTask($startTask, $endTask) {
+ $startTask['status'] = PerfexCRMTaskStatus::COMPLETED;
+ $endTask['status'] = PerfexCRMTaskStatus::IN_PROGRESS;
+
+ return [$startTask, $endTask];
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php
index 752c9644..7527760f 100644
--- a/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php
+++ b/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php
@@ -109,7 +109,7 @@ class UpdatePerfexCRMProcessor
*/
public function execute(UpdatePerfexCRMObject $updatePerfexCRMObject) {
$projectId = "";
-
+
// Customer has to exist first before Project can appear under it
// Check with Perfex CRM, if this user (email) was previously a lead, should automatically now become a customer
$crmCompany = $updatePerfexCRMObject->getCompanyName();
@@ -205,7 +205,9 @@ class UpdatePerfexCRMProcessor
// Get existing or create task
$result = $this->fetchesPerfexCRMTask->execute($tasks[$count]['name'], $milestoneId, 'project', $projectId);
if(isset($result->payload)){
- if($taskStatus != PerfexCRMTaskStatus::NOT_STARTED && $result->payload[0]['status'] == PerfexCRMTaskStatus::NOT_STARTED)
+ //&& $result->payload[0]['status'] == PerfexCRMTaskStatus::NOT_STARTED
+ Log::info(json_encode([$taskStatus]));
+ if($taskStatus != PerfexCRMTaskStatus::NOT_STARTED )
{
$task = $result->payload[0];
$result = $this->updatesPerfexCRMTask->execute($task['id'], $task['name'], $task['milestone'], $task['rel_id'], $taskStatus, $task['startdate'], is_null($task['duedate']) ? '': $task['duedate']);
diff --git a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php
index 65a52d99..9b944a5b 100644
--- a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php
+++ b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php
@@ -90,10 +90,11 @@ class CreateSupplierTransactionProcessor
$object = new TransactionObject($billNumber, TransactionType::BILL, $supplier->id, 1,
$supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH,
$payment->original_amount * (1 / $rate), $payment->original_amount, 1, $payment->original_currency_id,
- $rate, 0, $serviceCharge, null, ApprovalStatus::PENDING_VERIFICATION);
+ $rate, 0, $serviceCharge, null, ApprovalStatus::PENDING_SUBMISSION);
/** @var Transaction $billTransaction */
$billTransaction = $this->createsTransaction->execute($payment, $object);
+ $this->updatesTransactionStatus->execute($billTransaction, ApprovalStatus::PENDING_VERIFICATION);
$this->pushBill($billTransaction);
$transferFeeNumber = $this->generatesTransactionBillNumber->execute('TRFR-');
@@ -140,4 +141,4 @@ class CreateSupplierTransactionProcessor
$this->transferFee->push($transferFee);
}
-}
\ No newline at end of file
+}
diff --git a/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php b/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php
index 2091c3cf..9d6b9fe4 100644
--- a/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php
+++ b/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php
@@ -3,23 +3,24 @@
namespace App\Classes\Modules\Transactions\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
+use App\Classes\Modules\PerfexCRM\Processors\TransactionToPerfexCRMProcessorV2;
use App\Models\Transaction;
use App\Classes\Modules\PerfexCRM\Processors\TransactionToPerfexCRMProcessor;
class UpdatesTransactionStatus extends AbstractUpdateRecord
{
- /** @var TransactionToPerfexCRMProcessor */
+ /** @var TransactionToPerfexCRMProcessorV2 */
private $transactionToPerfexCRMProcessor;
/**
- * UpdatesTransactionStatus constructor.
- * @param TransactionToPerfexCRMProcessor $transactionToPerfexCRMProcessor
+ * @param TransactionToPerfexCRMProcessorV2 $transactionToPerfexCRMProcessor
*/
- public function __construct(TransactionToPerfexCRMProcessor $transactionToPerfexCRMProcessor)
+ public function __construct(TransactionToPerfexCRMProcessorV2 $transactionToPerfexCRMProcessor)
{
$this->transactionToPerfexCRMProcessor = $transactionToPerfexCRMProcessor;
}
+
/**
* @param Transaction $model
* @param int $status
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/PerfexCRMTasks.php b/app/Classes/ValueObjects/Constants/PerfexCRMTasks.php
index 9e19ccec..4129f2a8 100644
--- a/app/Classes/ValueObjects/Constants/PerfexCRMTasks.php
+++ b/app/Classes/ValueObjects/Constants/PerfexCRMTasks.php
@@ -43,9 +43,9 @@ class PerfexCRMTasks
â—‹ Outcomes: The payment will be approved in exchange, allowing the next steps in the process to be initiated.
',
'milestone' => '',
'reference' => 'TASK_1_DAY_TRANSFER_2',
- 'on_task_completion' => 'TASK_1_DAY_TRANSFER_3,TASK_1_DAY_TRANSFER_5',
+ 'on_task_completion' => 'TASK_1_DAY_TRANSFER_5',
'department' => 'Accounts',
- 'status' => '',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
'priority' => PerfexCRMTaskPriority::HIGH,
'duedate' => 0
];
@@ -160,9 +160,9 @@ class PerfexCRMTasks
â—‹ Link to order page: {link_transfer}
',
'milestone' => '',
'reference' => 'TASK_3_DAY_TRANSFER_2',
- 'on_task_completion' => 'TASK_3_DAY_TRANSFER_3,TASK_3_DAY_TRANSFER_5',
+ 'on_task_completion' => 'TASK_3_DAY_TRANSFER_5',
'department' => 'Accounts',
- 'status' => '',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
'priority' => PerfexCRMTaskPriority::HIGH,
'duedate' => 0
];
@@ -286,9 +286,9 @@ class PerfexCRMTasks
â—‹ Outcomes: The payment will be approved in exchange, allowing the next steps in the process to be initiated.
',
'milestone' => '',
'reference' => 'TASK_1688_PAYMENT_2',
- 'on_task_completion' => 'TASK_1688_PAYMENT_3,TASK_1688_PAYMENT_5',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_5',
'department' => 'Accounts',
- 'status' => '',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
'priority' => PerfexCRMTaskPriority::HIGH,
'duedate' => 0
];
@@ -353,7 +353,7 @@ class PerfexCRMTasks
â—‹ Outcomes: The order is placed with a supplier and the customer\'s order is confirmed.
',
'milestone' => '',
'reference' => 'TASK_1688_PAYMENT_5',
- 'on_task_completion' => 'TASK_1688_PAYMENT_6',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_7',
'department' => 'Operations',
'status' => '',
'priority' => PerfexCRMTaskPriority::HIGH,
@@ -454,14 +454,14 @@ class PerfexCRMTasks
â—‹ link: {link_transfer}
',
'milestone' => '',
'reference' => 'TASK_1688_PAYMENT_12',
- 'on_task_completion' => 'TASK_1688_PAYMENT_13',
+ 'on_task_completion' => '',
'department' => 'Operations',
'status' => '',
'priority' => PerfexCRMTaskPriority::LOW,
'duedate' => 1
];
public const TASK_1688_PAYMENT_13 = [
- 'name' => 'Complete Order bookeeping',
+ 'name' => 'Complete Order bookkeeping',
'description' => 'â—‹ Purpose: To complete the bookkeeping for the booking.
â—‹ Initial Status: Not Started
â—‹ Deadline: Same day
@@ -493,7 +493,7 @@ class PerfexCRMTasks
â—‹ Outcomes: Bank transaction is mapped successfully, allowing the next steps in the process to be initiated.
',
'milestone' => '',
'reference' => 'TASK_PURCHASE_ORDER_1',
- 'on_task_completion' => 'TASK_PURCHASE_ORDER_2',
+ 'on_task_completion' => '',
'department' => 'Operations',
'status' => PerfexCRMTaskStatus::IN_PROGRESS,
'priority' => PerfexCRMTaskPriority::LOW,
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 = '
| '.implode(' | ', $headers).' | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| '.$date->format('d-m-Y').' | +branch | +'.$description.' | +'.$credit.' | +'.$debit.' | +'.$row['pay_for'].' | +'.$system.' | +'.$systemReference.' | +'.$row['remarkreferences'].' | +'.$multiple.' | +'.$matches.' | +'.$systemAmount.' | +
| ID | +Date | +Description 1 | +Pay For | +System References | +Amount | +Action | +
|---|---|---|---|---|---|---|
| {{item.id}} | +{{item.date}} | +{{item.transaction_description_1 | truncate(30, '...')}} | +{{item.pay_for}} | +{{item.system_references}} | +{{item.amount}} | ++ + + + | +
| Account | +Currency | ++ |
|---|---|---|
| {{$account->number}} {{$account->type}} |
+ {{$account->currency}} | ++ Open + | +
| Account | +Date From | +Date To | +Total Amount | +Begin Balance | +End Balance | +Actions | +
|---|---|---|---|---|---|---|
| {{ $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 + | +
| Date | +Transaction Type | +Account Name | +Description | +Status | +
|---|---|---|---|---|
| 2023-04-11 | +Deposit | +Bank Account 1 | +Salary Payment | +Pending | +
| 2023-04-09 | +Withdrawal | +Bank Account 2 | +Vendor Payment | +Reconciled | +
| 2023-04-07 | +Deposit | +Bank Account 3 | +Online Order Payment | +Exception | +
This is the first step in the wizard.
+This is the second step in the wizard.
+This is the third step in the wizard.
+This is the fourth step in the wizard.
+This is the fifth step in the wizard.
+This is the sixth step in the wizard.
+This is the final step in the wizard.
+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.
+| Account | +Date From | +Date To | +Total Amount | +Begin Balance | +End Balance | +Actions | +
|---|---|---|---|---|---|---|
| {{ $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 + | +
| Date | +Transaction Type | +Account Name | +Description | +Status | +
|---|---|---|---|---|
| 2023-04-11 | +Deposit | +Bank Account 1 | +Salary Payment | +Pending | +
| 2023-04-09 | +Withdrawal | +Bank Account 2 | +Vendor Payment | +Reconciled | +
| 2023-04-07 | +Deposit | +Bank Account 3 | +Online Order Payment | +Exception | +
| Account | +Date From | +Date To | +Total Amount | +Begin Balance | +End Balance | +Actions | +
|---|---|---|---|---|---|---|
| {{ $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 + | +
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.
+