Merge branch 'master' of gitlab.com:CIEFWorldwideSdnBhd/exchange-2.0 into dillon/voucherify-35

# Conflicts:
#	routes/api.php
This commit is contained in:
edmondlang
2023-06-22 23:09:50 +08:00
88 changed files with 5423 additions and 85 deletions
@@ -0,0 +1,19 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class DateIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereIn('date', $value);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class HasAccountStatementId implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('statementTransaction', function ($query) use ($value) {
$query->where('account_statement_id', $value);
});
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class IsMapped implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $value ? $builder->whereHas('owners') : $builder->whereDoesntHave('owners');
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Illuminate\Database\Eloquent\Builder;
class IsMappedWithMultiple implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
$query = $builder->withCount(['owners' => function ($query){
$query->where('status', ApprovalStatus::PENDING_VERIFICATION);
}]);
return $value ? $query->having('owners_count', '>', 1) : $query->having('owners_count', '=', 1);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class MaxAmount implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('amount', '<=', $value);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class MinAmount implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('amount', '>=', $value);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class OwnerId implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('owner_id', $value);
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class PayFor implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('pay_for', $value);
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class PayForIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereIn('pay_for', $value);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionAccountId implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('account', function ($query) use ($value) {
$query->where('statement_accounts.id', $value);
});
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionOwnerStatusIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) use ($value) {
return $query->whereIn('statement_transaction_owners.status', $value);
});
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionOwnerTypeIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) use ($value) {
return $query->whereIn('type', $value);
});
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Classes\Jobs;
use App\Classes\Modules\Accounting\Processors\CreateBankStatementTransactionOwnersProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class CreateBankStatementTransactionOwners implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
(App()->make(CreateBankStatementTransactionOwnersProcessor::class))->execute();
}
public function delay($delay)
{
// Add delay in seconds to the job
$this->delay = $delay;
return $this;
}
}
+16 -14
View File
@@ -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);
}
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ class UpdatePerfexCRMInvoice implements ShouldQueue
/**
* CreatePerfexCRMSingleTask constructor.
* @param UpdatePerfexCRMInvoiceObject $createTaskPerfexCRMObject
* @param UpdatePerfexCRMInvoiceObject $updatePerfexCRMInvoiceObject
*/
public function __construct(UpdatePerfexCRMInvoiceObject $updatePerfexCRMInvoiceObject)
{
+34 -34
View File
@@ -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) {
@@ -0,0 +1,283 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransaction;
use App\Http\Resources\BankStatementTransactionResource;
use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\Modules\Accounting\Services\FetchesBankStatementDetails;
use App\Classes\Modules\Accounting\Services\ListsBankStatementDetails;
use App\Models\StatementTransactionOwner;
use Illuminate\Support\Collection;
class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => '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);
}
}
}
@@ -0,0 +1,86 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\ListsBankStatementTransactions;
use App\Http\Resources\BankStatementTransactionResource;
use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
class GroupApproveStatementTransactionLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => '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([]);
}
}
@@ -0,0 +1,147 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Jobs\CreateBankStatementTransactionOwners;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\AccountStatement;
use App\Models\StatementAccount;
use App\Models\StatementTransaction;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Maatwebsite\Excel\Facades\Excel;
class ImportBankStatementLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => '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([]);
}
}
@@ -0,0 +1,52 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\ListsBankStatementDetails;
use App\Http\Resources\BankStatementDetailResource;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListBankStatementDetailsLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => '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));
}
}
@@ -0,0 +1,53 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\ListsBankStatementDetails;
use App\Classes\Modules\Accounting\Services\ListsBankStatementTransactions;
use App\Http\Resources\BankStatementTransactionResource;
use App\Models\StatementTransaction;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListBankStatementTransactionsLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => '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));
}
}
@@ -0,0 +1,137 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\CreatesBankStatementTransactionOwner;
use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransaction;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use App\Classes\ValueObjects\Constants\SystemType;
use App\Classes\Modules\Accounting\Processors\ChecksBillNumber;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
use App\Models\Transaction;
use App\Models\Wallet;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UpdateBankStatementDetailLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => '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);
}
}
@@ -0,0 +1,81 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransaction;
use App\Http\Resources\BankStatementTransactionResource;
use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
class UpdateStatementTransactionStatusLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => '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));
}
}
@@ -0,0 +1,137 @@
<?php
namespace App\Classes\Modules\Accounting\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use PhpOffice\PhpSpreadsheet\Calculation\Logical\Boolean;
class BankStatementTransactionObject implements DataTransferObject
{
/** @var int */
private $statement_transaction_id;
/** @var int */
private $type;
/** @var string */
private $system;
/** @var string */
private $owner_type;
/** @var int */
private $owner_id;
/** @var string */
private $invoice_reference;
/** @var string */
private $receipt_reference;
/** @var Boolean */
private $is_auto_mapped;
/** @var int|null */
private $status;
/**
* BankStatementDetailObject constructor.
* @param string $pay_for
* @param string $system_references
* @param int|null $type
*/
public function __construct(
$statement_transaction_id,
$type,
$system,
$owner_type,
$owner_id,
$invoice_reference,
$receipt_reference,
$is_auto_mapped,
?int $status = ApprovalStatus::PENDING_VERIFICATION
) {
$this->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;
}
}
@@ -0,0 +1,46 @@
<?php
namespace App\Classes\Modules\Accounting\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class BankStatementTrasactionOwnerObject implements DataTransferObject
{
/** @var string */
private $pay_for;
/** @var string */
private $system_references;
// /** @var int|null */
// private $type;
/**
* BankStatementDetailObject constructor.
* @param string $pay_for
* @param string $system_references
* @param int|null $type
*/
public function __construct(string $pay_for, string $system_references)
{
$this->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;
}
}
@@ -0,0 +1,41 @@
<?php
namespace App\Classes\Modules\Accounting\Processors;
use App\Classes\Exceptions\MalformedRequestException;
use App\Models\Transaction;
use Illuminate\Support\Facades\Log;
class ChecksBillNumber
{
public function execute($bill_no, $system_reference)
{
if ($system_reference == 'izyim') {
try {
$url = 'https://izyim.cief-malaysia.com/public/api/v1/transactions/mappable/query';
$client = new \GuzzleHttp\Client(['verify' => 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.');
}
}
@@ -0,0 +1,255 @@
<?php
namespace App\Classes\Modules\Accounting\Processors;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
use App\Models\Company;
use App\Models\Group;
use App\Models\StatementTransaction;
use App\Models\Transaction;
use App\Models\Wallet;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class CreateBankStatementTransactionOwnersProcessor
{
/**
* @return void
*/
public function execute() {
$transactions = StatementTransaction::whereDoesntHave('owners', function($query){
return $query->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,
];
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Accounting\DataTransferObjects\BankStatementTransactionObject;
use App\Models\StatementTransactionOwner;
class CreatesBankStatementTransactionOwner extends AbstractUpdateRecord
{
/**
* @param StatementTransactionOwner $model
* @param BankStatementDetailsObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(BankStatementTransactionObject $object)
{
$model = new StatementTransactionOwner();
$model->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);
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\StatementTransactionOwner;
class FetchesBankStatementDetails extends AbstractFetchRecord
{
/** @var StatementTransactionOwner */
private $repository;
/**
* FetchesBankStatementDetails constructor.
* @param StatementTransactionOwner $repository
*/
public function __construct(StatementTransactionOwner $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\StatementTransaction;
class FetchesBankStatementTransaction extends AbstractFetchRecord
{
/** @var StatementTransaction */
private $repository;
/**
* FetchesBankStatementDetails constructor.
* @param StatementTransactionOwner $repository
*/
public function __construct(StatementTransaction $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\StatementTransactionOwner;
class ListsBankStatementDetails extends AbstractListRecord
{
/** @var StatementTransactionOwner */
private $repository;
/**
* ListsBankStatementDetails constructor.
* @param StatementTransactionOwner $repository
*/
public function __construct(StatementTransactionOwner $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use App\Models\StatementTransaction;
use Illuminate\Database\Eloquent\Builder;
use App\Models\StatementTransactionOwner;
class ListsBankStatementTransactions extends AbstractListRecord
{
/** @var StatementTransaction */
private $repository;
/**
* ListsBankStatementDetails constructor.
* @param StatementTransaction $repository
*/
public function __construct(StatementTransaction $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Accounting\DataTransferObjects\BankStatementDetailObject;
use App\Models\StatementTransactionOwner;
class UpdatesBankStatementDetails extends AbstractUpdateRecord
{
/**
* @param StatementTransactionOwner $model
* @param BankStatementDetailsObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(StatementTransactionOwner $model, BankStatementDetailObject $object)
{
$model->system_references = $object->getSystemReferences();
$model->pay_for = $object->getPayFor();
return $this->handler($model);
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Accounting\DataTransferObjects\BankStatementTransactionObject;
use App\Models\StatementTransactionOwner;
class UpdatesBankStatementTransactionOwner extends AbstractUpdateRecord
{
/**
* @param StatementTransactionOwner $model
* @param BankStatementDetailsObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(StatementTransactionOwner $model, BankStatementTransactionObject $object)
{
$model->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);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Models\StatementTransactionOwner;
class UpdatesBankStatementTransactionOwnerStatus extends AbstractUpdateRecord
{
/**
* @param StatementTransactionOwner $model
* @param BankStatementDetailsObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(StatementTransactionOwner $model, int $status)
{
$model->status = $status;
return $this->handler($model);
}
}
@@ -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]);
@@ -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'){
@@ -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
}
}
@@ -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
]);
}
}
}
@@ -0,0 +1,82 @@
<?php
namespace App\Classes\Modules\Imports\Services;
use App\Models\AccountStatement;
use App\Models\StatementAccount;
use App\Models\StatementTransaction;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class BankStatementImport implements ToCollection, WithHeadingRow
{
public function collection(Collection $rows)
{
$accountNumber = null;
foreach ($rows as $row) {
if ($row->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();
}
}
}
}
@@ -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;
@@ -0,0 +1,246 @@
<?php
namespace App\Classes\Modules\PerfexCRM\Processors;
use App\Classes\Modules\PerfexCRM\DataTransferObjects\UpdatePerfexCRMObject;
use App\Classes\ValueObjects\Constants\PerfexCRMTasks;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\PerfexCRMProjectStatus;
use App\Classes\ValueObjects\Constants\PerfexCRMTaskStatus;
use App\Classes\Jobs\UpdatePerfexCRMInvoice;
use App\Classes\Jobs\UpdatePerfexCRM;
use App\Classes\Jobs\UpdatePerfexCRMPrelude;
use App\Models\Booking;
use App\Models\Transaction;
use Illuminate\Support\Facades\Log;
class TransactionToPerfexCRMProcessorV2
{
public function execute(Transaction $model, int $status)
{
try {
if(in_array($model->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];
}
}
@@ -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']);
@@ -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);
}
}
}
@@ -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
@@ -23,6 +23,7 @@ class FileType
'application/pdf' => 'pdf',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'excel',
'application/vnd.ms-excel' => 'excel',
'text/plain' => 'text',
];
}
}
@@ -43,9 +43,9 @@ class PerfexCRMTasks
○ Outcomes: The payment will be approved in exchange, allowing the next steps in the process to be initiated.<br>',
'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}<br>',
'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.<br>',
'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.<br>',
'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}<br>',
'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.<br>
○ Initial Status: Not Started<br>
○ Deadline: Same day<br>
@@ -493,7 +493,7 @@ class PerfexCRMTasks
○ Outcomes: Bank transaction is mapped successfully, allowing the next steps in the process to be initiated.<br>',
'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,
@@ -0,0 +1,37 @@
<?php
namespace App\Classes\ValueObjects\Constants;
final class StatementTransactionOwnerType {
public const UNKNOWN = 0;
public const SALES = 1;
public const WALLET_TOP_UP = 2;
public const WALLET_WITHDRAWAL = 3;
public const CUSTOMER_REFUND = 4;
public const SUPPLIER_PAYMENT = 5;
public const INTERNAL_BANK_TRANSFER_OUT = 6;
public const INTERNAL_BANK_TRANSFER_IN = 7;
public const SALARY = 8;
public const STATUTORY = 9;
public const FPX_CHARGE = 10;
public const FPX_CHARGE_REFUND = 11;
public const BANK_CHARGE = 12;
public const CREDIT_CARD_PAYMENT = 13;
public const NON_OPERATIONAL = 14;
}
@@ -0,0 +1,16 @@
<?php
namespace App\Classes\ValueObjects\Constants;
final class SystemType {
public const EXCHANGE = 'EXCHANGE';
public const SHIPPING_PORTAL = 'SHIPPING_PORTAL';
public const SYSTEM_NAMES = [
'exchange' => self::EXCHANGE,
'shipping_portal' => self::SHIPPING_PORTAL,
'izyim' => self::SHIPPING_PORTAL,
];
}