>',
- $transaction->updated_at->format('d/m/Y'),
+ $transaction->updated_at->format('m/d/Y H:m'),
$company->debtor,
'',
'MYR',
$transaction->bill_no,
'W1',
'CREDIT SALES',
+ '',
1,
- $transaction->amount,
- '500-0000'
+ round($transaction->amount, 2),
+ '500-0000',
+ 'WALLET'
];
}
}
\ 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/Imports/Services/GenericImport.php b/app/Classes/Modules/Imports/Services/GenericImport.php
new file mode 100644
index 00000000..b2ffdb8f
--- /dev/null
+++ b/app/Classes/Modules/Imports/Services/GenericImport.php
@@ -0,0 +1,23 @@
+rows = $collection;
+ }
+}
diff --git a/app/Classes/Modules/Imports/Services/ImportsBankRecord.php b/app/Classes/Modules/Imports/Services/ImportsBankRecord.php
new file mode 100644
index 00000000..fa972e40
--- /dev/null
+++ b/app/Classes/Modules/Imports/Services/ImportsBankRecord.php
@@ -0,0 +1,62 @@
+where('type', TransactionType::PAYMENT)->where('owner_type', Booking::class)
+ ->WhereDate('created_at', $date->format('Y-m-d'))
+ ->where('amount', '>=', $credit)->where('amount', '<', ($credit + 0.01))
+ ->get();
+
+ if(count($transaction)) {
+ $systemReference = $transaction->pluck('owner.marking')->flatten()->implode(', ');
+ }
+
+ $matches = $systemReference === $collection['remarkreferences'] ? 'Yes' : 'No';
+
+ echo '
+ '.$date->format('d-m-Y').'
+ '.$collection['description'].'
+ '.$credit.'
+ '.$systemReference.'
+ '.$collection['remarkreferences'].'
+ '.$matches.'
+ ';
+
+ }
+
+ public function batchSize(): int
+ {
+ return 100;
+ }
+
+ public function rules(): array
+ {
+ return [
+
+ ];
+ }
+}
diff --git a/app/Classes/Modules/Milestones/ControllersLogic/CreateMilestoneLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/CreateMilestoneLogic.php
new file mode 100644
index 00000000..db30f789
--- /dev/null
+++ b/app/Classes/Modules/Milestones/ControllersLogic/CreateMilestoneLogic.php
@@ -0,0 +1,70 @@
+ 'Create Milestone',
+ 'message' => 'You have successfully created a milestone'
+ ];
+ }
+
+ /** @var FetchesReward */
+ private $fetchesReward;
+
+ /** @var CreateMilestoneProcessor */
+ private $createMilestoneProcessor;
+
+ /** @var AssignRewardProcessor */
+ private $assignRewardProcessor;
+
+ /**
+ * CreateMilestoneLogic constructor.
+ */
+ public function __construct(CreateMilestoneProcessor $createMilestoneProcessor, FetchesReward $fetchesReward, AssignRewardProcessor $assignRewardProcessor)
+ {
+ $this->createMilestoneProcessor = $createMilestoneProcessor;
+ $this->fetchesReward = $fetchesReward;
+ $this->assignRewardProcessor = $assignRewardProcessor;
+ }
+
+ /**
+ * @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
+ {
+ $milestone = $this->createMilestoneProcessor->execute($request);
+
+ //Extra checking to make sure that each reward exists
+ $rewardIds = $request->input('reward_ids');
+ foreach ($rewardIds as $rewardId) {
+ /** @var Reward $reward */
+ $this->fetchesReward->execute(['id' => $rewardId]);
+ }
+
+ $object = new AchievementObject($milestone, $rewardIds);
+ $this->assignRewardProcessor->execute($object);
+
+ return $this->resourceResponse(new MilestoneResource($milestone));
+ }
+}
diff --git a/app/Classes/Modules/Milestones/ControllersLogic/DeleteMilestoneLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/DeleteMilestoneLogic.php
new file mode 100644
index 00000000..5a2fbef2
--- /dev/null
+++ b/app/Classes/Modules/Milestones/ControllersLogic/DeleteMilestoneLogic.php
@@ -0,0 +1,74 @@
+ 'Delete Milestone',
+ 'message' => 'You have successfully deleted the Milestone'
+ ];
+ }
+
+ /** @var CanDeleteMilestone */
+ private $canDeleteMilestone;
+
+ /** @var DeletesMilestone */
+ private $deletesMilestone;
+
+ /** @var FetchesMilestone */
+ private $fetchesMiestone;
+
+
+ /**
+ * DeleteMilestoneLogic constructor.
+ * @param CanDeleteMilestone $canDeleteMilestone
+ * @param DeletesMilestone $deletesMilestone
+ * @param FetchesMilestone $fetchesMiestone
+ */
+ public function __construct(
+ CanDeleteMilestone $canDeleteMilestone,
+ DeletesMilestone $deletesMilestone,
+ FetchesMilestone $fetchesMiestone
+ )
+ {
+ $this->canDeleteMilestone = $canDeleteMilestone;
+ $this->deletesMilestone = $deletesMilestone;
+ $this->fetchesMiestone = $fetchesMiestone;
+ }
+
+ /**
+ * @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
+ {
+
+ $query = $this->fetchesMiestone->execute(['id' => $request->route('id')]);
+ $this->canDeleteMilestone->passes();
+ $this->deletesMilestone->execute($query);
+
+ return $this->resourceResponse(new MilestoneResource($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/ControllersLogic/ListMilestoneProgressLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestoneProgressLogic.php
new file mode 100644
index 00000000..55c78eb2
--- /dev/null
+++ b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestoneProgressLogic.php
@@ -0,0 +1,50 @@
+ 'Retrieved Milestone Progress',
+ 'message' => 'You have successfully retrieved a list of Milestone Progress'
+ ];
+ }
+
+
+ /** @var ListsMilestoneProgress */
+ private $listsMilestoneProgress;
+
+ /**
+ * ListMilestoneProgressLogic constructor.
+ * @param ListsMilestoneProgress $listsMilestoneProgress
+ */
+ public function __construct(ListsMilestoneProgress $listsMilestoneProgress)
+ {
+ $this->listsMilestoneProgress = $listsMilestoneProgress;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $query = $this->listsMilestoneProgress->execute($this->listsMilestoneProgress->deserializeFilters($request->input('filters')));
+
+ return $this->collectionResponse(MilestoneProgressResource::collection($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/ControllersLogic/ListMilestonesLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestonesLogic.php
new file mode 100644
index 00000000..202041e8
--- /dev/null
+++ b/app/Classes/Modules/Milestones/ControllersLogic/ListMilestonesLogic.php
@@ -0,0 +1,50 @@
+ 'Retrieved Milestones',
+ 'message' => 'You have successfully retrieved a list of Milestones'
+ ];
+ }
+
+
+ /** @var ListsMilestones */
+ private $listsMilestones;
+
+ /**
+ * ListMilestonesLogic constructor.
+ * @param ListsMilestones $listsMilestones
+ */
+ public function __construct(ListsMilestones $listsMilestones)
+ {
+ $this->listsMilestones = $listsMilestones;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $query = $this->listsMilestones->execute($this->listsMilestones->deserializeFilters($request->input('filters')));
+
+ return $this->collectionResponse(MilestoneResource::collection($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/ControllersLogic/UpdateMilestoneLogic.php b/app/Classes/Modules/Milestones/ControllersLogic/UpdateMilestoneLogic.php
new file mode 100644
index 00000000..a7a0c655
--- /dev/null
+++ b/app/Classes/Modules/Milestones/ControllersLogic/UpdateMilestoneLogic.php
@@ -0,0 +1,73 @@
+ 'Update Milestone',
+ 'message' => 'You have successfully updated a milestone'
+ ];
+ }
+
+ /** @var FetchesReward */
+ private $fetchesReward;
+
+ /** @var UpdateMilestoneProcessor */
+ private $updateMilestoneProcessor;
+
+ /** @var AssignRewardProcessor */
+ private $assignRewardProcessor;
+
+ /**
+ * UpdateMilestoneLogic constructor.
+ * @param UpdateMilestoneProcessor $updateMilestoneProcessor
+ * @param FetchesReward $fetchesReward
+ * @param AssignRewardProcessor $assignRewardProcessor
+ */
+ public function __construct(UpdateMilestoneProcessor $updateMilestoneProcessor, FetchesReward $fetchesReward, AssignRewardProcessor $assignRewardProcessor)
+ {
+ $this->updateMilestoneProcessor = $updateMilestoneProcessor;
+ $this->fetchesReward = $fetchesReward;
+ $this->assignRewardProcessor = $assignRewardProcessor;
+ }
+
+ /**
+ * @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
+ {
+ $milestone = $this->updateMilestoneProcessor->execute($request);
+
+ //Extra checking to make sure that each reward exists
+ $rewardIds = $request->input('reward_ids');
+ foreach ($rewardIds as $rewardId) {
+ /** @var Reward $reward */
+ $this->fetchesReward->execute(['id' => $rewardId]);
+ }
+
+ $object = new AchievementObject($milestone, $rewardIds);
+ $this->assignRewardProcessor->execute($object);
+
+ return $this->resourceResponse(new MilestoneResource($milestone));
+ }
+}
diff --git a/app/Classes/Modules/Milestones/DataTransferObjects/AchievementObject.php b/app/Classes/Modules/Milestones/DataTransferObjects/AchievementObject.php
new file mode 100644
index 00000000..c5eccfd4
--- /dev/null
+++ b/app/Classes/Modules/Milestones/DataTransferObjects/AchievementObject.php
@@ -0,0 +1,43 @@
+milestone = $milestone;
+ $this->rewardIds = $rewardIds;
+ }
+
+ /**
+ * @return Milestone
+ */
+ public function getMilestone(): Milestone
+ {
+ return $this->milestone;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRewardIds(): array
+ {
+ return $this->rewardIds;
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/DataTransferObjects/MilestoneObject.php b/app/Classes/Modules/Milestones/DataTransferObjects/MilestoneObject.php
new file mode 100644
index 00000000..306a0502
--- /dev/null
+++ b/app/Classes/Modules/Milestones/DataTransferObjects/MilestoneObject.php
@@ -0,0 +1,55 @@
+id = $id;
+ $this->name = $name;
+ $this->description = $description;
+ }
+
+ /**
+ * @return int
+ */
+ public function getId(): int
+ {
+ return $this->id;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Processors/AssignRewardProcessor.php b/app/Classes/Modules/Milestones/Processors/AssignRewardProcessor.php
new file mode 100644
index 00000000..a76166d5
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Processors/AssignRewardProcessor.php
@@ -0,0 +1,44 @@
+canAssignReward = $canAssignReward;
+ $this->assignsReward = $assignsReward;
+ }
+
+ /**
+ * @param AchievementObject $object
+ * @return Model
+ * @throws \App\Classes\Exceptions\AccessForbiddenException
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ public function execute(AchievementObject $object): Model {
+
+ $this->canAssignReward->passes($object);
+
+ return $this->assignsReward->execute($object);
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/Processors/CheckMilestonesForRewardProcessor.php b/app/Classes/Modules/Milestones/Processors/CheckMilestonesForRewardProcessor.php
new file mode 100644
index 00000000..597fa8bc
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Processors/CheckMilestonesForRewardProcessor.php
@@ -0,0 +1,170 @@
+createsVoucherifyVoucher = $createsVoucherifyVoucher;
+ $this->createsVoucher = $createsVoucher;
+ $this->fetchesMilestone = $fetchesMilestone;
+ $this->createsMilestoneProgress = $createsMilestoneProgress;
+ $this->createsUserReward = $createsUserReward;
+ $this->fetchesVoucherifyVoucher = $fetchesVoucherifyVoucher;
+ $this->fetchesVoucher = $fetchesVoucher;
+ $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher;
+ }
+
+
+ /**
+ * @param User $user
+ * @param array $milestone_constants
+ * @return void
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(User $user, array $milestone_constants)
+ {
+ try{
+ foreach($milestone_constants as $constant)
+ {
+ //update milestone progress
+ /** @var Milestone $milestone */
+ $milestone = $this->fetchesMilestone->execute(['name' => $constant]);
+ $result = null;
+ if($user->milestoneProgress->count() > 0){
+ $result = $user->milestoneProgress->where('milestone_id', $milestone->id)->first();
+ }
+
+ if(!$result){
+ $result = $this->createsMilestoneProgress->execute($milestone, $user->id);
+ }
+
+ if($result){
+ //fetch completed miletones
+ $completedMilestones = MilestoneProgress::where('user_id', $user->id)->pluck('milestone_id');
+ $rewards = $milestone->rewards;
+
+ //check for rewards that have a corresponding milestone count
+ if(count($rewards) > 0){
+ $this->checkMilestoneForReward($user, $completedMilestones, $rewards);
+ }
+ }
+ }
+ } catch (\Exception $e) {
+ Log::error($e);
+ }
+ }
+
+ /**
+ * @param User $user
+ * @param object $completedMilestones
+ * @param object $rewards
+ * @return void
+ */
+ private function checkMilestoneForReward(User $user, object $completedMilestones, object $rewards)
+ {
+ $milestoneIds = $rewards[0]->milestones->pluck('id');
+ if ($milestoneIds->intersect($completedMilestones)->count() >= count($rewards[0]->milestones)) {
+ /** @var Reward $reward */
+ foreach ($rewards as $reward) {
+ $result = null;
+ if (!$user->rewards->contains('reward_id', $reward->id)) {
+ $voucherId = 0;
+ $voucherName = '';
+ $voucherType = "";
+ if($reward->type == RewardType::REWARD_AMOUNT){
+ //Voucherify - Create Voucher
+ $result = $this->createsVoucherifyVoucher->execute($user, intval($reward->value));
+ }
+ else{
+ //Voucherify - Validates Voucher
+ $ValidateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject(0, $reward->value, 0.00, $user);
+ $voucherifyVoucherValidated = $this->validatesVoucherifyVoucher->execute($ValidateVoucherifyVoucherObject);
+
+ if(!isset($voucherifyVoucherValidated->reason)){
+ //Voucherify - Get Voucher
+ $result = $this->fetchesVoucherifyVoucher->execute($user, $reward->value);
+ }
+ }
+
+ if($result && !isset($result->reason)){
+ $voucherName = isset($result->campaign) ? $result->campaign : $reward->name;
+ $voucherType = $result->discount->type;
+ $voucherValue = isset($result->discount->amount_off) ? $result->discount->amount_off : $result->discount->percent_off;
+ $voucherStartDate = $result->start_date;
+ $voucherEndDate = $result->expiration_date;
+
+ //create voucher
+ $voucherObject = new VoucherObject($result->code, isset($voucherName) ? $voucherName : "", $voucherType, $voucherValue, $voucherStartDate, $voucherEndDate);
+ $voucher = $this->createsVoucher->execute($voucherObject);
+ if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherObject->getCode()]);
+ $voucherId = $voucher->id;
+
+ //create reward to user (user_reward)
+ $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count();
+ if($voucherCount == 0){
+ $this->createsUserReward->execute($reward, $user, $voucherId);
+ }
+ }
+ else{
+ Log::info('CheckMilestonesForRewardProcessor: no voucher fetched or created for reward '. json_encode($reward));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Processors/CreateMilestoneProcessor.php b/app/Classes/Modules/Milestones/Processors/CreateMilestoneProcessor.php
new file mode 100644
index 00000000..3c562d8d
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Processors/CreateMilestoneProcessor.php
@@ -0,0 +1,51 @@
+canCreateMilestone = $canCreateMilestone;
+ $this->createsMilestone = $createsMilestone;
+ }
+
+
+ /**
+ * @param Request $request
+ * @return Model
+ * @throws \App\Classes\Exceptions\AccessForbiddenException
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ public function execute(Request $request): Model
+ {
+ $userMilestoneObject = new MilestoneObject(
+ $request->input('id'),
+ $request->input('name'),
+ $request->input('description')
+ );
+
+ $this->canCreateMilestone->passes($userMilestoneObject);
+
+ return $this->createsMilestone->execute($userMilestoneObject);
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/Processors/UpdateMilestoneProcessor.php b/app/Classes/Modules/Milestones/Processors/UpdateMilestoneProcessor.php
new file mode 100644
index 00000000..b1fdbc14
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Processors/UpdateMilestoneProcessor.php
@@ -0,0 +1,59 @@
+canUpdateMilestone = $canUpdateMilestone;
+ $this->updatesMilestone = $updatesMilestone;
+ $this->fetchesMilestone = $fetchesMilestone;
+ }
+
+
+ /**
+ * @param Request $request
+ * @return Model
+ * @throws \App\Classes\Exceptions\AccessForbiddenException
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ public function execute(Request $request): Model
+ {
+ $milestoneObject = new MilestoneObject(
+ $request->input('id'),
+ $request->input('name'),
+ $request->input('description')
+ );
+
+ $this->canUpdateMilestone->passes($milestoneObject);
+
+ $milestone = $this->fetchesMilestone->execute(['id' => $request->route('id')]);
+
+ return $this->updatesMilestone->execute($milestone, $milestoneObject);
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/Services/AssignsReward.php b/app/Classes/Modules/Milestones/Services/AssignsReward.php
new file mode 100644
index 00000000..4d50a89e
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/AssignsReward.php
@@ -0,0 +1,29 @@
+getMilestone()->rewards()->attach($object->getReward()->id);
+ $object->getMilestone()->rewards()->sync($object->getRewardIds());
+
+ return $object->getMilestone();
+
+ } catch (QueryException $exception){
+ throw new MalformedRequestException($exception);
+ }
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Services/CheckIfMilestoneProgressExists.php b/app/Classes/Modules/Milestones/Services/CheckIfMilestoneProgressExists.php
new file mode 100644
index 00000000..f6b73820
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/CheckIfMilestoneProgressExists.php
@@ -0,0 +1,27 @@
+repository = $repository;
+ }
+
+ public function execute(int $user_id, int $milestone_id): bool {
+ return $this->repository->where('user_id', $user_id)->where('milestone_id', $milestone_id)->exists();
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/Services/CreatesMilestone.php b/app/Classes/Modules/Milestones/Services/CreatesMilestone.php
new file mode 100644
index 00000000..9ec9e2d1
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/CreatesMilestone.php
@@ -0,0 +1,25 @@
+name = $object->getName();
+ $model->description = $object->getDescription();
+
+ return $this->handler($model);
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Services/CreatesMilestoneProgress.php b/app/Classes/Modules/Milestones/Services/CreatesMilestoneProgress.php
new file mode 100644
index 00000000..bd04c624
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/CreatesMilestoneProgress.php
@@ -0,0 +1,40 @@
+isMilestoneProgressExists = $isMilestoneProgressExists;
+ }
+
+
+ /**
+ * @param Milestone $milestone
+ * @param string $userId
+ * @return \Illuminate\Database\Eloquent\Model|null
+ */
+ public function execute(Milestone $milestone, string $userId) {
+ if(!$this->isMilestoneProgressExists->execute($userId, $milestone->id))
+ {
+ $model = new MilestoneProgress();
+ $model->user_id = $userId;
+
+ return $this->handler($milestone->progress(), $model);
+ }
+
+ return null;
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Services/DeletesMilestone.php b/app/Classes/Modules/Milestones/Services/DeletesMilestone.php
new file mode 100644
index 00000000..f829d8e1
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/DeletesMilestone.php
@@ -0,0 +1,19 @@
+handler($model);
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Services/FetchesMilestone.php b/app/Classes/Modules/Milestones/Services/FetchesMilestone.php
new file mode 100644
index 00000000..e0c12881
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/FetchesMilestone.php
@@ -0,0 +1,34 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ public function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Services/ListsMilestoneProgress.php b/app/Classes/Modules/Milestones/Services/ListsMilestoneProgress.php
new file mode 100644
index 00000000..2cdf6de2
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/ListsMilestoneProgress.php
@@ -0,0 +1,33 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Services/ListsMilestones.php b/app/Classes/Modules/Milestones/Services/ListsMilestones.php
new file mode 100644
index 00000000..dae7c076
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/ListsMilestones.php
@@ -0,0 +1,33 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Services/UpdatesMilestone.php b/app/Classes/Modules/Milestones/Services/UpdatesMilestone.php
new file mode 100644
index 00000000..fbe463aa
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Services/UpdatesMilestone.php
@@ -0,0 +1,24 @@
+name = $object->getName();
+ $model->description = $object->getDescription();
+
+ return $this->handler($model);
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanAssignReward.php b/app/Classes/Modules/Milestones/Standards/Rules/CanAssignReward.php
new file mode 100644
index 00000000..35e8b31d
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Standards/Rules/CanAssignReward.php
@@ -0,0 +1,57 @@
+milestoneRewardValidation = $milestoneRewardValidation;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function authorized(): bool
+ {
+ if (!Auth::user()->can('edit milestone')) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param AchievementObject $object
+ * @return bool
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ protected function validators($object): bool
+ {
+ return $this->milestoneRewardValidation->validate($object);
+ }
+
+ /**
+ * @param AchievementObject $object
+ * @return bool
+ */
+ protected function criteria($object): bool
+ {
+ return true;
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanCreateMilestone.php b/app/Classes/Modules/Milestones/Standards/Rules/CanCreateMilestone.php
new file mode 100644
index 00000000..be25b64f
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Standards/Rules/CanCreateMilestone.php
@@ -0,0 +1,54 @@
+milestoneValidation = $milestoneValidation;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function authorized(): bool
+ {
+ if (!Auth::user()->can('add milestone')) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param MilestoneObject $object
+ * @return bool
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ protected function validators($object): bool
+ {
+ return $this->milestoneValidation->validate($object, 'POST');
+ }
+
+ /**
+ * @param MilestoneObject $object
+ * @return bool
+ */
+ protected function criteria($object): bool
+ {
+ return true;
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanDeleteMilestone.php b/app/Classes/Modules/Milestones/Standards/Rules/CanDeleteMilestone.php
new file mode 100644
index 00000000..c0c7fde7
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Standards/Rules/CanDeleteMilestone.php
@@ -0,0 +1,44 @@
+can('delete milestone')) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+ /**
+ * @param MilestoneObject $object
+ * @return bool
+ */
+ protected function validators($object): bool
+ {
+ return true;
+
+ }
+
+
+ /**
+ * @param MilestoneObject $object
+ * @return bool
+ */
+ protected function criteria($object): bool
+ {
+ return true;
+ }
+
+}
diff --git a/app/Classes/Modules/Milestones/Standards/Rules/CanUpdateMilestone.php b/app/Classes/Modules/Milestones/Standards/Rules/CanUpdateMilestone.php
new file mode 100644
index 00000000..9f7db714
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Standards/Rules/CanUpdateMilestone.php
@@ -0,0 +1,54 @@
+milestoneValidation = $milestoneValidation;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function authorized(): bool
+ {
+ if (!Auth::user()->can('edit milestone')) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param MilestoneObject $object
+ * @return bool
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ protected function validators($object): bool
+ {
+ return $this->milestoneValidation->validate($object, 'PUT');
+ }
+
+ /**
+ * @param MilestoneObject $object
+ * @return bool
+ */
+ protected function criteria($object): bool
+ {
+ return true;
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Standards/Validators/MilestoneRewardValidation.php b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneRewardValidation.php
new file mode 100644
index 00000000..a282b010
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneRewardValidation.php
@@ -0,0 +1,40 @@
+ $object->getMilestone()->id,
+ 'reward_ids' => $object->getRewardIds()
+ ];
+ }
+
+ /**
+ * @return array
+ */
+ protected function rules(): array
+ {
+ return [
+ 'milestone_id' => 'required',
+ 'reward_ids' => 'required',
+ ];
+ }
+
+ /**
+ * @return array
+ */
+ protected function messages(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Classes/Modules/Milestones/Standards/Validators/MilestoneValidation.php b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneValidation.php
new file mode 100644
index 00000000..81ffbced
--- /dev/null
+++ b/app/Classes/Modules/Milestones/Standards/Validators/MilestoneValidation.php
@@ -0,0 +1,68 @@
+ $object->getName(),
+ 'description' => $object->getDescription(),
+ ];
+
+ return $data;
+ }
+
+ /**
+ * @param null|string $type
+ * @return array
+ */
+ protected function rules(?string $type = 'POST'): array {
+ if ($type == 'POST') { //crete
+ return [
+ 'name' => [
+ 'required',
+ function ($attribute, $value, $fail) {
+ // Check if milestone name already exists in the database
+ $existingMilestone = Milestone::where('name', $value)->first();
+ if ($existingMilestone) {
+ $fail("The {$attribute} milestone name already exists in the database.");
+ }
+ },
+ ],
+ 'description' => [
+ 'required',
+ ]
+ ];
+
+ }
+ elseif($type == 'PUT') { //update
+ return [
+ 'name' => [
+ 'required',
+ ],
+ 'description' => [
+ 'required',
+ ]
+ ];
+ }
+ }
+
+ /**
+ * @return array
+ */
+ protected function messages(): array {
+ return [];
+ }
+
+}
diff --git a/app/Classes/Modules/Notifications/ControllersLogic/ListNotificationsLogic.php b/app/Classes/Modules/Notifications/ControllersLogic/ListNotificationsLogic.php
new file mode 100644
index 00000000..0fdbf6f9
--- /dev/null
+++ b/app/Classes/Modules/Notifications/ControllersLogic/ListNotificationsLogic.php
@@ -0,0 +1,61 @@
+ 'Retrieve Notifications',
+ 'message' => 'You have successfully retrieved a list of Notifications'
+ ];
+ }
+
+ /** @var ListsNotification */
+ private $listsNotification;
+
+ /**
+ * ListNotificationsLogic constructor.
+ * @param ListsNotification $listsNotification
+ */
+ public function __construct(
+ ListsNotification $listsNotification
+ )
+ {
+ $this->listsNotification = $listsNotification;
+ }
+
+
+ /**
+ * @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
+ {
+
+ $filters = [
+ // 'target_id'=>auth()->user()->id,
+ // 'per_page'=>$request->route('per_page')
+ ];
+
+ $notifications = $this->listsNotification->execute($filters);
+
+ return $this->collectionResponse(NotificationResource::collection($notifications));
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Notifications/DataTransferObjects/NotificationObject.php b/app/Classes/Modules/Notifications/DataTransferObjects/NotificationObject.php
new file mode 100644
index 00000000..40dc4f3c
--- /dev/null
+++ b/app/Classes/Modules/Notifications/DataTransferObjects/NotificationObject.php
@@ -0,0 +1,101 @@
+title = $title;
+ $this->description = $description;
+ $this->subject = $subject;
+ $this->target = $target;
+ $this->causer = $causer;
+ $this->status = $status;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @return int
+ */
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @return Notifiable
+ */
+ public function getSubject(): Notifiable
+ {
+ return $this->subject;
+ }
+
+ /**
+ * @return Notifiable
+ */
+ public function getTarget(): Notifiable
+ {
+ return $this->target;
+ }
+
+ /**
+ * @return Notifiable
+ */
+ public function getCauser(): Notifiable
+ {
+ return $this->causer;
+ }
+
+ /**
+ * @return int
+ */
+ public function getStatus(): int
+ {
+ return $this->status;
+ }
+
+
+}
diff --git a/app/Classes/Modules/Notifications/Processors/CreateNotificationProcessor.php b/app/Classes/Modules/Notifications/Processors/CreateNotificationProcessor.php
new file mode 100644
index 00000000..d6b077f8
--- /dev/null
+++ b/app/Classes/Modules/Notifications/Processors/CreateNotificationProcessor.php
@@ -0,0 +1,27 @@
+createsNotification = $createsNotification;
+ }
+
+ public function execute(NotificationObject $object)
+ {
+ $notification = $this->createsNotification->execute($object);
+ return $notification;
+ }
+}
diff --git a/app/Classes/Modules/Notifications/Services/CreatesNotification.php b/app/Classes/Modules/Notifications/Services/CreatesNotification.php
new file mode 100644
index 00000000..4f5ca5fd
--- /dev/null
+++ b/app/Classes/Modules/Notifications/Services/CreatesNotification.php
@@ -0,0 +1,30 @@
+title = $object->getTitle();
+ $model->description = $object->getDescription();
+ $model->status = $object->getStatus();
+ $model->subject_type = get_class($object->getSubject());
+ $model->subject_id = $object->getSubject()->id;
+ $model->target_type = get_class($object->getTarget());
+ $model->target_id = $object->getTarget()->id;
+ $model->causer_type = get_class($object->getCauser());
+ $model->causer_id = $object->getCauser()->id;
+ $model->save();
+
+ return $model;
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Notifications/Services/ListsNotification.php b/app/Classes/Modules/Notifications/Services/ListsNotification.php
new file mode 100644
index 00000000..707e7ae8
--- /dev/null
+++ b/app/Classes/Modules/Notifications/Services/ListsNotification.php
@@ -0,0 +1,33 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateLeadPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateLeadPerfexCRMObject.php
new file mode 100644
index 00000000..bea178f5
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateLeadPerfexCRMObject.php
@@ -0,0 +1,74 @@
+name = $name;
+ $this->email = $email;
+ $this->phone = $phone;
+ $this->companyName = $companyName;
+ $this->companyReference = $companyReference;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEmail(): string
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPhone(): string
+ {
+ return $this->phone;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCompanyName(): string
+ {
+ return $this->companyName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCompanyReference(): string
+ {
+ return $this->companyReference;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateTaskPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateTaskPerfexCRMObject.php
new file mode 100644
index 00000000..2475ebbf
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CreateTaskPerfexCRMObject.php
@@ -0,0 +1,173 @@
+email = $email;
+ $this->name = $name;
+ $this->description = $description;
+ $this->leadId = $leadId;
+ $this->projectId = $projectId;
+ $this->milestoneId = $milestoneId;
+ $this->reference = $reference;
+ $this->onTaskCompletion = $onTaskCompletion;
+ $this->status = $status;
+ $this->department = $department;
+ $this->priority = $priority;
+ $this->duedate = $duedate;
+ $this->invoiceId = $invoiceId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEmail(): string
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLeadId(): string
+ {
+ return $this->leadId;
+ }
+
+ public function setLeadId(string $leadId)
+ {
+ $this->leadId = $leadId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProjectId(): string
+ {
+ return $this->projectId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMilestoneId(): string
+ {
+ return $this->milestoneId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getReference(): string
+ {
+ return $this->reference;
+ }
+
+ /**
+ * @return string
+ */
+ public function getOnTaskCompletion(): string
+ {
+ return $this->onTaskCompletion;
+ }
+
+ /**
+ * @return string
+ */
+ public function getStatus(): string
+ {
+ return $this->status;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDepartment(): string
+ {
+ return $this->department;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPriority(): string
+ {
+ return $this->priority;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDuedate(): string
+ {
+ return $this->duedate;
+ }
+
+ /**
+ * @return int
+ */
+ public function getInvoiceId(): int
+ {
+ return $this->invoiceId;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/CustomerContactObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CustomerContactObject.php
new file mode 100644
index 00000000..3530524d
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/CustomerContactObject.php
@@ -0,0 +1,99 @@
+customerId = $customerId;
+ $this->firstname = $firstname;
+ $this->lastname = $lastname;
+ $this->email = $email;
+ $this->password = $password;
+ $this->isPrimary = $isPrimary;
+ $this->sendSetPasswordEmail = $sendSetPasswordEmail;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCustomerId(): int
+ {
+ return $this->customerId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFirstName(): string
+ {
+ return $this->firstname;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLastName(): string
+ {
+ return $this->lastname;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEmail(): string
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPassword(): string
+ {
+ return $this->password;
+ }
+
+ /**
+ * @return string
+ */
+ public function getIsPrimary(): string
+ {
+ return $this->isPrimary;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSendSetPasswordEmail(): string
+ {
+ return $this->sendSetPasswordEmail;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/FetchPerfexCRMInvoiceObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/FetchPerfexCRMInvoiceObject.php
new file mode 100644
index 00000000..56c5a01d
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/FetchPerfexCRMInvoiceObject.php
@@ -0,0 +1,38 @@
+email = $email;
+ $this->transaction = $transaction;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEmail(): string
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return Transaction
+ */
+ public function getTransaction(): Transaction
+ {
+ return $this->transaction;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InitialPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InitialPerfexCRMObject.php
new file mode 100644
index 00000000..66c2c86c
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InitialPerfexCRMObject.php
@@ -0,0 +1,124 @@
+companyName = $companyName;
+ $this->companyReference = $companyReference;
+ $this->contactName = $contactName;
+ $this->contactEmail = $contactEmail;
+ $this->bookingMarking = $bookingMarking;
+ $this->projectName = $projectName;
+ $this->projectStatus = $projectStatus;
+ $this->milestoneNames = $milestoneNames;
+ $this->taskNames = $taskNames;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCompanyName(): string
+ {
+ return $this->companyName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCompanyReference(): string
+ {
+ return $this->companyReference;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContactName(): string
+ {
+ return $this->contactName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContactEmail(): string
+ {
+ return $this->contactEmail;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBookingMarking(): string
+ {
+ return $this->bookingMarking;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProjectName(): string
+ {
+ return $this->projectName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProjectStatus(): int
+ {
+ return $this->projectStatus;
+ }
+
+ /**
+ * @return array
+ */
+ public function getMilestoneNames(): array
+ {
+ return $this->milestoneNames;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTaskNames(): array
+ {
+ return $this->taskNames;
+ }
+
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePaymentPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePaymentPerfexCRMObject.php
new file mode 100644
index 00000000..3d34c540
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePaymentPerfexCRMObject.php
@@ -0,0 +1,86 @@
+invoiceId = $invoiceId;
+ $this->amount = $amount;
+ $this->date = $date;
+ $this->paymentMode = $paymentMode;
+ $this->transactionId = $transactionId;
+ $this->note = $note;
+ }
+
+ /**
+ * @return int
+ */
+ public function getInvoiceId(): int
+ {
+ return $this->invoiceId;
+ }
+
+ /**
+ * @return float
+ */
+ public function getAmount(): float
+ {
+ return $this->amount;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDate(): string
+ {
+ return $this->date;
+ }
+
+ /**
+ * @return int
+ */
+ public function getPaymentMode(): int
+ {
+ return $this->paymentMode;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTransactionId(): string
+ {
+ return $this->transactionId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNote(): string
+ {
+ return $this->note;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePerfexCRMObject.php
new file mode 100644
index 00000000..f1f21f11
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoicePerfexCRMObject.php
@@ -0,0 +1,145 @@
+clientId = $clientId;
+ $this->number = $number;
+ $this->date = $date;
+ $this->dueDate = $dueDate;
+ $this->currency = $currency;
+ $this->subTotal = $subTotal;
+ $this->total = $total;
+ $this->billingStreet = $billingStreet;
+ $this->projectId = $projectId;
+ $this->allowedPaymentModes = $allowedPaymentModes;
+ $this->invoiceItems = $invoiceItems;
+ }
+
+ /**
+ * @return string
+ */
+ public function getClientId(): string
+ {
+ return $this->clientId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getNumber(): string
+ {
+ return $this->number;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDate(): string
+ {
+ return $this->date;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDueDate(): string
+ {
+ return $this->dueDate;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCurrency(): string
+ {
+ return $this->currency;
+ }
+
+ /**
+ * @return float
+ */
+ public function getSubTotal(): float
+ {
+ return $this->subTotal;
+ }
+
+ /**
+ * @return float
+ */
+ public function getTotal(): float
+ {
+ return $this->total;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBillingStreet(): string
+ {
+ return $this->billingStreet;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProjectId(): string
+ {
+ return $this->projectId;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedPaymentModes(): array
+ {
+ return $this->allowedPaymentModes;
+ }
+
+ /**
+ * @return array
+ */
+ public function getInvoiceItems(): array
+ {
+ return $this->invoiceItems;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoiceSingleItemPerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoiceSingleItemPerfexCRMObject.php
new file mode 100644
index 00000000..7b9428db
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/InvoiceSingleItemPerfexCRMObject.php
@@ -0,0 +1,86 @@
+description = $description;
+ $this->longDescription = $longDescription;
+ $this->qty = $qty;
+ $this->rate = $rate;
+ $this->order = $order;
+ $this->unit = $unit;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLongDescription(): string
+ {
+ return $this->longDescription;
+ }
+
+ /**
+ * @return float
+ */
+ public function getQty(): float
+ {
+ return $this->qty;
+ }
+
+ /**
+ * @return float
+ */
+ public function getRate(): float
+ {
+ return $this->rate;
+ }
+
+ /**
+ * @return int
+ */
+ public function getOrder(): int
+ {
+ return $this->order;
+ }
+
+ /**
+ * @return string
+ */
+ public function getUnit(): string
+ {
+ return $this->unit;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMInvoiceObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMInvoiceObject.php
new file mode 100644
index 00000000..ad7fb921
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMInvoiceObject.php
@@ -0,0 +1,74 @@
+email = $email;
+ $this->transaction = $transaction;
+ $this->isPaid = $isPaid;
+ $this->projectName = $projectName;
+ $this->projectId = $projectId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEmail(): string
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return Transaction
+ */
+ public function getTransaction(): Transaction
+ {
+ return $this->transaction;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getIsPaid(): bool
+ {
+ return $this->isPaid;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProjectName(): string
+ {
+ return $this->projectName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProjectId(): string
+ {
+ return $this->projectId;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMObject.php b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMObject.php
new file mode 100644
index 00000000..96bbc8bd
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/DataTransferObjects/UpdatePerfexCRMObject.php
@@ -0,0 +1,146 @@
+companyName = $companyName;
+ $this->companyReference = $companyReference;
+ $this->contactName = $contactName;
+ $this->contactEmail = $contactEmail;
+ $this->bookingMarking = $bookingMarking;
+ $this->projectName = $projectName;
+ $this->projectStatus = $projectStatus;
+ $this->$invoiceId = $invoiceId;
+ $this->milestoneNames = $milestoneNames;
+ $this->tasks = $tasks;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCompanyName(): string
+ {
+ return $this->companyName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCompanyReference(): string
+ {
+ return $this->companyReference;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContactName(): string
+ {
+ return $this->contactName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getContactEmail(): string
+ {
+ return $this->contactEmail;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBookingMarking(): string
+ {
+ return $this->bookingMarking;
+ }
+
+ /**
+ * @return string
+ */
+ public function getProjectName(): string
+ {
+ return $this->projectName;
+ }
+
+ /**
+ * @return int
+ */
+ public function getProjectStatus(): int
+ {
+ return $this->projectStatus;
+ }
+
+ /**
+ * @return int
+ */
+ public function getInvoiceId(): int
+ {
+ return $this->invoiceId;
+ }
+
+ /**
+ * @return array
+ */
+ public function getMilestoneNames(): array
+ {
+ return $this->milestoneNames;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTasks(): array
+ {
+ return $this->tasks;
+ }
+
+ public function setTasks($tasks)
+ {
+ $this->tasks = $tasks;
+ }
+
+ public function setInvoiceId($invoiceId)
+ {
+ $this->invoiceId = $invoiceId;
+ }
+
+}
diff --git a/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php
new file mode 100644
index 00000000..379caae7
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/BookingToPerfexCRMProcessor.php
@@ -0,0 +1,47 @@
+company->reference;
+ $companyName = $booking->company->name;
+ $employee = $booking->company->employees()->first();
+ $contactEmail = $employee->email;
+ $contactName = $employee->name;
+ $bookingMarking = $booking->marking;
+
+ $serviceTypeName = $booking->company->services()->where('id', $booking->service_id)->first()->name;
+ $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking;
+
+ $updatePerfexCRMObject = new UpdatePerfexCRMObject(
+ $companyName,
+ $companyReference,
+ $contactName,
+ $contactEmail,
+ $bookingMarking,
+ $projectName,
+ PerfexCRMProjectStatus::NOT_STARTED,
+ 0,
+ [],
+ []
+ );
+
+ UpdatePerfexCRM::dispatch($updatePerfexCRMObject, null, null);
+
+ return true;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMInvoiceProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMInvoiceProcessor.php
new file mode 100644
index 00000000..4edb2ef5
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMInvoiceProcessor.php
@@ -0,0 +1,227 @@
+createsPerfexCRMInvoice = $createsPerfexCRMInvoice;
+ $this->createsPerfexCRMInvoicePayment = $createsPerfexCRMInvoicePayment;
+ $this->convertsPerfexCRMLeadToCustomer = $convertsPerfexCRMLeadToCustomer;
+ $this->fetchesPerfexCRMProject = $fetchesPerfexCRMProject;
+ $this->createsPerfexCRMCustomerProject = $createsPerfexCRMCustomerProject;
+ $this->fetchesCompany = $fetchesCompany;
+ }
+
+ /**
+ * @param $transaction
+ * @return null|object
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute($transaction) {
+ $booking = $transaction->booking;
+ $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]);
+ $purchaseOrder = $booking->transactions()
+ ->where('type', TransactionType::PURCHASE_ORDER)
+ ->complete()
+ ->first();
+
+ $clientId = "";
+ $number = $transaction->bill_no;
+
+ $prefix = "INV-";
+ //This will remove the prefix if prefix already exist in the string
+ if (substr($number, 0, strlen($prefix)) == $prefix) {
+ $number = substr($number, strlen($prefix));
+ }
+
+ $number = 'EXC-'.$number;
+ $date = Carbon::parse($booking->created_at)->format('Y-m-d');
+ $dueDate = Carbon::parse($booking->created_at)->format('Y-m-d');
+ $currency = 1;
+ $subTotal = 0.00;
+ $total = 0.00;
+
+ $billingStreet = "";
+ $addresses = $supplier->addresses()->where('billing', '=', true)->first();
+
+ if ($addresses !== null) {
+ $billingStreet = $billingStreet.$addresses->street_one;
+ $billingStreet = $billingStreet.$addresses->street_two.',';
+ $billingStreet = $billingStreet.$addresses->district()->first()->name.',';
+ $billingStreet = $billingStreet.$addresses->postcode;
+ $billingStreet = $billingStreet.$addresses->state()->first()->name.',';
+ $billingStreet = $billingStreet.$addresses->country()->first()->name;
+ }
+ else{
+ $billingStreet = "[Pending Billing Details by user]";
+ }
+
+ $allowedPaymentModes = [];
+ $invoiceItems = [];
+
+ $firstSupplier = $supplier->employees()->first();
+ $email = null;
+ if ($firstSupplier) {
+ $email = $firstSupplier->email;
+ Log::error('CreatePerfexCRMInvoiceProcessor debug:'.$email);
+ } else {
+ $bookingMarking = $transaction->owner->marking;
+ $serviceTypeName = $transaction->owner->company->services()->where('id', $transaction->owner->service_id)->first()->name;
+ $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking;
+ Log::error('$projectName: '.$projectName);
+ return $email;
+ }
+
+ $result = $this->convertsPerfexCRMLeadToCustomer->execute($email);
+ if(isset($result->payload)){
+ $clientId = $result->payload['client_id'];
+ }
+
+ //get project
+ $projectId = "";
+ $bookingMarking = $transaction->owner->marking;
+ $serviceTypeName = $transaction->owner->company->services()->where('id', $transaction->owner->service_id)->first()->name;
+ $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking;
+ $result = $this->fetchesPerfexCRMProject->execute($projectName, $clientId);
+ if(isset($result->payload)){
+ $project = $result->payload[0];
+ $projectId = $project['id'];
+ }
+ else{
+ $result = $this->createsPerfexCRMCustomerProject->execute($projectName, PerfexCRMProjectStatus::NOT_STARTED, $clientId);
+ if(isset($result->payload)){ //Here means project creation successful
+ $projectId = $result->payload['project_id'];
+ }
+ }
+
+ if(is_null($purchaseOrder)){
+ $extraInvoiceSingleItem = new InvoiceSingleItemPerfexCRMObject(
+ 'Refer to booking: '.$booking->marking,
+ "",
+ 1.00,
+ $transaction->amount,
+ 1,
+ ""
+ );
+ $subTotal += number_format($transaction->amount, 2,'.','') * 1;
+ array_push($invoiceItems, $extraInvoiceSingleItem);
+ }
+ else{
+ foreach ($purchaseOrder->transactionDetails as $key => $transaction_detail){
+ $order = $key + 1;
+ $stockCode = $transaction_detail->product_code;
+ $description = $transaction_detail->product_name;
+ $quantity = $transaction_detail->quantity;
+ $unitPrice = 0.00;
+ if($booking->first()->fix_currency_id !== 1)
+ $unitPrice = (1/$transaction->currency_rate) * $transaction_detail->price;
+ else
+ $unitPrice = $transaction_detail->price;
+
+ //$totalAmount = 0.00;
+ if($booking->first()->fix_currency_id !== 1){
+
+ //$totalAmount = (float)number_format( (1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','')*$transaction_detail->quantity;
+ $subTotal += number_format((1/$transaction->currency_rate) * $transaction_detail->price, 2,'.','') * $transaction_detail->quantity;
+ }
+ else
+ {
+ //$totalAmount = (float)number_format($transaction_detail->price, 2,'.','')*$transaction_detail->quantity;
+ $subTotal += number_format($transaction_detail->price, 2,'.','') * $transaction_detail->quantity;
+ }
+
+ //string $description, string $longDescription, int $qty, int $rate, int $order, string $unit
+ $invoiceSingleItem = new InvoiceSingleItemPerfexCRMObject(
+ $description,
+ "",
+ $quantity,
+ $unitPrice,
+ $order,
+ ""
+ );
+ array_push($invoiceItems, $invoiceSingleItem);
+ }
+ }
+
+ //When this transaction is of TransactionType::PAYMENT, the amount is actually in the currecy user choose to pay (RM)
+ //So there is no need to convert it
+ if ($transaction->type == TransactionType::PAYMENT){
+ $total = number_format($transaction->amount, 2,'.','') * 1;
+ }
+ else{
+ if($booking->first()->fix_currency_id !== 1){
+ $total = ((1/$transaction->currency_rate) * $transaction->amount) + $transaction->service_charge + $transaction->tax;
+ }
+ else{
+ $total = $transaction->amount + $transaction->service_charge + $transaction->tax;
+ }
+ }
+
+ //This setting is similar to Setup > Leads > Sources, Setup > Leads > Statuses
+ //Can be found at Finance > Payment Modes
+ array_push($allowedPaymentModes, 1, 2);
+
+ $invoicePerfexCRMObject = new InvoicePerfexCRMObject(
+ $clientId,
+ $number,
+ $date,
+ $dueDate,
+ $currency,
+ $subTotal,
+ $total,
+ $billingStreet,
+ $projectId,
+ $allowedPaymentModes,
+ $invoiceItems
+ );
+
+ $result = $this->createsPerfexCRMInvoice->execute($invoicePerfexCRMObject);
+ return $result;
+ }
+}
+
diff --git a/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMLeadProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMLeadProcessor.php
new file mode 100644
index 00000000..5a0bc6dc
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMLeadProcessor.php
@@ -0,0 +1,36 @@
+createsPerfexCRMLead = $createsPerfexCRMLead;
+ }
+
+ /**
+ * @param CreateLeadPerfexCRMObject $createLeadPerfexCRMObject
+ * @return null|object
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute(CreateLeadPerfexCRMObject $createLeadPerfexCRMObject) {
+ return $this->createsPerfexCRMLead->execute($createLeadPerfexCRMObject);
+ }
+
+}
diff --git a/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMTaskProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMTaskProcessor.php
new file mode 100644
index 00000000..efd6df0e
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/CreatePerfexCRMTaskProcessor.php
@@ -0,0 +1,32 @@
+createsPerfexCRMTask = $createsPerfexCRMTask;
+ }
+
+ /**
+ * @param CreateTaskPerfexCRMObject $createTaskPerfexCRMObject
+ * @return true
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute(CreateTaskPerfexCRMObject $createTaskPerfexCRMObject) {
+ $this->createsPerfexCRMTask->execute($createTaskPerfexCRMObject);
+ return true;
+ }
+
+}
diff --git a/app/Classes/Modules/PerfexCRM/Processors/FetchPerfexCRMInvoiceProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/FetchPerfexCRMInvoiceProcessor.php
new file mode 100644
index 00000000..4c8ee9bf
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/FetchPerfexCRMInvoiceProcessor.php
@@ -0,0 +1,74 @@
+fetchesPerfexCRMCustomer = $fetchesPerfexCRMCustomer;
+ $this->fetchesPerfexCRMInvoice = $fetchesPerfexCRMInvoice;
+ $this->createPerfexCRMInvoiceProcessor = $createPerfexCRMInvoiceProcessor;
+ }
+
+ public function execute(FetchPerfexCRMInvoiceObject $fetchPerfexCRMInvoiceObject)
+ {
+ //invoiceId to be returned - fetch or create
+ $invoiceId = 0;
+
+ //get the client id
+ $customer = $this->fetchesPerfexCRMCustomer->execute($fetchPerfexCRMInvoiceObject->getEmail());
+ $transaction = $fetchPerfexCRMInvoiceObject->getTransaction();
+
+ $number = $transaction->bill_no;
+
+ $prefix = "INV-";
+ //This will remove the prefix if prefix already exist in the string
+ if (substr($number, 0, strlen($prefix)) == $prefix) {
+ $number = substr($number, strlen($prefix));
+ }
+
+ $number = 'EXC-'.$number;
+ $invoice = $this->fetchesPerfexCRMInvoice->execute($customer->userid,"INV-", $number);
+ if(is_null($invoice)){
+ $result = $this->createPerfexCRMInvoiceProcessor->execute($transaction);
+ if ($result) {
+ $invoiceId = $result->payload['id'];
+ } else {
+ $log['message'] = 'FetchPerfexCRMInvoiceProcessor failed for transaction > bill_no: '.$number;
+ Helper::debugLogger($log);
+ }
+ }
+ else{
+ $invoiceId = $invoice->id;
+ }
+
+ return $invoiceId;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Processors/NewLeadTaskToPerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/NewLeadTaskToPerfexCRMProcessor.php
new file mode 100644
index 00000000..2195fc46
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/NewLeadTaskToPerfexCRMProcessor.php
@@ -0,0 +1,43 @@
+employees()->first();
+ $contactEmail = $employee->email;
+
+ $createTaskPerfexCRMObject = new CreateTaskPerfexCRMObject(
+ $contactEmail,
+ PerfexCRMTasks::TASK_IDENTIFICATION_1['name'],
+ PerfexCRMTasks::TASK_IDENTIFICATION_1['description'],
+ "",
+ "",
+ "",
+ "",
+ "",
+ PerfexCRMTasks::TASK_IDENTIFICATION_1['status'],
+ PerfexCRMTasks::TASK_IDENTIFICATION_1['department'],
+ PerfexCRMTaskPriority::DEFAULT,
+ "0",
+ 0
+ );
+
+ CreatePerfexCRMSingleTask::dispatch($createTaskPerfexCRMObject);
+ return true;
+ }
+
+}
diff --git a/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessor.php b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessor.php
new file mode 100644
index 00000000..b996e592
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/TransactionToPerfexCRMProcessor.php
@@ -0,0 +1,160 @@
+owner instanceof \App\Models\Booking) {
+ $companyReference = $model->owner->company->reference;
+ $companyName = $model->owner->company->name;
+ $employee = $model->owner->company->employees()->first();
+ $contactEmail = $employee->email;
+ $contactName = $employee->name;
+ $bookingMarking = $model->owner->marking;
+
+ $serviceTypeName = $model->owner->company->services()->where('id', $model->owner->service_id)->first()->name;
+ $projectName = 'Exchange | '.$serviceTypeName.' | '.$bookingMarking;
+
+ if($status == ApprovalStatus::APPROVED)
+ {
+ if($model->type == TransactionType::PAYMENT){
+
+ $tasks = [
+ PerfexCRMTasks::TASK_1
+ ];
+ if($model->owner->service_id == 1){
+ $tasks = [
+ PerfexCRMTasks::TASK_1,
+ 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,
+ ];
+ }
+ else if($model->owner->service_id == 3){
+ $tasks = [
+ PerfexCRMTasks::TASK_1,
+ 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,
+ ];
+ }
+ else if($model->owner->service_id == 4){
+ $tasks = [
+ PerfexCRMTasks::TASK_1,
+ 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,
+
+ ];
+ }
+
+ //Add in tasks if customer has no purchase order yet and using 1 days or 3 days transfer service
+ if($model->owner->service_id == 1 || $model->owner->service_id == 3){
+ $booking = $model->booking;
+ $purchaseOrder = $booking->transactions()
+ ->where('type', TransactionType::PURCHASE_ORDER)
+ ->complete()
+ ->first();
+ if(is_null($purchaseOrder)){
+ $potask1 = PerfexCRMTasks::TASK_PURCHASE_ORDER_1;
+ $potask1['status'] = PerfexCRMTaskStatus::NOT_STARTED;
+ $poTasks = [
+ $potask1,
+ PerfexCRMTasks::TASK_PURCHASE_ORDER_2,
+ ];
+ $tasks = array_merge($tasks, $poTasks);
+ }
+ }
+
+ $updatePerfexCRMObject = new UpdatePerfexCRMObject(
+ $companyName,
+ $companyReference,
+ $contactName,
+ $contactEmail,
+ $bookingMarking,
+ $projectName,
+ PerfexCRMProjectStatus::IN_PROGRESS,
+ 0,
+ [],
+ $tasks
+ );
+
+ UpdatePerfexCRMPrelude::dispatch($model, $updatePerfexCRMObject, UpdatePerfexCRM::class, UpdatePerfexCRMInvoice::class);
+ }
+ }
+ else if($status == ApprovalStatus::PENDING_VERIFICATION){
+ if($model->type == TransactionType::PURCHASE_ORDER){
+ $tasks = [];
+ if($model->owner->service_id == 1 || $model->owner->service_id == 3){
+ $tasks = [
+ PerfexCRMTasks::TASK_PURCHASE_ORDER_1,
+ PerfexCRMTasks::TASK_PURCHASE_ORDER_2,
+ ];
+ }
+
+ if(count($tasks) > 0){
+ $updatePerfexCRMObject = new UpdatePerfexCRMObject(
+ $companyName,
+ $companyReference,
+ $contactName,
+ $contactEmail,
+ $bookingMarking,
+ $projectName,
+ PerfexCRMProjectStatus::IN_PROGRESS,
+ 0,
+ [],
+ $tasks
+ );
+ UpdatePerfexCRM::dispatch($updatePerfexCRMObject, null, null);
+ }
+ }
+ }
+ }
+ }
+ catch (\Exception $exception) {
+ Log::error('TransactionToPerfexCRMProcessor debug:');
+ Log::error($exception);
+ }
+ 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..b9566a4f
--- /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);
+
+ //Log::error("TransactionToPerfexCRMProcessorV2 status: ".$status." - for project name: ".$projectName);
+
+ if(count($tasks) > 0) {
+ $updatePerfexCRMObject = new UpdatePerfexCRMObject(
+ $bookingInfo['companyName'],
+ $bookingInfo['companyReference'],
+ $bookingInfo['contactName'],
+ $bookingInfo['contactEmail'],
+ $bookingInfo['bookingMarking'],
+ $projectName,
+ PerfexCRMProjectStatus::IN_PROGRESS,
+ 0,
+ [],
+ $tasks
+ );
+ $this->dispatchUpdateJob($transaction, $status, $updatePerfexCRMObject);
+ }
+ }
+ } catch (\Exception $exception) {
+ 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 instanceof Booking && $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
new file mode 100644
index 00000000..fbf64be8
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Processors/UpdatePerfexCRMProcessor.php
@@ -0,0 +1,256 @@
+convertsPerfexCRMLeadToCustomer = $convertsPerfexCRMLeadToCustomer;
+ $this->createsPerfexCRMCustomerProject = $createsPerfexCRMCustomerProject;
+ $this->createsPerfexCRMMilestone = $createsPerfexCRMMilestone;
+ $this->createsPerfexCRMTask = $createsPerfexCRMTask;
+ $this->fetchesPerfexCRMProject = $fetchesPerfexCRMProject;
+ $this->fetchesPerfexCRMMilestone = $fetchesPerfexCRMMilestone;
+ $this->fetchesPerfexCRMTask = $fetchesPerfexCRMTask;
+ $this->createsPerfexCRMCustomer = $createsPerfexCRMCustomer;
+ $this->createsPerfexCRMCustomerContact = $createsPerfexCRMCustomerContact;
+ $this->updatesPerfexCRMTask = $updatesPerfexCRMTask;
+ $this->updatesPerfexCRMCustomer = $updatesPerfexCRMCustomer;
+ $this->updatesPerfexCRMProject = $updatesPerfexCRMProject;
+ }
+
+ /**
+ * @param UpdatePerfexCRMObject $updatePerfexCRMObject
+ * @return null|object
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ 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();
+ $result = $this->convertsPerfexCRMLeadToCustomer->execute($updatePerfexCRMObject->getContactEmail());
+
+ if(isset($result->payload)){
+ $crmClientId = $result->payload['client_id'];
+ if (isset($result->payload['company'])) {
+ $crmCompany = $result->payload['company'];
+ }
+ }
+ else{
+ //if reach this point, this means this user is not a official customer nor is a lead in crm
+ //Create Customer has 2 parts: Create Company (client), Create Contact
+ $result = $this->createsPerfexCRMCustomer->execute($crmCompany);
+ if(is_null($result)){
+ $crmCompany = $crmCompany." 2";
+ $result = $this->createsPerfexCRMCustomer->execute($crmCompany);
+ }
+ $crmClientId = $result->payload['clientId'];
+
+ $customerContactObject = new CustomerContactObject(
+ $crmClientId,
+ $updatePerfexCRMObject->getContactName(),
+ $updatePerfexCRMObject->getContactName(),
+ $updatePerfexCRMObject->getContactEmail(),
+ "pU^T@sC#9Q",
+ "on",
+ "on"
+ );
+ $result = $this->createsPerfexCRMCustomerContact->execute($customerContactObject);
+ }
+
+ //Update custom fields to identify company reference from exchange or shipping portal
+ $value_exists = false;
+ if (isset($result->payload['customfields'])) {
+ foreach ($result->payload['customfields'] as $element) {
+ if ($element['value'] === $updatePerfexCRMObject->getCompanyReference()) {
+ $value_exists = true;
+ break;
+ }
+ }
+ }
+ if(!$value_exists);
+ {
+ $result = $this->updatesPerfexCRMCustomer->execute($crmClientId, $crmCompany, $updatePerfexCRMObject->getCompanyReference());
+ }
+
+ // Get existing or create project, project has to exist first before milestone can appear under it
+ $result = $this->fetchesPerfexCRMProject->execute($updatePerfexCRMObject->getProjectName(), $crmClientId);
+ if(isset($result->payload)){
+ $project = $result->payload[0];
+ $projectId = $project['id'];
+ if($updatePerfexCRMObject->getProjectStatus() != PerfexCRMProjectStatus::NOT_STARTED && $project['status'] == PerfexCRMProjectStatus::NOT_STARTED)
+ {
+ $this->updatesPerfexCRMProject->execute($project, $updatePerfexCRMObject->getProjectStatus());
+ }
+ }
+ else{
+ $result = $this->createsPerfexCRMCustomerProject->execute($updatePerfexCRMObject->getProjectName(), $updatePerfexCRMObject->getProjectStatus(), $crmClientId);
+ if(isset($result->payload)){ //Here means project creation successful
+ $projectId = $result->payload['project_id'];
+ }
+ }
+
+ $tasks = $updatePerfexCRMObject->getTasks();
+ if(count($tasks) > 0){
+
+ //Create tasks with milestone
+ for($count=0; $count < count($tasks); $count++) {
+ $milestoneId = 0; //By default milestoneId is 0, having this set at individual task is optional
+ if($tasks[$count]['milestone'] != "") //Create milestone only if it is defined
+ {
+ // Get existing or create milestone, milestone has to exist first before task can appear under it
+ $result = $this->fetchesPerfexCRMMilestone->execute($tasks[$count]['milestone'], $projectId);
+ if(isset($result->payload)){
+ $array = json_decode(json_encode($result->payload[0]), true);
+ $milestoneId = $array['id'];
+ }
+ else{
+ $result = $this->createsPerfexCRMMilestone->execute($tasks[$count]['milestone'], $projectId, $count);
+ if(isset($result->payload)){
+ $milestoneId = $result->payload['milestone_id'];
+ }
+ }
+ }
+
+ $taskStatus = PerfexCRMTaskStatus::NOT_STARTED;
+ if($tasks[$count]['status'] != ''){
+ $taskStatus = $tasks[$count]['status'];
+ }
+
+ // Get existing or create task
+ $taskName = $tasks[$count]['name'];
+ $taskReference = $tasks[$count]['reference'];
+ $taskOnTaskCompletion = $tasks[$count]['on_task_completion'];
+ $taskIsAllowMultiple = $tasks[$count]['is_allow_multiple'];
+ $taskIsOnTaskCompletionUpdate = $tasks[$count]['is_on_task_completion_update'];
+ if($updatePerfexCRMObject->getInvoiceId() != 0 && $taskIsAllowMultiple){
+ $taskName = $taskName." (".$updatePerfexCRMObject->getInvoiceId().")";
+ $taskReference = $taskReference."_".$updatePerfexCRMObject->getInvoiceId();
+ if($taskOnTaskCompletion && $taskIsOnTaskCompletionUpdate){
+ $taskOnTaskCompletion = $taskOnTaskCompletion."_".$updatePerfexCRMObject->getInvoiceId();
+ }
+ }
+
+ $result = $this->fetchesPerfexCRMTask->execute($taskName, $milestoneId, 'project', $projectId, $updatePerfexCRMObject->getInvoiceId());
+ // Log::error("UpdatePerfexCRMProcessor task: ".$taskName." , ".json_encode($result));
+ Log::error("UpdatePerfexCRMProcessor task: ".$taskName);
+
+ if(isset($result->payload)){
+ //&& $result->payload[0]['status'] == PerfexCRMTaskStatus::NOT_STARTED
+ 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']);
+ }
+ }
+ else{
+ $createTaskPerfexCRMObject = new CreateTaskPerfexCRMObject(
+ "",
+ $taskName,
+ $tasks[$count]['description'],
+ "",
+ $projectId,
+ $milestoneId,
+ $taskReference,
+ $taskOnTaskCompletion,
+ $taskStatus,
+ $tasks[$count]['department'],
+ $tasks[$count]['priority'],
+ $tasks[$count]['duedate'],
+ $updatePerfexCRMObject->getInvoiceId()
+ );
+ $result = $this->createsPerfexCRMTask->execute($createTaskPerfexCRMObject);
+ }
+ }
+ }
+
+ $payload = [];
+ $payload['projectId'] = $projectId;
+ return (object) $payload;
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/ConvertsPerfexCRMLeadToCustomer.php b/app/Classes/Modules/PerfexCRM/Services/ConvertsPerfexCRMLeadToCustomer.php
new file mode 100644
index 00000000..e6e7323e
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/ConvertsPerfexCRMLeadToCustomer.php
@@ -0,0 +1,34 @@
+ config('perfexcrm.api_key'),])
+ ->get(config('perfexcrm.base_url').'/api/leads/convertocustomer/'.$email);
+
+ if($response->successful()){
+ $data = $response->json();
+
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomer.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomer.php
new file mode 100644
index 00000000..e5a19ce3
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomer.php
@@ -0,0 +1,37 @@
+ $companyName
+ ];
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/customers',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerContact.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerContact.php
new file mode 100644
index 00000000..519c1495
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerContact.php
@@ -0,0 +1,44 @@
+ $customerContactObject->getCustomerId(),
+ 'firstname' => $customerContactObject->getFirstName(),
+ 'lastname' => $customerContactObject->getLastName(),
+ 'email' => $customerContactObject->getEmail(), //$email
+ 'password' => $customerContactObject->getPassword(),
+ 'is_primary' => $customerContactObject->getIsPrimary(),
+ //'send_set_password_email' => $customerContactObject->getSendSetPasswordEmail(),
+ ];
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/contacts',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerProject.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerProject.php
new file mode 100644
index 00000000..f5d4de3e
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMCustomerProject.php
@@ -0,0 +1,43 @@
+ $projectName,
+ 'rel_type' => 'customer',
+ 'billing_type' => 1,
+ 'clientid' => $clientId,
+ 'start_date' => date('Y-m-d'),
+ 'status' => $status
+ ];
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/projects',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoice.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoice.php
new file mode 100644
index 00000000..ba0c2755
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoice.php
@@ -0,0 +1,63 @@
+getSubTotal() * 100) / 100;
+ $total = floor($invoicePerfexCRMObject->getTotal() * 100) / 100;
+
+ $data = [
+ 'clientid' => $invoicePerfexCRMObject->getClientId(),
+ 'number' => $invoicePerfexCRMObject->getNumber(),
+ 'date' => $invoicePerfexCRMObject->getDate(),
+ 'duedate' => $invoicePerfexCRMObject->getDueDate(),
+ 'currency' => $invoicePerfexCRMObject->getCurrency(),
+ 'subtotal' => number_format($subtotal, 2, '.', ''),
+ 'total' => number_format($total, 2, '.', ''),
+ 'billing_street' => $invoicePerfexCRMObject->getBillingStreet(),
+ 'project_id' => $invoicePerfexCRMObject->getProjectId(),
+ 'allowed_payment_modes[0]' => 1,
+ 'allowed_payment_modes[1]' => 2,
+ ];
+
+ for($count=0; $count < count($invoicePerfexCRMObject->getInvoiceItems()); $count++) {
+ $oneItem = [
+ "newitems[".$count."][description]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->description,
+ "newitems[".$count."][long_description]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->longDescription,
+ "newitems[".$count."][qty]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->qty,
+ "newitems[".$count."][rate]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->rate,
+ "newitems[".$count."][order]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->order,
+ "newitems[".$count."][unit]" => $invoicePerfexCRMObject->getInvoiceItems()[$count]->unit,
+ ];
+ $data = array_merge($data, $oneItem);
+ }
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/invoices',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoicePayment.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoicePayment.php
new file mode 100644
index 00000000..f3e92529
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMInvoicePayment.php
@@ -0,0 +1,43 @@
+ $invoicePaymentPerfexCRMObject->getInvoiceId(),
+ 'amount' => number_format($invoicePaymentPerfexCRMObject->getAmount(), 2, '.', ''),
+ 'date' => $invoicePaymentPerfexCRMObject->getDate(),
+ 'paymentmode' => $invoicePaymentPerfexCRMObject->getPaymentMode(),
+ 'transactionid' => $invoicePaymentPerfexCRMObject->getTransactionId(),
+ 'note' => $invoicePaymentPerfexCRMObject->getNote(),
+ ];
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/invoices/recordpayment',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMLead.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMLead.php
new file mode 100644
index 00000000..373f4171
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMLead.php
@@ -0,0 +1,50 @@
+ [
+ 3 => $createLeadPerfexCRMObject->getCompanyReference()
+ ]
+ ];
+
+ $data = [
+ 'name' => $createLeadPerfexCRMObject->getName(),
+ 'email' => $createLeadPerfexCRMObject->getEmail(),
+ 'phonenumber' => $createLeadPerfexCRMObject->getPhone(),
+ 'company' => $createLeadPerfexCRMObject->getCompanyName(),
+ 'source' => 1, //1: Exchange, 2: Shipping Portal
+ 'status' => 2, //2: Lead, 1: Customer,
+ 'custom_fields' => $custom_fields
+ ];
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/leads/byemail',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMMilestone.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMMilestone.php
new file mode 100644
index 00000000..80cb49bf
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMMilestone.php
@@ -0,0 +1,46 @@
+ $milestoneName,
+ 'project_id' => $projectId,
+ 'due_date' => date('Y-m-d'),
+ 'start_date' => date('Y-m-d')
+ ];
+
+ if($milestoneOrder != ""){
+ $data['milestone_order'] = $milestoneOrder;
+ }
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/milestones',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMTask.php b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMTask.php
new file mode 100644
index 00000000..feb8f9c8
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/CreatesPerfexCRMTask.php
@@ -0,0 +1,66 @@
+getDepartment() != ""){
+ $custom_fields = [
+ "tasks" => [
+ PerfexCRMCustomFields::TASKS_DEPARTMENT => $createTaskPerfexCRMObject->getDepartment()
+ ]
+ ];
+ }
+
+ $data = [
+ 'name' => $createTaskPerfexCRMObject->getName(),
+ 'description' => $createTaskPerfexCRMObject->getDescription(),
+ 'milestone' => $createTaskPerfexCRMObject->getMilestoneId(),
+ 'startdate' => date('Y-m-d'),
+ 'rel_type' => 'project',
+ 'rel_id' => $createTaskPerfexCRMObject->getProjectId(),
+ 'status' => $createTaskPerfexCRMObject->getStatus(),
+ 'is_system_created' => 1,
+ 'reference' => $createTaskPerfexCRMObject->getReference(),
+ 'on_task_completion' => $createTaskPerfexCRMObject->getOnTaskCompletion(),
+ 'custom_fields' => $custom_fields,
+ 'priority' => $createTaskPerfexCRMObject->getPriority(),
+ 'duedate' => date('Y-m-d', strtotime('+' . $createTaskPerfexCRMObject->getDuedate() . ' days')),
+ 'invoice_id' => $createTaskPerfexCRMObject->getInvoiceId(),
+ ];
+
+ if($createTaskPerfexCRMObject->getLeadId() != '') {
+ $data['rel_type'] = 'lead';
+ $data['rel_id'] = $createTaskPerfexCRMObject->getLeadId();
+ }
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/tasks',$data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMCustomer.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMCustomer.php
new file mode 100644
index 00000000..9f3928f6
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMCustomer.php
@@ -0,0 +1,34 @@
+ config('perfexcrm.api_key'),])
+ ->get(config('perfexcrm.base_url').'/api/customers/byemail/'.$email);
+
+ if($response->successful()){
+ $data = $response->json();
+
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMInvoice.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMInvoice.php
new file mode 100644
index 00000000..cd7aff30
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMInvoice.php
@@ -0,0 +1,36 @@
+ config('perfexcrm.api_key'),])
+ ->get(config('perfexcrm.base_url').'/api/invoices/customsearch/'.$clientId.'/'.$invoicePrefix.'/'.$invoiceNumber);
+
+ if($response->successful()){
+ $data = $response->json();
+
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMLead.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMLead.php
new file mode 100644
index 00000000..df9c754d
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMLead.php
@@ -0,0 +1,34 @@
+ config('perfexcrm.api_key'),])
+ ->get(config('perfexcrm.base_url').'/api/leads/byemail/'.$email);
+
+ if($response->successful()){
+ $data = $response->json();
+
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMMilestone.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMMilestone.php
new file mode 100644
index 00000000..30ae0737
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMMilestone.php
@@ -0,0 +1,40 @@
+ $milestoneName,
+ 'project_id' => $projectId,
+ ];
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/milestones/bynameandprojectid', $data);
+
+ if($response->successful()){
+ $data = $response->json();
+
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMProject.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMProject.php
new file mode 100644
index 00000000..c55f2c01
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMProject.php
@@ -0,0 +1,40 @@
+ $projectName,
+ 'clientid' => $clientId,
+ ];
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/projects/bynameandclientid', $data);
+
+ if($response->successful()){
+ $data = $response->json();
+
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php
new file mode 100644
index 00000000..6cd4f052
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/FetchesPerfexCRMTask.php
@@ -0,0 +1,54 @@
+ $taskName,
+ 'rel_type' => $relType,
+ 'rel_id' => $relId,
+ ];
+
+ if($milestoneId != '') {
+ $newItem = ['milestone' => $milestoneId ];
+ $data = array_merge($data, $newItem);
+ }
+
+ if($invoiceId != 0) {
+ $newItem = ['invoice_id' => $invoiceId ];
+ $data = array_merge($data, $newItem);
+ }
+
+ $response = Http::asForm()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->post(config('perfexcrm.base_url').'/api/tasks/customsearch', $data);
+
+ if($response->successful()){
+ $data = $response->json();
+
+ return (object) $data;
+ }else{
+ Helper::debugLogger($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php
new file mode 100644
index 00000000..6d91c081
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMCustomer.php
@@ -0,0 +1,48 @@
+ [
+ PerfexCRMCustomFields::CUSTOMERS_EXCHANGE_REFERENCE => $companyReference
+ ]
+ ];
+
+ $data = [
+ 'company' => $companyName,
+ 'custom_fields' => $custom_fields
+ ];
+
+ $response = Http::asJson()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->put(config('perfexcrm.base_url').'/api/customers/'.$customerId, $data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Helper::debugLogger($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMInvoice.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMInvoice.php
new file mode 100644
index 00000000..9adb9460
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMInvoice.php
@@ -0,0 +1,70 @@
+The Invoice number field is required.<\/p>
+ // The Invoice date field is required.<\/p>
+ //
The Currency field is required.<\/p>
+ //
The Items field is required.<\/p>
+ //
The Allow Payment Mode field is required.<\/p>
+ //
The Billing Street field is required.<\/p>
+ //
The Sub Total field is required.<\/p>
+ //
The Total field is required.<\/p>
+
+ $newInvoiceItems = [];
+ foreach ($invoice->items as $item) {
+ $item['itemid'] = $item['id'];
+ unset($item['id']);
+ $item['order'] = $item['item_order'];
+ unset($item['item_order']);
+ array_push($newInvoiceItems,$item);
+ }
+
+ $allowedPaymentModes = [];
+ array_push($allowedPaymentModes, 1, 2);
+
+ $data = [
+ 'number' => $invoice->number,
+ 'date' => $invoice->date,
+ 'duedate' => $invoice->duedate,
+ 'currency' => $invoice->currency,
+ 'subtotal' => $invoice->subtotal,
+ 'total' => $invoice->total,
+ 'billing_street' => $invoice->billing_street,
+ 'shipping_street' => $invoice->billing_street,
+ 'project_id' => $projectId,
+ 'items' => $newInvoiceItems,
+ 'allowed_payment_modes' => $allowedPaymentModes,
+ ];
+
+ $response = Http::asJson()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->put(config('perfexcrm.base_url').'/api/invoices/'.$invoice->id, $data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMLead.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMLead.php
new file mode 100644
index 00000000..f8fbc944
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMLead.php
@@ -0,0 +1,51 @@
+ [
+ PerfexCRMCustomFields::LEADS_EXCHANGE_REFERENCE => $companyReference
+ ]
+ ];
+
+ $data = [
+ 'name' => $lead->name,
+ 'email' => $lead->email,
+ 'phonenumber' => $lead->phonenumber,
+ 'company' => $lead->company,
+ 'custom_fields' => $custom_fields,
+ 'source' => $lead->source,
+ 'status' => $lead->status,
+ ];
+
+ $response = Http::asJson()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->put(config('perfexcrm.base_url').'/api/leads/'.$lead->id, $data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMProject.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMProject.php
new file mode 100644
index 00000000..cf782806
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMProject.php
@@ -0,0 +1,43 @@
+ $project['name'],
+ 'clientid' => $project['clientid'],
+ // 'rel_type' => 'customer',
+ 'billing_type' => 1,
+ 'start_date' => $project['start_date'],
+ 'status' => $status
+ ];
+
+ $response = Http::asJson()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->put(config('perfexcrm.base_url').'/api/projects/'.$project['id'], $data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server: ' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMTask.php b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMTask.php
new file mode 100644
index 00000000..95a09525
--- /dev/null
+++ b/app/Classes/Modules/PerfexCRM/Services/UpdatesPerfexCRMTask.php
@@ -0,0 +1,50 @@
+ $taskName,
+ 'milestone' => $milestoneId,
+ 'startdate' => $startDate,
+ 'duedate' => $dueDate,
+ 'rel_type' => 'project',
+ 'rel_id' => $projectId,
+ 'status' => $status,
+ 'repeat_every' => '',
+ ];
+
+ $response = Http::asJson()->withHeaders([
+ 'authtoken' => config('perfexcrm.api_key')])
+ ->put(config('perfexcrm.base_url').'/api/tasks/'.$taskId, $data);
+
+ if($response->successful()){
+ $data = $response->json();
+ return (object) $data;
+ }else{
+ Log::error($response);
+ return null;
+ }
+ }catch(\Exception $exception){
+ throw new MalformedRequestException('Unable to get correct response from Perfex CRM server' . $exception->getMessage());
+ }
+ }
+}
diff --git a/app/Classes/Modules/Rewards/ControllersLogic/CreateRewardLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/CreateRewardLogic.php
new file mode 100644
index 00000000..dc6fecf2
--- /dev/null
+++ b/app/Classes/Modules/Rewards/ControllersLogic/CreateRewardLogic.php
@@ -0,0 +1,67 @@
+ 'Create/Update Reward',
+ 'message' => 'You have successfully created/updated a reward'
+ ];
+ }
+
+ /** @var CreatesReward */
+ private $createsReward;
+
+ /** @var CanCreateReward */
+ private $canCreateReward;
+
+ /**
+ * CreateRewardLogic constructor.
+ */
+ public function __construct(CreatesReward $createsMilestone, CanCreateReward $canCreateReward)
+ {
+ $this->createsReward = $createsMilestone;
+ $this->canCreateReward = $canCreateReward;
+ }
+
+ /**
+ * @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
+ {
+ $userRewardObject = new RewardObject(
+ $request->input('id'),
+ $request->input('name'),
+ $request->input('description'),
+ $request->input('value'),
+ $request->input('type'),
+ $request->input('is_active'),
+ $request->input('order'),
+ );
+
+ $this->canCreateReward->passes($userRewardObject);
+
+ $result = $this->createsReward->execute($userRewardObject);
+
+ return $this->response(['data' => $result]);
+ }
+
+
+}
diff --git a/app/Classes/Modules/Rewards/ControllersLogic/DeleteRewardLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/DeleteRewardLogic.php
new file mode 100644
index 00000000..6b2c306c
--- /dev/null
+++ b/app/Classes/Modules/Rewards/ControllersLogic/DeleteRewardLogic.php
@@ -0,0 +1,74 @@
+ 'Delete Reward',
+ 'message' => 'You have successfully deleted the Reward'
+ ];
+ }
+
+ /** @var CanDeleteReward */
+ private $canDeleteReward;
+
+ /** @var DeletesReward */
+ private $deletesReward;
+
+ /** @var FetchesReward */
+ private $fetchesReward;
+
+
+ /**
+ * DeleteRewardLogic constructor.
+ * @param CanDeleteReward $canDeleteReward
+ * @param DeletesReward $deletesReward
+ * @param FetchesReward $fetchesReward
+ */
+ public function __construct(
+ CanDeleteReward $canDeleteReward,
+ DeletesReward $deletesReward,
+ FetchesReward $fetchesReward
+ )
+ {
+ $this->canDeleteReward = $canDeleteReward;
+ $this->deletesReward = $deletesReward;
+ $this->fetchesReward = $fetchesReward;
+ }
+
+ /**
+ * @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
+ {
+
+ $query = $this->fetchesReward->execute(['id' => $request->route('id')]);
+ $this->canDeleteReward->passes();
+ $this->deletesReward->execute($query);
+
+ return $this->resourceResponse(new RewardResource($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsDetailsLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsDetailsLogic.php
new file mode 100644
index 00000000..0f3cfda8
--- /dev/null
+++ b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsDetailsLogic.php
@@ -0,0 +1,49 @@
+ 'Retrieved Rewards Details',
+ 'message' => 'You have successfully retrieved a list of Rewards Details'
+ ];
+ }
+
+ /** @var ListsRewards */
+ private $listsRewards;
+
+ /**
+ * ListRewardsDetailsLogic constructor.
+ * @param ListsRewards $listsRewards
+ */
+ public function __construct(ListsRewards $listsRewards)
+ {
+ $this->listsRewards = $listsRewards;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $query = $this->listsRewards->execute($this->listsRewards->deserializeFilters($request->input('filters')));
+
+ return $this->collectionResponse(RewardDetailsResource::collection($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsLogic.php b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsLogic.php
new file mode 100644
index 00000000..593947e6
--- /dev/null
+++ b/app/Classes/Modules/Rewards/ControllersLogic/ListRewardsLogic.php
@@ -0,0 +1,50 @@
+ 'Retrieved Rewards',
+ 'message' => 'You have successfully retrieved a list of Rewards'
+ ];
+ }
+
+
+ /** @var ListsRewards */
+ private $listsRewards;
+
+ /**
+ * ListRewardsLogic constructor.
+ * @param ListsRewards $listsRewards
+ */
+ public function __construct(ListsRewards $listsRewards)
+ {
+ $this->listsRewards = $listsRewards;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $query = $this->listsRewards->execute($this->listsRewards->deserializeFilters($request->input('filters')));
+
+ return $this->collectionResponse(RewardResource::collection($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Rewards/DataTransferObjects/RewardObject.php b/app/Classes/Modules/Rewards/DataTransferObjects/RewardObject.php
new file mode 100644
index 00000000..45092222
--- /dev/null
+++ b/app/Classes/Modules/Rewards/DataTransferObjects/RewardObject.php
@@ -0,0 +1,106 @@
+id = $id;
+ $this->name = $name;
+ $this->description = $description;
+ $this->value = $value;
+ $this->type = $type;
+ $this->isActive = $isActive;
+ $this->order = $order;
+ }
+
+ /**
+ * @return int
+ */
+ public function getId(): int
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDescription(): string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getIsActive(): bool
+ {
+ return $this->isActive;
+ }
+
+ /**
+ * @return int
+ */
+ public function getOrder(): int
+ {
+ return $this->order ?? 9999;
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Services/CreatesReward.php b/app/Classes/Modules/Rewards/Services/CreatesReward.php
new file mode 100644
index 00000000..c9a16fdc
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Services/CreatesReward.php
@@ -0,0 +1,45 @@
+name = $object->getName();
+ // $model->description = $object->getDescription();
+ // $model->value = $object->getValue();
+ // $model->type = $object->getType();
+ // $model->is_active = $object->getIsActive();
+ // return $this->handler($model);
+
+ try{
+ $data = [
+ 'id' => $object->getId(),
+ 'name' => $object->getName(),
+ 'description' => $object->getDescription(),
+ 'value' => $object->getValue(),
+ 'type' => $object->getType(),
+ 'is_active' => $object->getIsActive(),
+ 'order' => $object->getOrder(),
+ ];
+
+ return Reward::upsert([$data], ['id'], ['name', 'description', 'value', 'type', 'is_active', 'order']);
+
+ } catch (QueryException $exception){
+ throw new MalformedRequestException($exception);
+ }
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Services/CreatesUserReward.php b/app/Classes/Modules/Rewards/Services/CreatesUserReward.php
new file mode 100644
index 00000000..8a7f5d29
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Services/CreatesUserReward.php
@@ -0,0 +1,32 @@
+user_id = $user->id;
+ $model->voucher_id = $voucherId;
+ return $this->handler($reward->users(), $model);
+ }
+ else{
+ $model = new UserReward();
+ $model->voucher_id = $voucherId;
+ return $this->handler($user->rewards(), $model);
+ }
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Services/DeletesReward.php b/app/Classes/Modules/Rewards/Services/DeletesReward.php
new file mode 100644
index 00000000..5d3d9133
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Services/DeletesReward.php
@@ -0,0 +1,19 @@
+handler($model);
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Services/FetchesReward.php b/app/Classes/Modules/Rewards/Services/FetchesReward.php
new file mode 100644
index 00000000..ab336092
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Services/FetchesReward.php
@@ -0,0 +1,34 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ public function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Services/ListsRewards.php b/app/Classes/Modules/Rewards/Services/ListsRewards.php
new file mode 100644
index 00000000..79d0d1d4
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Services/ListsRewards.php
@@ -0,0 +1,33 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Services/ListsUserRewards.php b/app/Classes/Modules/Rewards/Services/ListsUserRewards.php
new file mode 100644
index 00000000..d81761cd
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Services/ListsUserRewards.php
@@ -0,0 +1,33 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Standards/Rules/CanCreateReward.php b/app/Classes/Modules/Rewards/Standards/Rules/CanCreateReward.php
new file mode 100644
index 00000000..5249b35c
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Standards/Rules/CanCreateReward.php
@@ -0,0 +1,55 @@
+rewardValidation = $rewardValidation;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function authorized(): bool
+ {
+ if (!Auth::user()->can('add reward')) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param RewardObject $object
+ * @return bool
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ protected function validators($object): bool
+ {
+ return true; // $this->rewardValidation->validate($object, 'POST');
+ }
+
+ /**
+ * @param RewardObject $object
+ * @return bool
+ */
+ protected function criteria($object): bool
+ {
+ return true;
+ }
+}
diff --git a/app/Classes/Modules/Rewards/Standards/Rules/CanDeleteReward.php b/app/Classes/Modules/Rewards/Standards/Rules/CanDeleteReward.php
new file mode 100644
index 00000000..888b0d1e
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Standards/Rules/CanDeleteReward.php
@@ -0,0 +1,44 @@
+can('delete reward')) {
+ return false;
+ }
+
+ return true;
+
+ }
+
+ /**
+ * @param RewardObject $object
+ * @return bool
+ */
+ protected function validators($object): bool
+ {
+ return true;
+
+ }
+
+
+ /**
+ * @param RewardObject $object
+ * @return bool
+ */
+ protected function criteria($object): bool
+ {
+ return true;
+ }
+
+}
diff --git a/app/Classes/Modules/Rewards/Standards/Validators/RewardValidation.php b/app/Classes/Modules/Rewards/Standards/Validators/RewardValidation.php
new file mode 100644
index 00000000..18d875dc
--- /dev/null
+++ b/app/Classes/Modules/Rewards/Standards/Validators/RewardValidation.php
@@ -0,0 +1,55 @@
+ $object->getName(),
+ 'description' => $object->getDescription(),
+ ];
+
+ return $data;
+ }
+
+ /**
+ * @param null|string $type
+ * @return array
+ */
+ protected function rules(): array {
+ return [
+ 'name' => [
+ 'required',
+ function ($attribute, $value, $fail) {
+ // Check if reward name already exists in the database
+ $existingMilestone = Reward::where('name', $value)->first();
+ if ($existingMilestone) {
+ $fail("The {$attribute} reward name already exists in the database.");
+ }
+ },
+ ],
+ 'description' => [
+ 'required',
+ ]
+ ];
+ }
+
+ /**
+ * @return array
+ */
+ protected function messages(): array {
+ return [];
+ }
+
+}
diff --git a/app/Classes/Modules/Segments/ControllersLogic/CreateSegmentLogic.php b/app/Classes/Modules/Segments/ControllersLogic/CreateSegmentLogic.php
index 32eeb217..f00f8996 100644
--- a/app/Classes/Modules/Segments/ControllersLogic/CreateSegmentLogic.php
+++ b/app/Classes/Modules/Segments/ControllersLogic/CreateSegmentLogic.php
@@ -54,7 +54,7 @@ class CreateSegmentLogic extends AbstractControllerLogic
*/
public function logic(Request $request) : JsonResponse
{
- $segment_object = new SegmentObject($request->input('name'));
+ $segment_object = new SegmentObject($request->input('name'), $request->input('type') ?? 2);
$this->canCreateSegment->passes($segment_object);
$segment = $this->createsSegment->execute($segment_object);
diff --git a/app/Classes/Modules/Segments/DataTransferObjects/SeasonalSegmentObject.php b/app/Classes/Modules/Segments/DataTransferObjects/SeasonalSegmentObject.php
new file mode 100644
index 00000000..d045556e
--- /dev/null
+++ b/app/Classes/Modules/Segments/DataTransferObjects/SeasonalSegmentObject.php
@@ -0,0 +1,74 @@
+companyId = $companyId;
+ $this->segmentId = $segmentId;
+ $this->starting_on = $starting_on;
+ $this->ending_on = $ending_on;
+ $this->status = $status;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCompanyId(): int
+ {
+ return $this->companyId;
+ }
+
+ /**
+ * @return int
+ */
+ public function getSegmentId(): int
+ {
+ return $this->segmentId;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getStartingOn(): string
+ {
+ return $this->starting_on;
+ }
+
+ /**
+ * @return string
+ */
+ public function getEndingOn(): ?string
+ {
+ return $this->ending_on;
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Segments/Processors/RiskAnalysisProcessor.php b/app/Classes/Modules/Segments/Processors/RiskAnalysisProcessor.php
new file mode 100644
index 00000000..66cf3a93
--- /dev/null
+++ b/app/Classes/Modules/Segments/Processors/RiskAnalysisProcessor.php
@@ -0,0 +1,37 @@
+assignSegmentProcessor = $assignSegmentProcessor;
+ }
+
+
+ function execute(User $user, string $token){
+ $captchaToken = GoogleReCaptchaV3::verifyResponse($token);
+
+ if($captchaToken->getScore() < 0.6 && !in_array('timeout-or-duplicate', $captchaToken->getErrorCodes())){
+ $this->assignSegmentProcessor->execute($user->company()->first(), 18);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Segments/Services/CreatesSeasonalSegment.php b/app/Classes/Modules/Segments/Services/CreatesSeasonalSegment.php
new file mode 100644
index 00000000..0125a956
--- /dev/null
+++ b/app/Classes/Modules/Segments/Services/CreatesSeasonalSegment.php
@@ -0,0 +1,27 @@
+company_id = $object->getCompanyId();
+ $model->segment_id = $object->getSegmentId();
+ $model->starting_on = $object->getStartingOn();
+ $model->ending_on = $object->getEndingOn();
+
+ return $this->handler($model);
+
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Segments/Services/CreatesSegment.php b/app/Classes/Modules/Segments/Services/CreatesSegment.php
index b18a8db5..0c2491d9 100644
--- a/app/Classes/Modules/Segments/Services/CreatesSegment.php
+++ b/app/Classes/Modules/Segments/Services/CreatesSegment.php
@@ -4,7 +4,6 @@ namespace App\Classes\Modules\Segments\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Segments\DataTransferObjects\SegmentObject;
-use App\Classes\ValueObjects\Constants\SegmentConstants;
use App\Models\Segment;
class CreatesSegment extends AbstractUpdateRecord
@@ -17,7 +16,7 @@ class CreatesSegment extends AbstractUpdateRecord
public function execute(SegmentObject $object) {
$model = new Segment();
$model->name = $object->getName();
- $model->type = SegmentConstants::CUSTOM_SEGMENT;
+ $model->type = $object->getType();
return $this->handler($model);
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderDocumentLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderDocumentLogic.php
new file mode 100644
index 00000000..297d275b
--- /dev/null
+++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderDocumentLogic.php
@@ -0,0 +1,48 @@
+ 'Generate Bulk Purchase Order',
+ 'message' => 'You have successfully generated bulk purchase order'
+ ];
+ }
+
+ /** @var GenerateGroupTransactionsPurchaseOrder */
+ private $generateGroupTransactionsPurchaseOrder;
+
+ /**
+ * CreateBulkPurchaseOrderDocumentLogic constructor.
+ * @param GenerateGroupTransactionsPurchaseOrder $generateGroupTransactionsPurchaseOrder
+ */
+ public function __construct(GenerateGroupTransactionsPurchaseOrder $generateGroupTransactionsPurchaseOrder)
+ {
+ $this->generateGroupTransactionsPurchaseOrder = $generateGroupTransactionsPurchaseOrder;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ */
+ public function logic(Request $request): JsonResponse
+ {
+ $this->generateGroupTransactionsPurchaseOrder::dispatch();
+
+ return $this->response([]);
+ }
+}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php
new file mode 100644
index 00000000..3771c559
--- /dev/null
+++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateBulkPurchaseOrderTransactionLogic.php
@@ -0,0 +1,100 @@
+ 'Generate Bulk Purchase Order',
+ 'message' => 'You have successfully generated bulk purchase order'
+ ];
+ }
+
+ /** @var ListsGroups */
+ private $listsGroups;
+
+ /** @var FetchesCompany */
+ private $fetchesCompany;
+
+ /** @var CreateInvoiceDocumentProcessor */
+ private $invoiceDocumentProcessor;
+
+ /**
+ * CreateBulkPurchaseOrderTransactionLogic constructor.
+ * @param ListsGroups $listsGroups
+ * @param FetchesCompany $fetchesCompany
+ * @param CreateInvoiceDocumentProcessor $invoiceDocumentProcessor
+ */
+ public function __construct(ListsGroups $listsGroups, FetchesCompany $fetchesCompany, CreateInvoiceDocumentProcessor $invoiceDocumentProcessor)
+ {
+ $this->listsGroups = $listsGroups;
+ $this->fetchesCompany = $fetchesCompany;
+ $this->invoiceDocumentProcessor = $invoiceDocumentProcessor;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $groups = $this->listsGroups->execute(['issuer_id' => [$request->issuer_id], 'date_start' => $request->start_date, 'date_end' => $request->end_date]);
+
+ foreach($groups as $group){
+ foreach($group->transactions as $transaction){
+ if($transaction->owner()->owner()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('status', '!=', ApprovalStatus::APPROVED)->exists()){
+ throw new MalformedRequestException('You can\'t generate bulk purchased order if there in uncomplete transactions');
+ }
+ }
+ }
+
+ foreach($groups as $group){
+ foreach($group->transactions as $transaction){
+ $completed_transactions = $transaction->owner()->owner()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('status', '=', ApprovalStatus::APPROVED)->get();
+
+ $purchaseOrder = $transaction->owner()->transactions()
+ ->where('type', TransactionType::PURCHASE_ORDER)
+ ->complete()
+ ->first();
+
+ foreach($completed_transactions as $transaction){
+ $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]);
+
+ $voucherRedemption = $transaction->voucherRedemption;
+
+ // purchase order
+ $this->invoiceDocumentProcessor->execute($transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption);
+ }
+ }
+ }
+
+ return $this->response([]);
+
+ }
+
+
+
+}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php
index 460f5181..46534d77 100644
--- a/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php
+++ b/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php
@@ -14,6 +14,7 @@ use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\CompanyType;
use App\Classes\ValueObjects\Constants\DocumentType;
+use App\Classes\Jobs\SendUserPaymentProofUploadedEmail;
use App\Models\Company;
use App\Models\Document;
use Illuminate\Http\JsonResponse;
@@ -50,20 +51,25 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic
/** @var CreateInvoiceTransactionProcessor */
private $createInvoiceTransactionProcessor;
+ /** @var SendUserPaymentProofUploadedEmail */
+ private $sendUserPaymentProofUploadedEmail;
+
/**
- * CreatePaymentVerificationDocumentLogic constructor.
+ * CreatePaymentProofDocumentLogic constructor.
* @param FetchesTransaction $fetchesTransaction
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
* @param UpdatesTransactionStatus $updatesTransactionStatus
+ * @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
*/
- public function __construct(FetchesTransaction $fetchesTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor)
+ public function __construct(FetchesTransaction $fetchesTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor, SendUserPaymentProofUploadedEmail $sendUserPaymentProofUploadedEmail)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
+ $this->sendUserPaymentProofUploadedEmail = $sendUserPaymentProofUploadedEmail;
}
/**
@@ -81,12 +87,21 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic
/** @var Document $document */
$document = $this->createsDocument->execute($transaction, $object);
- $this->createsFile->execute($document, $object);
+ $file = $this->createsFile->execute($document, $object);
$this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED);
$this->createInvoiceTransactionProcessor->execute($transaction->owner->booking);
+ // send email to customer
+ // todo: a function to send a proof to the receipiant, they have to give us a email of the receipiant and also need to submiited purchase order
+ $companyEmployee = $transaction->owner->booking->company->employees;
+ foreach ($companyEmployee as $employee) {
+ if (app()->environment('production') || in_array($employee->email, ['cief.enquirycntr@gmail.com', 'tech.ciefmalaysia@gmail.com'])) {
+ $this->sendUserPaymentProofUploadedEmail::dispatch($employee, $transaction->owner->booking, $file[0]);
+ }
+ }
+
return $this->response([]);
}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php
index 1cf71643..2dcbd0f7 100644
--- a/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php
+++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierTransactionLogic.php
@@ -4,16 +4,18 @@
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\Modules\Transactions\Processors\CreateSupplierTransactionProcessor;
+use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Models\Document;
+use App\Models\Group;
+
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
-use Meneses\LaravelMpdf\Facades\LaravelMpdf;
+use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Companies\Services\FetchesCompany;
-use Meneses\LaravelLaravelMpdf\Facades\LaravelLaravelMpdf;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
@@ -43,19 +45,25 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic
/** @var CreatesFiles */
private $createsFile;
+ /** @var GeneratesTransactionBillNumber */
+ private $generatesTransactionBillNumber;
+
+
/**
* CreateSupplierTransactionLogic constructor.
* @param FetchesCompany $fetchesCompany
* @param CreateSupplierTransactionProcessor $createSupplierTransactionProcessor
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
+ * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
*/
- public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile)
+ public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber)
{
$this->fetchesCompany = $fetchesCompany;
$this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor;
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
+ $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
}
public function logic(Request $request) : JsonResponse
@@ -71,6 +79,45 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic
if(!count($this->createSupplierTransactionProcessor->getBills())) return $this->response([]);
+ $group = new Group();
+ $group->save();
+
+ $issuer = '';
+ $receiver = '';
+ $amount = 0;
+ $original_amount = 0;
+ $currency_id = 0;
+ $original_currency_id = '';
+ $currency_rate = '';
+ $tax = 0;
+ $service_charge = 0;
+
+ foreach ($this->createSupplierTransactionProcessor->getBills() as $key => $row) {
+ $group->transactions()->sync($row->id, false);
+ $issuer = $row->issuer;
+ $receiver = $row->receiver;
+ $amount += $row->amount;
+ $original_amount += $row->original_amount;
+ $currency_id = $row->currency_id;
+ $original_currency_id = $row->original_currency_id;
+ $currency_rate = $row->currency_rate;
+ $tax += $row->tax;
+ $service_charge += $row->service_charge;
+ }
+
+ $group->issuer = $issuer;
+ $group->receiver = $receiver;
+ $group->reference = $this->generatesTransactionBillNumber->execute('SPO-');
+ $group->amount = $amount;
+ $group->original_amount = $original_amount;
+ $group->currency_id = $currency_id;
+ $group->original_currency_id = $original_currency_id;
+ $group->currency_rate = $currency_rate;
+ $group->tax = $tax;
+ $group->service_charge = $service_charge;
+
+ $group->update();
+
$pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $this->createSupplierTransactionProcessor->getBills(), 'transferFeeTransactions' => $this->createSupplierTransactionProcessor->getTransferTransactions(), 'supplier' => $supplier]);
$object = new DocumentObject(
@@ -82,9 +129,10 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic
);
/** @var Document $document */
- $document = $this->createsDocument->execute($supplier, $object);
+ $document = $this->createsDocument->execute($group, $object);
$this->createsFile->execute($document, $object);
+
return $this->response([]);
}
}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/DeleteGroupLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/DeleteGroupLogic.php
new file mode 100644
index 00000000..601a1530
--- /dev/null
+++ b/app/Classes/Modules/Transactions/ControllersLogic/DeleteGroupLogic.php
@@ -0,0 +1,73 @@
+ 'Delete Group Transaction',
+ 'message' => 'You have successfully deleted this Group Transaction'
+ ];
+ }
+
+ /** @var UpdatesTransactionStatus */
+ private $updatesTransactionStatus;
+
+ /** @var FetchesGroup */
+ private $fetchesGroup;
+
+ /** @var DeletesTransaction */
+ private $deletesTransaction;
+
+ /**
+ * DeleteGroupLogic constructor.
+ * @param updatesTransactionStatus $updatesTransactionStatus
+ * @param FetchesGroup $fetchesGroup
+ * @param DeletesTransaction $deletesTransaction
+ */
+ public function __construct(updatesTransactionStatus $updatesTransactionStatus, FetchesGroup $fetchesGroup, DeletesTransaction $deletesTransaction)
+ {
+ $this->updatesTransactionStatus = $updatesTransactionStatus;
+ $this->fetchesGroup = $fetchesGroup;
+ $this->deletesTransaction = $deletesTransaction;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $group = $this->fetchesGroup->execute(['id' => $request->route('id')]);
+
+ $items = $group->transactions()->get();
+
+ foreach($items as $item) {
+ $bill = $item;
+ $payment = $bill->owner;
+ $group->transactions()->detach($bill->id);
+ $this->updatesTransactionStatus->execute($payment, ApprovalStatus::APPROVED);
+ $this->deletesTransaction->execute($bill);
+ }
+
+ $group->delete();
+
+ return $this->resourceResponse(new GroupResource($group));
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php
index e1875f0a..09dfddb8 100644
--- a/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php
+++ b/app/Classes/Modules/Transactions/ControllersLogic/DownloadMockUpWhiteFormPdfLogic.php
@@ -6,9 +6,8 @@ namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\Modules\Transactions\Processors\CreateSupplierTransactionProcessor;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
-use Meneses\LaravelMpdf\Facades\LaravelMpdf;
+use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use App\Classes\Modules\Companies\Services\FetchesCompany;
-use Meneses\LaravelLaravelMpdf\Facades\LaravelLaravelMpdf;
class DownloadMockUpWhiteFormPdfLogic
{
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php
new file mode 100644
index 00000000..a078a01d
--- /dev/null
+++ b/app/Classes/Modules/Transactions/ControllersLogic/FetchCompanyTransactionStatementLogic.php
@@ -0,0 +1,73 @@
+ 'Retrieved Company Transaction Statement',
+ 'message' => 'You have successfully retrieved company transaction statement'
+ ];
+ }
+
+ /**
+ * FetchCompanyAccountBalanceLogic constructor.
+ */
+ public function __construct()
+ {
+ }
+
+ public function logic(Request $request): JsonResponse
+ {
+ $companyId = $request->route('id');
+
+ $transactions = Transaction::where(function ($query) use ($companyId) {
+ $query
+ ->where('type', TransactionType::PAYMENT)
+ ->where('owner_type', Booking::class)
+ ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
+ ->whereHas('booking', function ($query) use ($companyId) {
+ $query->where('company_id', $companyId);
+ });
+ })
+ ->orWhere(function ($query) use ($companyId) {
+ $query->whereHas('owner', function ($query) use ($companyId) {
+ $query->where('owner_id', $companyId);
+ $query->where('owner_type', Company::class);
+ })
+ ->where('owner_type', Wallet::class)
+ ->where('type', '!=', TransactionType::PAYMENT);
+ })
+ ->orWhere(function ($query) use ($companyId) {
+ $query
+ ->where('type', TransactionType::INVOICE)
+ ->where('owner_type', Booking::class)
+ ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
+ ->whereHas('booking', function ($query) use ($companyId) {
+ $query->where('company_id', $companyId);
+ });
+ })
+ ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
+ ->orderBy('created_at', 'desc')
+ ->get();
+
+ return $this->collectionResponse(PaymentTransactionResource::collection($transactions));
+ }
+}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfLogic.php
new file mode 100644
index 00000000..949dd243
--- /dev/null
+++ b/app/Classes/Modules/Transactions/ControllersLogic/GenerateCreditNotePdfLogic.php
@@ -0,0 +1,56 @@
+fetchesTransaction = $fetchesTransaction;
+ $this->fetchesCompany = $fetchesCompany;
+ $this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor;
+ }
+
+ /**
+ * @param Request $request
+ * @return string|\Symfony\Component\HttpFoundation\Response
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute(Request $request)
+ {
+ $transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]);
+
+ $booking = $transaction->booking;
+
+ $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]);
+
+ $pdf = LaravelMpdf::loadView('pages.pdfs.credit_note', ['transaction' => $transaction, 'booking' => $booking, 'supplier' => $supplier]);
+
+ return $pdf->stream('CreditNote.pdf');
+ }
+}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/ListGroupsLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/ListGroupsLogic.php
new file mode 100644
index 00000000..5cf99bbe
--- /dev/null
+++ b/app/Classes/Modules/Transactions/ControllersLogic/ListGroupsLogic.php
@@ -0,0 +1,42 @@
+listsGroups = $listsGroups;
+ }
+
+ /**
+ * @return array
+ */
+ protected function notification():array {
+ return [
+ 'title' => 'Retrieved Groups',
+ 'message' => 'You have successfully retrieved a list of groups'
+ ];
+ }
+
+ /** @var ListsGroups */
+ private $listsGroups;
+
+ public function logic(Request $request) : JsonResponse
+ {
+ $query = $this->listsGroups->execute($this->listsGroups->deserializeFilters($request->input('filters')));
+
+ return $this->collectionResponse(GroupResource::collection($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php
index e824547e..0500d6ae 100644
--- a/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php
+++ b/app/Classes/Modules/Transactions/ControllersLogic/SuspendTransactionLogic.php
@@ -7,6 +7,9 @@ use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\ListsTransactions;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
+use App\Classes\Modules\Vouchers\Services\FetchesVoucherRedemption;
+use App\Classes\Modules\Vouchers\Services\CreatesVoucherRedemption;
+use App\Classes\Modules\Vouchers\Services\RollbacksRedemption;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Http\Resources\BookingResource;
use App\Http\Resources\TransactionResource;
@@ -32,16 +35,27 @@ class SuspendTransactionLogic extends AbstractControllerLogic
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
+ /** @var RollbacksRedemption */
+ private $rollbacksRedemption;
+
+ /** @var FetchesVoucherRedemption */
+ private $fetchesVoucherRedemption;
+
+ /** @var CreatesVoucherRedemption */
+ private $createsVoucherRedemption;
/**
* SuspendTransactionLogic constructor.
* @param FetchesTransaction $fetchesTransaction
* @param UpdatesTransactionStatus $updatesTransactionStatus
*/
- public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus)
+ public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, RollbacksRedemption $rollbacksRedemption, FetchesVoucherRedemption $fetchesVoucherRedemption, CreatesVoucherRedemption $createsVoucherRedemption)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->updatesTransactionStatus = $updatesTransactionStatus;
+ $this->rollbacksRedemption = $rollbacksRedemption;
+ $this->fetchesVoucherRedemption = $fetchesVoucherRedemption;
+ $this->createsVoucherRedemption = $createsVoucherRedemption;
}
@@ -52,6 +66,14 @@ class SuspendTransactionLogic extends AbstractControllerLogic
$this->updatesTransactionStatus->execute($transaction, ApprovalStatus::SUSPENDED);
+ if($transaction->voucherRedemption) {
+ $result = $this->rollbacksRedemption->execute($transaction->voucherRedemption->redemption_id);
+ if($result){
+ $redemptionId = $result->id;
+ $this->createsVoucherRedemption->execute($transaction, $transaction->voucherRedemption->voucher, $redemptionId, $transaction->voucherRedemption->value);
+ }
+ }
+
return $this->response([]);
}
diff --git a/app/Classes/Modules/Transactions/ControllersLogic/UpdateGroupLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/UpdateGroupLogic.php
new file mode 100644
index 00000000..aa432aea
--- /dev/null
+++ b/app/Classes/Modules/Transactions/ControllersLogic/UpdateGroupLogic.php
@@ -0,0 +1,185 @@
+ 'Update Group Transaction',
+ 'message' => 'You have successfully updated this Group Transaction'
+ ];
+ }
+
+ /** @var FetchesGroup */
+ private $fetchesGroup;
+
+ /** @var FetchesCompany */
+ private $fetchesCompany;
+
+ /** @var CalculatesTransactionServiceCharge */
+ private $calculatesTransactionServiceCharge;
+
+ /** @var UpdatesTransaction */
+ private $updatesTransaction;
+
+ /** @var CalculatesTransactionTransferFee */
+ private $calculatesTransactionTransferFee;
+
+ /** @var CreatesDocument */
+ private $createsDocument;
+
+ /** @var CreatesFiles */
+ private $createsFile;
+
+ /**
+ * UpdateGroupLogic constructor.
+ * @param FetchesGroup $fetchesGroup
+ * @param FetchesCompany $fetchesCompany
+ * @param CalculatesTransactionServiceCharge $calculatesTransactionServiceCharge
+ * @param UpdatesTransaction $updatesTransaction
+ * @param CalculatesTransactionTransferFee $calculatesTransactionTransferFee
+ * @param CreatesDocument $createsDocument
+ * @param CreatesFiles $createsFile
+ */
+ public function __construct(FetchesGroup $fetchesGroup, FetchesCompany $fetchesCompany, CalculatesTransactionServiceCharge $calculatesTransactionServiceCharge, UpdatesTransaction $updatesTransaction, CalculatesTransactionTransferFee $calculatesTransactionTransferFee, CreatesDocument $createsDocument, CreatesFiles $createsFile)
+ {
+ $this->fetchesGroup = $fetchesGroup;
+ $this->fetchesCompany = $fetchesCompany;
+ $this->calculatesTransactionServiceCharge = $calculatesTransactionServiceCharge;
+ $this->updatesTransaction = $updatesTransaction;
+ $this->calculatesTransactionTransferFee = $calculatesTransactionTransferFee;
+ $this->createsDocument = $createsDocument;
+ $this->createsFile = $createsFile;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $group = $this->fetchesGroup->execute(['id' => $request->route('id')]);
+
+ $transactions = $group->transactions()->get();
+
+ $rate = $request->input('rate');
+
+ $supplier = $this->fetchesCompany->execute(['id' => $request->input('supplier_id')]);
+
+// $group->transactions()->update(['issuer' => $supplier->id, 'currency_rate' => $rate]);
+// $group->transactions()->update(['amount' => DB::raw('(original_amount * (1 / currency_rate)) + service_charge + tax')]);
+
+
+ foreach($transactions as $transaction) {
+
+ $constant = SegmentConstant::where('reference', SegmentConstants::SERVICE_CHARGE)->where('detail->id', $supplier->id)->first();
+ $serviceCharge = $this->calculatesTransactionServiceCharge->execute($transaction->original_amount, $rate, $constant);
+
+ $object = new TransactionObject(
+ $transaction->bill_no,
+ TransactionType::BILL,
+ $supplier->id,
+ 1,
+ $supplier->banks()->where('default', true)->first()->id,
+ PaymentMethodType::CASH,
+ $transaction->original_amount * (1 / $rate),
+ $transaction->original_amount,
+ 1,
+ $transaction->original_currency_id,
+ $rate,
+ 0,
+ $serviceCharge,
+ null,
+ ApprovalStatus::PENDING_VERIFICATION
+ );
+
+ $billTransaction = $this->updatesTransaction->execute($transaction, $object);
+
+ $transferTransaction = $transaction->transactions()->where('type', TransactionType::TRANSFER_FEE)->first();
+
+ $transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant);
+
+ $object = new TransactionObject(
+ $transferTransaction->bill_no,
+ TransactionType::TRANSFER_FEE,
+ $supplier->id,
+ 1,
+ $supplier->banks()->where('default', true)->first()->id,
+ PaymentMethodType::CASH,
+ $transaction->original_amount,
+ $transaction->original_amount,
+ $transaction->original_currency_id,
+ $transaction->original_currency_id,
+ 1,
+ 0,
+ $transferFee,
+ null,
+ ApprovalStatus::PENDING_VERIFICATION
+ );
+
+ $this->updatesTransaction->execute($transferTransaction, $object);
+ }
+
+ $group->issuer = $supplier->id;
+ $group->amount = $group->transactions()->sum('amount');
+ $group->currency_rate = $rate;
+ $group->tax = $group->transactions()->sum('tax');
+ $group->service_charge = $group->transactions()->sum('service_charge');
+
+ $group->save();
+
+ $group->documents()->delete();
+
+ $transferFeeTransactions = $group->transactions()->with([
+ 'transactions' => function ($transaction) {
+ return $transaction->where('type', TransactionType::TRANSFER_FEE);
+ }])->get()->pluck('transactions')->flatten();
+
+ $pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $group->transactions, 'transferFeeTransactions' => $transferFeeTransactions, 'supplier' => $supplier]);
+
+ $object = new DocumentObject(
+ DocumentType::CURRENCY_VENDOR_ORDER,
+ [chunk_split('data:application/pdf;base64,'.base64_encode($pdf->output()))],
+ '',
+ ApprovalStatus::COMPLETED,
+ 'currency_vendor_order'
+ );
+
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($group, $object);
+ $this->createsFile->execute($document, $object);
+
+ return $this->resourceResponse(new GroupResource($group));
+ }
+
+}
diff --git a/app/Classes/Modules/Transactions/Processors/CreateCashBackTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateCashBackTransactionProcessor.php
new file mode 100644
index 00000000..738cb2b7
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Processors/CreateCashBackTransactionProcessor.php
@@ -0,0 +1,122 @@
+createsTransaction = $createsTransaction;
+ $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
+ $this->creditWalletProcessor = $creditWalletProcessor;
+ }
+
+
+ /**
+ * @param Transaction $transaction
+ * @return Transaction|\Illuminate\Database\Eloquent\Model
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute(Transaction $transaction)
+ {
+ // leave this disabled until ready to launch to production
+ return;
+ // ((MYR) * (cash back %)) * (1/conversion rate)
+ $current_total_cash_back = Transaction::
+ where('type', TransactionType::CASH_BACK)
+ ->whereMonth('created_at', Carbon::now()->month)->sum('amount');
+
+ if (
+ $current_total_cash_back < CashBack::MAX &&
+ $transaction->owner()->first()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->count() > 0
+ ) {
+
+ $cash_back_segemnt = CashBack::SEGMENT;
+
+ foreach ($cash_back_segemnt as $key => $row) {
+ if ($row['max_value'] > $transaction->amount && $row['min_value'] <= $transaction->amount) {
+
+ $method = $this->getRandomWeightedElement($row['weight']);
+
+ $total = $transaction->amount * $row['percent'][$method];
+
+ $billNumber = $this->generatesTransactionBillNumber->execute('CBACK-');
+
+ $object = new TransactionObject(
+ $billNumber,
+ TransactionType::CASH_BACK,
+ $transaction->issuer,
+ 1,
+ 1,
+ PaymentMethodType::WALLET,
+ $total,
+ $total,
+ $transaction->currency_id,
+ $transaction->currency_id,
+ 1,
+ 0,
+ 0,
+ null,
+ ApprovalStatus::APPROVED,
+ null,
+ 'cash back ' . $transaction->bill_no
+ );
+
+ $cash_back_transaction = $this->createsTransaction->execute($transaction, $object);
+
+ $company = $transaction->owner()->first()->company;
+ $credit = $this->creditWalletProcessor->execute($company, $transaction->type, $cash_back_transaction->amount, 'cash back ' . $transaction->bill_no);
+ }
+ }
+ }
+ return $transaction;
+ }
+
+ public function getRandomWeightedElement(array $weightedValues) {
+ $rand = mt_rand(1, (int) array_sum($weightedValues));
+ foreach ($weightedValues as $key => $value) {
+ $rand -= $value;
+ if ($rand <= 0) {
+ return $key;
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php
new file mode 100644
index 00000000..6215a1e1
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php
@@ -0,0 +1,77 @@
+createsDocument = $createsDocument;
+ $this->createsFile = $createsFile;
+ }
+
+ /**
+ * @param $transaction
+ * @param $purchaseOrder
+ * @param $supplier
+ * @param $document_type
+ * @return void
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute($transaction, $purchaseOrder, $supplier, $document_type, $voucherRedemption = null)
+ {
+ $lowercaseDocumentType = strtolower($document_type);
+
+ $order_pdf = LaravelMpdf::loadView('pages.pdfs.' . $lowercaseDocumentType, ['transaction' => $transaction, 'po_order_transaction' => $purchaseOrder, 'supplier' => $supplier, 'voucher_redemption' => $voucherRedemption]);
+
+ if($purchaseOrder->booking->service_id === 4) {
+ $purchaseOrderDocuments = $purchaseOrder->booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->get();
+
+
+ $oMerger = PDFMerger::init();
+ $order_pdf->save(storage_path('app/documents/temp.pdf'));
+
+ $oMerger->addPDF(storage_path('app/documents/temp.pdf'), 'all');
+ foreach ($purchaseOrderDocuments as $document){
+ $oMerger->addPDF(storage_path('app/documents/'.$document->files()->first()->file->file_info->original->file), 'all');
+ }
+
+ $oMerger->merge();
+
+ $order_pdf = $oMerger;
+ }
+
+ $document_object = new DocumentObject(
+ $document_type,
+ [chunk_split('data:application/pdf;base64,' . base64_encode($order_pdf->output()))],
+ '',
+ ApprovalStatus::COMPLETED,
+ $lowercaseDocumentType . 's'
+ );
+
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($purchaseOrder->booking, $document_object);
+ $this->createsFile->execute($document, $document_object);
+
+ }
+}
diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php
index 5a29a1a2..7f105dc8 100644
--- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php
+++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionProcessor.php
@@ -2,6 +2,7 @@
namespace App\Classes\Modules\Transactions\Processors;
+use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingTransferredAmount;
use App\Classes\Modules\ServiceTypes\Services\FetchesServiceConfigurations;
@@ -11,25 +12,17 @@ use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPaidAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingCurrencyAverageRate;
use App\Classes\Modules\Companies\Services\FetchesCompany;
-use App\Classes\Modules\Documents\Services\CreatesDocument;
-use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Bookings\Services\UpdatesBookingStatus;
-
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
-use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\SegmentConstants;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Booking;
-use App\Models\Document;
use App\Models\SegmentConstant;
-use Meneses\LaravelMpdf\Facades\LaravelMpdf;
class CreateInvoiceTransactionProcessor
{
- /** @var ListsTransactions */
- private $listsTransactions;
/** @var CreatesTransaction */
private $createsTransaction;
@@ -46,24 +39,19 @@ class CreateInvoiceTransactionProcessor
/** @var CalculatesBookingTransferredAmount */
private $calculatesBookingTransferredAmount;
- /** @var FetchesServiceConfigurations */
- private $fetchesServiceConfigurations;
-
/** @var CalculatesBookingCurrencyAverageRate */
private $calculatesBookingCurrencyAverageRate;
/** @var FetchesCompany */
private $fetchesCompany;
- /** @var CreatesDocument */
- private $createsDocument;
-
- /** @var CreatesFiles */
- private $createsFile;
-
/** @var UpdatesBookingStatus */
private $updatesBookingStatus;
+ /** @var CreateInvoiceDocumentProcessor */
+ private $invoiceDocumentProcessor;
+
+
/**
* CreateInvoiceTransactionProcessor constructor.
* @param ListsTransactions $listsTransactions
@@ -75,33 +63,30 @@ class CreateInvoiceTransactionProcessor
* @param FetchesServiceConfigurations $fetchesServiceConfigurations
* @param CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate
* @param FetchesCompany $fetchesCompany
- * @param CreatesDocument $createsDocument
- * @param CreatesFiles $createsFile
* @param UpdatesBookingStatus $updatesBookingStatus
+ * @param CreateInvoiceDocumentProcessor $invoiceDocumentProcessor
*/
- public function __construct(ListsTransactions $listsTransactions, CreatesTransaction $createsTransaction, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CalculatesBookingPaidAmount $calculatesBookingPaidAmount, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingTransferredAmount $calculatesBookingTransferredAmount, FetchesServiceConfigurations $fetchesServiceConfigurations, CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate, FetchesCompany $fetchesCompany, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesBookingStatus $updatesBookingStatus)
+ public function __construct(ListsTransactions $listsTransactions, CreatesTransaction $createsTransaction, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CalculatesBookingPaidAmount $calculatesBookingPaidAmount, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingTransferredAmount $calculatesBookingTransferredAmount, FetchesServiceConfigurations $fetchesServiceConfigurations, CalculatesBookingCurrencyAverageRate $calculatesBookingCurrencyAverageRate, FetchesCompany $fetchesCompany, UpdatesBookingStatus $updatesBookingStatus, CreateInvoiceDocumentProcessor $invoiceDocumentProcessor)
{
- $this->listsTransactions = $listsTransactions;
$this->createsTransaction = $createsTransaction;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->calculatesBookingPaidAmount = $calculatesBookingPaidAmount;
$this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount;
$this->calculatesBookingTransferredAmount = $calculatesBookingTransferredAmount;
- $this->fetchesServiceConfigurations = $fetchesServiceConfigurations;
$this->calculatesBookingCurrencyAverageRate = $calculatesBookingCurrencyAverageRate;
$this->fetchesCompany = $fetchesCompany;
- $this->createsDocument = $createsDocument;
- $this->createsFile = $createsFile;
$this->updatesBookingStatus = $updatesBookingStatus;
+ $this->invoiceDocumentProcessor = $invoiceDocumentProcessor;
}
+
/**
* @param Booking $booking
* @return void
- * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws MalformedRequestException
*/
- public function execute(Booking $booking)
+ public function execute(Booking $booking)
{
if ($booking->status === ApprovalStatus::COMPLETED) {
@@ -116,24 +101,28 @@ class CreateInvoiceTransactionProcessor
return;
}
// confirm that all payments has been transferred
- if($this->calculatesBookingTransferredAmount->execute($booking) !== $this->calculatesBookingPaidAmount->execute($booking)){
+ if ($this->calculatesBookingTransferredAmount->execute($booking) !== $this->calculatesBookingPaidAmount->execute($booking)) {
return;
}
- $po_order_transaction = $booking->transactions()
+ $purchaseOrder = $booking->transactions()
->where('type', TransactionType::PURCHASE_ORDER)
->complete()
->first();
$constants = SegmentConstant::where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->id', $booking->service->id)->first();
- if($constants->detail->is_billable && !$po_order_transaction) {
+ if ($constants->detail->is_billable && !$purchaseOrder) {
return;
}
+ // $transaction = $booking->transactions()
+ // ->where('type', TransactionType::PAYMENT)
+ // ->first();
+
$transaction = $booking->transactions()
- ->where('type', TransactionType::PAYMENT)
- ->first();
+ ->where('type', TransactionType::PAYMENT)
+ ->latest()->get()[0];
$billNumber = $this->generatesTransactionBillNumber->execute('INV-');
@@ -166,46 +155,20 @@ class CreateInvoiceTransactionProcessor
null,
ApprovalStatus::APPROVED
);
- $invoice_transaction = $this->createsTransaction->execute($po_order_transaction->booking, $transaction_object);
+ $invoice_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object);
+
+ $voucherRedemption = $transaction->voucherRedemption;
$supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]);
- $purchase_order_pdf = LaravelMpdf::loadView('pages.pdfs.purchase_order', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
- $document_object = new DocumentObject(
- DocumentType::PURCHASE_ORDER,
- [chunk_split('data:application/pdf;base64,'.base64_encode($purchase_order_pdf->output()))],
- '',
- ApprovalStatus::COMPLETED,
- 'purchase_orders'
- );
- /** @var Document $document */
- $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
- $this->createsFile->execute($document, $document_object);
+ // purchase order
+ $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption);
- $deliver_order_pdf = LaravelMpdf::loadView('pages.pdfs.deliver_order', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
- $document_object = new DocumentObject(
- DocumentType::DELIVER_ORDER,
- [chunk_split('data:application/pdf;base64,'.base64_encode($deliver_order_pdf->output()))],
- '',
- ApprovalStatus::COMPLETED,
- 'delivery_orders'
- );
+ // deliver order
+ $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::DELIVER_ORDER, $voucherRedemption);
- /** @var Document $document */
- $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
- $this->createsFile->execute($document, $document_object);
-
-
- $invoice_pdf = LaravelMpdf::loadView('pages.pdfs.invoice', ['invoice_transaction' => $invoice_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
- $document_object = new DocumentObject(
- DocumentType::INVOICE,
- [chunk_split('data:application/pdf;base64,'.base64_encode($invoice_pdf->output()))],
- '',
- ApprovalStatus::COMPLETED,
- 'invoices'
- );
- $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
- $this->createsFile->execute($document, $document_object);
+ // invoice
+ $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::INVOICE, $voucherRedemption);
$billNumber = $this->generatesTransactionBillNumber->execute('SPDO-');
@@ -231,19 +194,16 @@ class CreateInvoiceTransactionProcessor
null,
ApprovalStatus::APPROVED
);
- $supplier_deliver_order_transaction = $this->createsTransaction->execute($po_order_transaction->booking, $transaction_object);
+ $supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object);
- $supplier_order_pdf = LaravelMpdf::loadView('pages.pdfs.supplier_deliver_order', ['supplier_deliver_order_transaction' => $supplier_deliver_order_transaction, 'po_order_transaction' => $po_order_transaction, 'supplier' => $supplier]);
- $document_object = new DocumentObject(
- DocumentType::SUPPLIER_DELIVER_ORDER,
- [chunk_split('data:application/pdf;base64,'.base64_encode($supplier_order_pdf->output()))],
- '',
- ApprovalStatus::COMPLETED,
- 'supplier_delivery_orders'
- );
- $document = $this->createsDocument->execute($po_order_transaction->booking, $document_object);
- $this->createsFile->execute($document, $document_object);
+ // supply deliver order
+ $this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER, null);
$this->updatesBookingStatus->execute($booking, ApprovalStatus::COMPLETED);
+
+ // update perfex crm
+ // if(config('perfexcrm.is_enabled') == 'true'){
+ // CreatePerfexCRMInvoice::dispatch($invoice_transaction, $purchaseOrder, $supplier);
+ // }
}
}
diff --git a/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php
index 3e7e8137..8dab2d24 100644
--- a/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php
+++ b/app/Classes/Modules/Transactions/Processors/CreateProformaInvoiceTransactionProcessor.php
@@ -24,7 +24,7 @@ use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Booking;
use App\Models\Document;
use Carbon\Carbon;
-use Meneses\LaravelMpdf\Facades\LaravelMpdf;
+use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
class CreateProformaInvoiceTransactionProcessor
{
@@ -100,7 +100,7 @@ class CreateProformaInvoiceTransactionProcessor
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
- public function execute(Booking $booking)
+ public function execute(Booking $booking)
{
$po_order_transaction = $booking->transactions()
@@ -129,14 +129,26 @@ class CreateProformaInvoiceTransactionProcessor
$billNumber = $this->generatesTransactionBillNumber->execute('PROFORMA-');
- $payable_amount = $booking->transactions()->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])->payments()->sum('amount');
+ $payable_amount = $booking->transactions()->payments()->where(function($query){
+ return $query->where(function($query){
+ return $query->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString());
+ })->orWhere(function($query){
+ return $query->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
+ });
+ })->sum('amount');
$booking_amount = $booking->fix_amount;
$transaction = $booking->transactions()
->where('type', TransactionType::PAYMENT)
->first();
- $booking_currency_average_rate = $booking_amount / $booking->transactions()->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::SUSPENDED])->payments()->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total');
+ $booking_currency_average_rate = $booking_amount / $booking->transactions()->payments()->where(function($query){
+ return $query->where(function($query){
+ return $query->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString());
+ })->orWhere(function($query){
+ return $query->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
+ });
+ })->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total');
$total_service_charge = $booking->transactions()
->where('type', TransactionType::PAYMENT)
diff --git a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php
index 75516fdf..9b944a5b 100644
--- a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php
+++ b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php
@@ -90,14 +90,15 @@ 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-');
- $transferFee = $this->calculatesTransactionTransferFee->execute($payment->original_amount, $constant);
+ $transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant);
$object = new TransactionObject($transferFeeNumber, TransactionType::TRANSFER_FEE, 1, $supplier->id,
$supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH,
$payment->original_amount, $payment->original_amount, $payment->original_currency_id, $payment->original_currency_id,
@@ -140,4 +141,4 @@ class CreateSupplierTransactionProcessor
$this->transferFee->push($transferFee);
}
-}
\ No newline at end of file
+}
diff --git a/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsPurchaseOrder.php b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsPurchaseOrder.php
new file mode 100644
index 00000000..91267ff2
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsPurchaseOrder.php
@@ -0,0 +1,89 @@
+listsGroups = $listsGroups;
+ $this->fetchesCompany = $fetchesCompany;
+ $this->createsDocument = $createsDocument;
+ $this->createsFile = $createsFile;
+ }
+
+ public function execute(){
+ $groups = Group::whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->whereDoesntHave('transactions', function ($query){
+ $query->whereHasMorph('owner', [Transaction::class], function($query){
+ return $query->whereHas('booking', function($query){
+ return $query->whereDoesntHave('transactions', function($query){
+ return $query->where('type', TransactionType::PURCHASE_ORDER)->where('status', '=', ApprovalStatus::APPROVED);
+ });
+ });
+ });
+ })->get();
+
+
+ foreach ($groups as $group) {
+// if ($transaction->owner()->owner()->transactions()->where('type', TransactionType::PURCHASE_ORDER)->where('status', '!=', ApprovalStatus::APPROVED)->exists()) {
+// throw new MalformedRequestException('You can\'t generate bulk purchased order if there in uncomplete transactions');
+// }
+ $supplier = $this->fetchesCompany->execute(['id' => $group->issuer]);
+
+ $document_type = DocumentType::BULK_PURCHASE_ORDER;
+
+ $lowercaseDocumentType = strtolower($document_type);
+
+ $order_pdf = LaravelMpdf::loadView('pages.pdfs.bulk_purchase_order', ['group' => $group, 'supplier' => $supplier]);
+ $document_object = new DocumentObject(
+ $document_type,
+ [chunk_split('data:application/pdf;base64,' . base64_encode($order_pdf->output()))],
+ '',
+ ApprovalStatus::COMPLETED,
+ $lowercaseDocumentType . 's'
+ );
+
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($group, $document_object);
+ $this->createsFile->execute($document, $document_object);
+
+ $group->status = ApprovalStatus::COMPLETED;
+ $group->save();
+
+ }
+ }
+}
diff --git a/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsWhiteForm.php b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsWhiteForm.php
new file mode 100644
index 00000000..2b66b478
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Processors/GeneratesGroupTransactionsWhiteForm.php
@@ -0,0 +1,74 @@
+listsGroups = $listsGroups;
+ $this->fetchesCompany = $fetchesCompany;
+ $this->createsDocument = $createsDocument;
+ $this->createsFile = $createsFile;
+ }
+
+
+ public function execute(){
+ $groups = Group::where('status', ApprovalStatus::PENDING_SUBMISSION)->get();
+
+
+ foreach ($groups as $group) {
+ $pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $group->transactions, 'transferFeeTransactions' => $group->transferFees, 'supplier' => $group->issuerCompany]);
+
+ $object = new DocumentObject(
+ DocumentType::CURRENCY_VENDOR_ORDER,
+ [chunk_split('data:application/pdf;base64,'.base64_encode($pdf->output()))],
+ '',
+ ApprovalStatus::COMPLETED,
+ 'currency_vendor_order'
+ );
+
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($group, $object);
+ $this->createsFile->execute($document, $object);
+
+ $group->status = ApprovalStatus::APPROVED;
+ $group->save();
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php
index 7c7277f8..c1d36edb 100644
--- a/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php
+++ b/app/Classes/Modules/Transactions/Processors/UpdateWalletTransactionProcessor.php
@@ -27,6 +27,9 @@ class UpdateWalletTransactionProcessor
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
+ /** @var UpdatesWallet */
+ private $updatesWallet;
+
public function __construct(UpdatesTransactionStatus $updatesTransactionStatus, UpdatesWallet $updatesWallet, FetchesTransaction $fetchesTransaction)
{
$this->fetchesTransaction = $fetchesTransaction;
diff --git a/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php b/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php
index e04edee2..695b465e 100644
--- a/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php
+++ b/app/Classes/Modules/Transactions/Services/CalculatesTransactionServiceCharge.php
@@ -25,15 +25,18 @@ class CalculatesTransactionServiceCharge
* @param SegmentConstant|null $service_charge
* @return float
*/
- public function execute(float $amount, float $rate, ?SegmentConstant $service_charge) {
+ public function execute(float $amount, float $rate, ?SegmentConstant $service_charge)
+ {
- if(!$service_charge) {
+ if (!$service_charge) {
return 0;
}
- $transfer_fee = $this->calculatesTransactionTransferFee->execute($amount, $service_charge);
- return $service_charge->detail->amount->type === 'percentage' ? (($amount + $transfer_fee) * ( (float) $service_charge->detail->amount->value /100) * (1/$rate)) : (float) $service_charge->detail->amount->value;
-
+ $transfer_fee = $this->calculatesTransactionTransferFee->execute(($amount * (1 / $rate)), $service_charge);
+ if (isset($service_charge->detail->amount)) {
+ return $service_charge->detail->amount->type === 'percentage' ? (($amount + $transfer_fee) * ((float) $service_charge->detail->amount->value / 100) * (1 / $rate)) : (float) $service_charge->detail->amount->value;
+ } else {
+ return 0;
+ }
}
-
-}
\ No newline at end of file
+}
diff --git a/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php b/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php
index 5a02a394..0bcaba28 100644
--- a/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php
+++ b/app/Classes/Modules/Transactions/Services/CalculatesTransactionTransferFee.php
@@ -12,13 +12,17 @@ class CalculatesTransactionTransferFee
* @param SegmentConstant|null $service_charge
* @return float
*/
- public function execute(float $amount, ?SegmentConstant $service_charge) {
+ public function execute(float $amount, ?SegmentConstant $service_charge)
+ {
- if(!$service_charge) {
+ if (!$service_charge) {
return 0;
}
- return $service_charge->detail->transferFee->type === 'percentage' ? $amount * ((float) $service_charge->detail->transferFee->value /100) : (float) $service_charge->detail->transferFee->value;
+ if (isset($service_charge->detail->transferFee)) {
+ return $service_charge->detail->transferFee->type === 'percentage' ? $amount * ((float) $service_charge->detail->transferFee->value / 100) : (float) $service_charge->detail->transferFee->value;
+ } else {
+ return 0;
+ }
}
-
-}
\ No newline at end of file
+}
diff --git a/app/Classes/Modules/Transactions/Services/ChecksIfGroupTransactionBillNumberExists.php b/app/Classes/Modules/Transactions/Services/ChecksIfGroupTransactionBillNumberExists.php
new file mode 100644
index 00000000..d3be2121
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Services/ChecksIfGroupTransactionBillNumberExists.php
@@ -0,0 +1,22 @@
+repository = $repository;
+ }
+
+ public function execute(string $bill_no): bool {
+ return $this->repository->where('reference', $bill_no)->exists();
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Transactions/Services/FetchesGroup.php b/app/Classes/Modules/Transactions/Services/FetchesGroup.php
new file mode 100644
index 00000000..6533664b
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Services/FetchesGroup.php
@@ -0,0 +1,31 @@
+repository = $repository;
+ }
+
+ /**
+ * @return Builder
+ */
+ public function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Transactions/Services/GeneratesGroupTransactionBillNumber.php b/app/Classes/Modules/Transactions/Services/GeneratesGroupTransactionBillNumber.php
new file mode 100644
index 00000000..a8aab459
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Services/GeneratesGroupTransactionBillNumber.php
@@ -0,0 +1,39 @@
+checksIfGroupTransactionBillNumberExists = $checksIfGroupTransactionBillNumberExists;
+ }
+
+ /**
+ * @param string $prefix
+ * @param Carbon|null $date
+ * @return string
+ */
+ public function execute(string $prefix, ?Carbon $date = null): string {
+ if(!$date){
+ $date = carbon::now();
+ }
+
+ $billNumber = $prefix.$date->format('Y').$date->format('m').'-'.mt_rand(10000, 99999);
+
+ return !$this->checksIfGroupTransactionBillNumberExists->execute($billNumber) ? $billNumber : self::execute($prefix);
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php b/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php
index 7f92ab60..943da696 100644
--- a/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php
+++ b/app/Classes/Modules/Transactions/Services/GeneratesTransactionBillNumber.php
@@ -3,6 +3,7 @@
namespace App\Classes\Modules\Transactions\Services;
+use App\Classes\Exceptions\InternalServerErrorException;
use Carbon\Carbon;
class GeneratesTransactionBillNumber
@@ -23,15 +24,25 @@ class GeneratesTransactionBillNumber
/**
* @param string $prefix
+ * @param Carbon|null $date
* @return string
*/
- public function execute(string $prefix): string {
- $date = carbon::now();
+ public function execute(string $prefix, ?Carbon $date = null): string {
+ if (!$date) {
+ $date = Carbon::now();
+ }
- $billNumber = $prefix.$date->format('Y').$date->format('m').'-'.mt_rand(10000, 99999);
-
- return !$this->checksIfTransactionBillNumberExists->execute($billNumber) ? $billNumber : self::execute($prefix);
+ $attempt = 0;
+ while ($attempt < 10) { // Retry up to 10 times
+ $billNumber = $prefix . $date->format('Y') . $date->format('m') . '-' . microtime(true);
+ if (!$this->checksIfTransactionBillNumberExists->execute($billNumber)) {
+ return $billNumber;
+ }
+ $attempt++;
+ }
+ throw new InternalServerErrorException("Unable to generate unique bill number after {$attempt} attempts.");
}
-}
\ No newline at end of file
+
+}
diff --git a/app/Classes/Modules/Transactions/Services/ListsGroups.php b/app/Classes/Modules/Transactions/Services/ListsGroups.php
new file mode 100644
index 00000000..f9126739
--- /dev/null
+++ b/app/Classes/Modules/Transactions/Services/ListsGroups.php
@@ -0,0 +1,31 @@
+repository = $repository;
+ }
+
+ /**
+ * @return Builder
+ */
+ public function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php b/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php
index aba7b07a..d1c5065f 100644
--- a/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php
+++ b/app/Classes/Modules/Transactions/Services/UpdatesTransaction.php
@@ -2,19 +2,21 @@
namespace App\Classes\Modules\Transactions\Services;
+use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\General\Eloquent\AbstractUpdateRelationshipRecord;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Models\Booking;
use App\Models\Transaction;
+use Illuminate\Database\Eloquent\Model;
class UpdatesTransaction extends AbstractUpdateRecord
{
/**
* @param Transaction $transaction
* @param TransactionObject $object
- * @return \Illuminate\Database\Eloquent\Model
- * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @return Model
+ * @throws MalformedRequestException
*/
public function execute(Transaction $transaction, TransactionObject $object) {
$transaction->recipient_bank_account_id = $object->getRecipientBankAccountId();
@@ -30,4 +32,4 @@ class UpdatesTransaction extends AbstractUpdateRecord
return $this->handler($transaction);
}
-}
\ 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 769f2dc0..d92e3e9a 100644
--- a/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php
+++ b/app/Classes/Modules/Transactions/Services/UpdatesTransactionStatus.php
@@ -3,10 +3,31 @@
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\Vouchers\Processors\Voucherify\TransactionToVoucherifyProcessor;
+use App\Classes\ValueObjects\Constants\ApprovalStatus;
+use App\Classes\ValueObjects\Constants\TransactionType;
class UpdatesTransactionStatus extends AbstractUpdateRecord
{
+ /** @var TransactionToPerfexCRMProcessorV2 */
+ private $transactionToPerfexCRMProcessor;
+
+ /** @var TransactionToVoucherifyProcessor */
+ private $transactionToVoucherifyProcessor;
+
+ /**
+ * UpdatesTransactionStatus constructor.
+ * @param TransactionToPerfexCRMProcessorV2 $transactionToPerfexCRMProcessor
+ * @param TransactionToVoucherifyProcessor $transactionToVoucherifyProcessor
+ */
+ public function __construct(TransactionToPerfexCRMProcessorV2 $transactionToPerfexCRMProcessor, TransactionToVoucherifyProcessor $transactionToVoucherifyProcessor)
+ {
+ $this->transactionToPerfexCRMProcessor = $transactionToPerfexCRMProcessor;
+ $this->transactionToVoucherifyProcessor = $transactionToVoucherifyProcessor;
+ }
+
/**
* @param Transaction $model
@@ -16,7 +37,17 @@ class UpdatesTransactionStatus extends AbstractUpdateRecord
*/
public function execute(Transaction $model, int $status)
{
+ $transaction = clone($model);
$model->status = $status;
- return $this->handler($model);
+ $result = $this->handler($model);
+ if(config('perfexcrm.is_enabled') == 'true'){
+ $this->transactionToPerfexCRMProcessor->execute($transaction, $status);
+ }
+
+ if($status == ApprovalStatus::APPROVED && ($transaction->type == TransactionType::PAYMENT || $transaction->type == TransactionType::TOP_UP)){
+ $this->transactionToVoucherifyProcessor->execute($transaction, "PAID");
+ }
+
+ return $result;
}
-}
\ No newline at end of file
+}
diff --git a/app/Classes/Modules/Vouchers/ControllersLogic/CreateVoucherLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/CreateVoucherLogic.php
new file mode 100644
index 00000000..a5d65544
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/ControllersLogic/CreateVoucherLogic.php
@@ -0,0 +1,49 @@
+ 'Create Voucher',
+ 'message' => 'You have successfully created a voucher'
+ ];
+ }
+
+ /** @var CreateVoucherProcessor */
+ private $createVoucherProcessor;
+
+ /**
+ * CreateVoucherLogic constructor.
+ * @param CreateVoucherProcessor $createVoucherProcessor
+ */
+ public function __construct(CreateVoucherProcessor $createVoucherProcessor)
+ {
+ $this->createVoucherProcessor = $createVoucherProcessor;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $user = User::where('id', $request->input('userId'))->first();
+ $result = $this->createVoucherProcessor->execute($user, $request->input('voucherCode'), null);
+ return $this->response(['data' => $result]);
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php
new file mode 100644
index 00000000..16356f0f
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/ControllersLogic/ListUserVouchersLogic.php
@@ -0,0 +1,46 @@
+listsUserRewards = $listsUserRewards;
+ }
+
+ /**
+ * @return array
+ */
+ protected function notification():array {
+ return [
+ 'title' => 'Retrieved User Vouchers',
+ 'message' => 'You have successfully retrieved a list of user vouchers'
+ ];
+ }
+
+ /** @var ListsUserRewards */
+ private $listsUserRewards;
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $query = $this->listsUserRewards->execute($this->listsUserRewards->deserializeFilters($request->input('filters')));
+ return $this->collectionResponse(UserRewardResource::collection($query));
+ }
+
+}
diff --git a/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php b/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php
new file mode 100644
index 00000000..4b820eb1
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/ControllersLogic/ValidateVoucherLogic.php
@@ -0,0 +1,53 @@
+ 'Fetch Voucher',
+ 'message' => 'You have successfully fetched a voucher'
+ ];
+ }
+
+ /** @var ValidatesVoucherifyVoucher */
+ private $validatesVoucherifyVoucher;
+
+ /**
+ * ValidateVoucherLogic constructor.
+ * @param ValidatesVoucherifyVoucher $validatesVoucherifyVoucher
+ */
+ public function __construct(ValidatesVoucherifyVoucher $validatesVoucherifyVoucher)
+ {
+ $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws MalformedRequestException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $booking = Booking::find($request->input('itemId'));
+ $employee = $booking->company->employees()->first();
+
+ $validateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject($booking->company_id, $request->input('voucherCode'), $request->input('amount'), $employee);
+ $result = $this->validatesVoucherifyVoucher->execute($validateVoucherifyVoucherObject);
+ return $this->response(['data' => $result]);
+ }
+
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyCustomerObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyCustomerObject.php
new file mode 100644
index 00000000..9678dafe
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyCustomerObject.php
@@ -0,0 +1,74 @@
+companyId = $companyId;
+ $this->user = $user;
+ $this->isNew = $isNew;
+ $this->acquisitionChannel = $acquisitionChannel;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCompanyId(): int
+ {
+ return $this->companyId;
+ }
+
+
+ /**
+ * @return User
+ */
+ public function getUser(): User
+ {
+ return $this->user;
+ }
+
+
+ /**
+ * @return bool
+ */
+ public function getIsNew(): bool
+ {
+ return $this->isNew;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAcquisitionChannel(): string
+ {
+ if(!$this->isNew){
+ return "";
+ }
+ return $this->acquisitionChannel;
+ }
+
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyOrderObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyOrderObject.php
new file mode 100644
index 00000000..c483d45e
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/CreateVoucherifyOrderObject.php
@@ -0,0 +1,95 @@
+employee = $employee;
+ $this->companyId = $companyId;
+ $this->transactionId = $transactionId;
+ $this->amount = $amount;
+ $this->isNoVoucher = $isNoVoucher;
+ $this->isTopUpWallet = $isTopUpWallet;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCompanyId(): int
+ {
+ return $this->companyId;
+ }
+
+ /**
+ * @return int
+ */
+ public function getTransactionId(): int
+ {
+ return $this->transactionId;
+ }
+
+ /**
+ * @return float
+ */
+ public function getAmount(): float
+ {
+ return $this->amount;
+ }
+
+ /**
+ * @return User
+ */
+ public function getEmployee(): User
+ {
+ return $this->employee;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getIsNoVoucher(): bool
+ {
+ return $this->isNoVoucher;
+ }
+
+ /**
+ * @return bool
+ */
+ public function getIsTopUpWallet(): bool
+ {
+ return $this->isTopUpWallet;
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/RedeemVoucherifyVoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/RedeemVoucherifyVoucherObject.php
new file mode 100644
index 00000000..325df2f4
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/RedeemVoucherifyVoucherObject.php
@@ -0,0 +1,83 @@
+companyId = $companyId;
+ $this->transactionId = $transactionId;
+ $this->promoCode = $promoCode;
+ $this->amount = $amount;
+ $this->employee = $employee;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCompanyId(): int
+ {
+ return $this->companyId;
+ }
+
+
+ /**
+ * @return int
+ */
+ public function getTransactionId(): int
+ {
+ return $this->transactionId;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getPromoCode(): string
+ {
+ return $this->promoCode;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAmount(): string
+ {
+ return $this->amount;
+ }
+
+ /**
+ * @return object
+ */
+ public function getEmployee(): object
+ {
+ return $this->employee;
+ }
+
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/UpdateVoucherifyOrderObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/UpdateVoucherifyOrderObject.php
new file mode 100644
index 00000000..bc49ee86
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/UpdateVoucherifyOrderObject.php
@@ -0,0 +1,42 @@
+id = $id;
+ $this->status = $status;
+ }
+
+ /**
+ * @return string
+ */
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getStatus(): string
+ {
+ return $this->status;
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/ValidateVoucherifyVoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidateVoucherifyVoucherObject.php
new file mode 100644
index 00000000..b48bee16
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidateVoucherifyVoucherObject.php
@@ -0,0 +1,70 @@
+companyId = $companyId;
+ $this->voucherCode = $voucherCode;
+ $this->amount = $amount;
+ $this->user = $user;
+ }
+
+ /**
+ * @return int
+ */
+ public function getCompanyId(): int
+ {
+ return $this->companyId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getVoucherCode(): string
+ {
+ return $this->voucherCode;
+ }
+
+ /**
+ * @return float
+ */
+ public function getAmount(): float
+ {
+ return $this->amount;
+ }
+
+ /**
+ * @return User
+ */
+ public function getUser(): User
+ {
+ return $this->user;
+ }
+
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/ValidatedVoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidatedVoucherObject.php
new file mode 100644
index 00000000..c3e766c2
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/ValidatedVoucherObject.php
@@ -0,0 +1,82 @@
+voucher_name = $voucher_name;
+ $this->code = $code;
+ $this->discount_type = $discount_type;
+ $this->total_discount_amount = $total_discount_amount;
+ $this->total_amount = $total_amount;
+ }
+
+ /**
+ * @return string
+ */
+ public function getVoucherName(): string
+ {
+ return $this->voucher_name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCode(): string
+ {
+ return $this->code;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDiscountType(): string
+ {
+ return $this->discount_type;
+ }
+
+ /**
+ * @return float
+ */
+ public function getTotalDiscountAmount(): float
+ {
+ return $this->total_discount_amount / 100;
+ }
+
+ /**
+ * @return float
+ */
+ public function getTotalAmount(): float
+ {
+ return $this->total_amount / 100;
+ }
+
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherEntityObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherEntityObject.php
new file mode 100644
index 00000000..692b221b
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherEntityObject.php
@@ -0,0 +1,41 @@
+id = $id;
+ $this->type = $type;
+ }
+
+ /**
+ * @return string
+ */
+ public function getId(): string
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php
new file mode 100644
index 00000000..f624b3cc
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/DataTransferObjects/VoucherObject.php
@@ -0,0 +1,111 @@
+code = $code;
+ $this->name = $name;
+ $this->type = $type;
+ $this->value = $value;
+ $this->startDate = $startDate;
+ $this->endDate = $endDate;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCode(): string
+ {
+ return $this->code;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+
+ /**
+ * @return string
+ */
+ public function getType(): ?string
+ {
+ return $this->type;
+ }
+
+
+ /**
+ * @return float
+ */
+ public function getValue(): ?float
+ {
+ return $this->value;
+ }
+
+ /**
+ * @return DateTime
+ */
+ public function getStartDate(): ?DateTime
+ {
+ try {
+ if(!$this->startDate) return null;
+ $dateTime = new DateTime($this->startDate);
+ return $dateTime;
+ } catch (\Exception $e) {
+ Log::error($e);
+ return null;
+ }
+ }
+
+ /**
+ * @return DateTime
+ */
+ public function getEndDate(): ?DateTime
+ {
+ try {
+ if(!$this->endDate) return null;
+ $dateTime = new DateTime($this->endDate);
+ return $dateTime;
+ } catch (\Exception $e) {
+ Log::error($e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Processors/CreateVoucherProcessor.php b/app/Classes/Modules/Vouchers/Processors/CreateVoucherProcessor.php
new file mode 100644
index 00000000..690ad59f
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Processors/CreateVoucherProcessor.php
@@ -0,0 +1,114 @@
+fetchesVoucherifyVoucher = $fetchesVoucherifyVoucher;
+ $this->createsVoucher = $createsVoucher;
+ $this->createsUserReward = $createsUserReward;
+ $this->fetchesVoucher = $fetchesVoucher;
+ $this->validatesVoucherifyVoucher = $validatesVoucherifyVoucher;
+ }
+
+
+ /**
+ * @param ?User $userParam
+ * @param string $voucherCodeInput
+ * @return array
+ * @throws \App\Classes\Exceptions\AccessForbiddenException
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ */
+ public function execute(?User $userParam, string $voucherCodeInput) {
+ try{
+ $result = null;
+ $user = Auth::user(); /** @var User $user */
+ if($user && isset($user->type) && in_array($user->type, RoleTypes::ADMIN_ROLES) && $userParam){
+ $user = User::where('id', $userParam->id)->first();
+ }
+ else{
+ $user = $userParam ? $userParam : $user;
+ }
+
+ //Voucherify - Validates Voucher
+ $ValidateVoucherifyVoucherObject = new ValidateVoucherifyVoucherObject(0, $voucherCodeInput, 0.00, $user);
+ $voucherifyVoucherValidated = $this->validatesVoucherifyVoucher->execute($ValidateVoucherifyVoucherObject);
+
+ if(isset($voucherifyVoucherValidated->reason)){
+ $result = [];
+ $result['reason'] = $voucherifyVoucherValidated->reason;
+ }
+ else if($voucherifyVoucherValidated){
+ $voucher = $this->recordVoucherInfo($user, $voucherCodeInput);
+ $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count();
+ if($voucherCount == 0){
+ $result = $this->createsUserReward->execute(null, $user, $voucher->id);
+ }
+ else{
+ $result['reason'] = 'Voucher already added';
+ }
+ }
+
+ return $result;
+ } catch (\Exception $e) {
+ Log::error($e);
+ }
+ }
+
+ private function recordVoucherInfo(User $user, string $voucherCodeInput){
+ //Voucherify - Get Voucher
+ $voucherifyVoucherFetched = $this->fetchesVoucherifyVoucher->execute($user, $voucherCodeInput);
+
+ $voucherName = $voucherifyVoucherFetched->campaign;
+ $voucherType = $voucherifyVoucherFetched->discount->type;
+ $voucherValue = isset($voucherifyVoucherFetched->discount->amount_off) ? $voucherifyVoucherFetched->discount->amount_off : $voucherifyVoucherFetched->discount->percent_off;
+ $voucherCode = $voucherifyVoucherFetched->code;
+ $voucherStartDate = $voucherifyVoucherFetched->start_date;
+ $voucherEndDate = $voucherifyVoucherFetched->expiration_date;
+
+ $voucherObject= new VoucherObject($voucherCode, isset($voucherName) ? $voucherName : "Voucherify Voucher Added Manually", $voucherType, $voucherValue, $voucherStartDate, $voucherEndDate);
+ $voucher = $this->createsVoucher->execute($voucherObject);
+ if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherCodeInput]);
+ return $voucher;
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Processors/Voucherify/BookingToVoucherifyProcessor.php b/app/Classes/Modules/Vouchers/Processors/Voucherify/BookingToVoucherifyProcessor.php
new file mode 100644
index 00000000..323b2eaf
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Processors/Voucherify/BookingToVoucherifyProcessor.php
@@ -0,0 +1,163 @@
+createsVoucher = $createsVoucher;
+ $this->fetchesVoucher = $fetchesVoucher;
+ $this->createsVoucherRedemption = $createsVoucherRedemption;
+ $this->redeemsVoucherifyVoucher = $redeemsVoucherifyVoucher;
+ $this->createsVoucherifyOrder = $createsVoucherifyOrder;
+ $this->createsVoucherEntityMapping = $createsVoucherEntityMapping;
+ $this->createsUserReward = $createsUserReward;
+ }
+
+
+ /**
+ * @param User $user
+ * @param Transaction $transaction
+ * @param int $companyId
+ * @param float $amount
+ * @param float $voucherDiscountAmount
+ * @param string $voucherCode
+ * @return void
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(User $user, Transaction $transaction, int $companyId, float $amount, float $voucherDiscountAmount, ?string $voucherCode = "")
+ {
+ try{
+ $voucherify_customer_id = "";
+ $voucherify_order_id = "";
+ if($voucherCode){
+ $redeemVoucherifyVoucherObject = new RedeemVoucherifyVoucherObject($companyId, $transaction->id, $voucherCode, $amount, $user);
+ $redeemVoucherResult = $this->redeemsVoucherifyVoucher->execute($redeemVoucherifyVoucherObject);
+ $redeemedVoucher = $redeemVoucherResult->voucher;
+ $redemptionId = $redeemVoucherResult->id;
+
+ if($redeemVoucherResult && isset($redeemVoucherResult->order)){
+ $voucherify_order_id = $redeemVoucherResult->order->id;
+ }
+
+ if($redeemVoucherResult && isset($redeemVoucherResult->customer)){
+ $voucherify_customer_id = $redeemVoucherResult->customer->id;
+ }
+
+ $voucher = $this->recordVoucherInfo($redeemedVoucher, $transaction);
+ $this->createsVoucherRedemption->execute($transaction, $voucher, $redemptionId, $voucherDiscountAmount);
+ $this->recordVoucherForUserInfo($user, $voucher);
+ }
+ else{
+ $createVoucherifyOrderObject = new CreateVoucherifyOrderObject($user, $companyId, $transaction->id, $amount, true, $transaction->type == TransactionType::TOP_UP);
+ $createVoucherufyOrderResult = $this->createsVoucherifyOrder->execute($createVoucherifyOrderObject);
+
+ if($createVoucherufyOrderResult && isset($createVoucherufyOrderResult->id)){
+ $voucherify_order_id = $createVoucherufyOrderResult->id;
+ if(isset($createVoucherufyOrderResult->customer)){
+ $voucherify_customer_id = $createVoucherufyOrderResult->customer->id;
+ }
+ }
+ }
+
+ $this->recordVoucherifyOrderInfo($voucherify_order_id, $transaction);
+ $this->recordVoucherifyCustomerInfo($voucherify_customer_id, $user);
+
+ } catch (\Exception $e) {
+ Log::error($e);
+ }
+ }
+
+ private function recordVoucherInfo(object $redeemedVoucher){
+ //Create records at 3 tables
+ $voucherValue = isset($redeemedVoucher->discount->amount_off) ? $redeemedVoucher->discount->amount_off : $redeemedVoucher->discount->percent_off;
+ $voucherType = $redeemedVoucher->discount ? $redeemedVoucher->discount->type : null;
+
+ $voucherObject= new VoucherObject($redeemedVoucher->code, isset($redeemedVoucher->metadata->name) ? $redeemedVoucher->metadata->name : "", $voucherType, $voucherValue);
+ $voucher = $this->createsVoucher->execute($voucherObject);
+ if(!$voucher) $voucher = $this->fetchesVoucher->execute(['code' => $voucherObject->getCode()]);
+
+ return $voucher;
+ }
+
+ private function recordVoucherForUserInfo(User $user, Voucher $voucher){
+ //create reward to user (user_reward)
+ $voucherCount = $user->rewards->where('voucher_id', $voucher->id)->count();
+ if($voucherCount == 0){
+ $this->createsUserReward->execute(null, $user, $voucher->id);
+ }
+ }
+
+ private function recordVoucherifyOrderInfo(string $voucherify_order_id, Transaction $transaction){
+ //Update Database - 1 table
+ if($voucherify_order_id){
+ $voucherify_entity = $transaction->voucherifyEntities()->first();
+ if(!$voucherify_entity){
+ $voucherEntityObject = new VoucherEntityObject($voucherify_order_id, VoucherifyEntityType::ORDER);
+ $this->createsVoucherEntityMapping->execute($transaction, $voucherEntityObject);
+ }
+ }
+ }
+
+ private function recordVoucherifyCustomerInfo(string $voucherify_customer_id, User $user){
+ //Update Database - 1 table
+ if($voucherify_customer_id){
+ $voucherify_entity = $user->voucherifyEntities()->first();
+ if(!$voucherify_entity){
+ $voucherEntityObject = new VoucherEntityObject($voucherify_customer_id, VoucherifyEntityType::CUSTOMER);
+ $this->createsVoucherEntityMapping->execute($user, $voucherEntityObject);
+ }
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Processors/Voucherify/NewCustomerToVoucherifyProcessor.php b/app/Classes/Modules/Vouchers/Processors/Voucherify/NewCustomerToVoucherifyProcessor.php
new file mode 100644
index 00000000..006bcd94
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Processors/Voucherify/NewCustomerToVoucherifyProcessor.php
@@ -0,0 +1,55 @@
+createsVoucherEntityMapping = $createsVoucherEntityMapping;
+ $this->createsVoucherifyCustomer = $createsVoucherifyCustomer;
+ }
+
+
+ /**
+ * @param int $companyId
+ * @param User $user
+ * @param bool $isNew
+ * @return void
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute(int $companyId, User $user, bool $isNew)
+ {
+ try
+ {
+ $createVoucherifyCustomerObject = new CreateVoucherifyCustomerObject($companyId, $user, $isNew);
+ $result = $this->createsVoucherifyCustomer->execute($createVoucherifyCustomerObject);
+
+ if($result && isset($result->id)){
+ $voucherEntityObject = new VoucherEntityObject($result->id, VoucherifyEntityType::CUSTOMER);
+ $this->createsVoucherEntityMapping->execute($createVoucherifyCustomerObject->getUser(), $voucherEntityObject);
+ }
+ } catch (\Exception $e) {
+ Log::error($e);
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Processors/Voucherify/TransactionToVoucherifyProcessor.php b/app/Classes/Modules/Vouchers/Processors/Voucherify/TransactionToVoucherifyProcessor.php
new file mode 100644
index 00000000..46dbf7a1
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Processors/Voucherify/TransactionToVoucherifyProcessor.php
@@ -0,0 +1,44 @@
+updatesVoucherifyOrder = $updatesVoucherifyOrder;
+ }
+
+
+ /**
+ * @param Transaction $transaction
+ * @param string $status
+ * @return void
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(Transaction $transaction, string $status)
+ {
+ try{
+ $voucherify_entity = $transaction->voucherifyEntities()->first();
+ if($voucherify_entity){
+ $updateVoucherifyOrderObject = new UpdateVoucherifyOrderObject($voucherify_entity->voucherify_entity_id, $status);
+ $this->updatesVoucherifyOrder->execute($updateVoucherifyOrderObject);
+ }
+ } catch (\Exception $e) {
+ Log::error($e);
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/CheckIfVoucherExists.php b/app/Classes/Modules/Vouchers/Services/CheckIfVoucherExists.php
new file mode 100644
index 00000000..9bd38dfd
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/CheckIfVoucherExists.php
@@ -0,0 +1,27 @@
+repository = $repository;
+ }
+
+ public function execute(string $code): bool {
+ return $this->repository->where('code', $code)->exists();
+ }
+
+}
diff --git a/app/Classes/Modules/Vouchers/Services/CreatesVoucher.php b/app/Classes/Modules/Vouchers/Services/CreatesVoucher.php
new file mode 100644
index 00000000..9162dda8
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/CreatesVoucher.php
@@ -0,0 +1,42 @@
+voucherExists = $voucherExists;
+ }
+
+ /**
+ * @param VoucherObject $object
+ * @return \Illuminate\Database\Eloquent\Model
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute(VoucherObject $object) {
+ if(!$this->voucherExists->execute($object->getCode()))
+ {
+ $model = new Voucher();
+ $model->code = $object->getCode();
+ $model->name = $object->getName();
+ $model->type = $object->getType();
+ $model->value = $object->getValue();
+ $model->start_date = $object->getStartDate();
+ $model->end_date = $object->getEndDate();
+ return $this->handler($model);
+ }
+ return null;
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/CreatesVoucherEntityMapping.php b/app/Classes/Modules/Vouchers/Services/CreatesVoucherEntityMapping.php
new file mode 100644
index 00000000..1a1edb28
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/CreatesVoucherEntityMapping.php
@@ -0,0 +1,24 @@
+voucherify_entity_id = $object->getId();
+ $model->voucherify_entity_type = $object->getType();
+ return $this->handler($voucherifable->voucherifyEntities(), $model);
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/CreatesVoucherRedemption.php b/app/Classes/Modules/Vouchers/Services/CreatesVoucherRedemption.php
new file mode 100644
index 00000000..c032031e
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/CreatesVoucherRedemption.php
@@ -0,0 +1,28 @@
+voucher_id = $voucher->id;
+ $model->redemption_id = $redemptionId;
+ $model->value = $value;
+
+ return $this->handler($transaction->voucherRedemption(), $model);
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/FetchesVoucher.php b/app/Classes/Modules/Vouchers/Services/FetchesVoucher.php
new file mode 100644
index 00000000..4abc873a
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/FetchesVoucher.php
@@ -0,0 +1,34 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ public function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/FetchesVoucherRedemption.php b/app/Classes/Modules/Vouchers/Services/FetchesVoucherRedemption.php
new file mode 100644
index 00000000..0efc163d
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/FetchesVoucherRedemption.php
@@ -0,0 +1,34 @@
+repository = $repository;
+ }
+
+
+ /**
+ * @return Builder
+ */
+ public function getRepository(): Builder
+ {
+ return $this->repository->newQuery();
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/RollbacksRedemption.php b/app/Classes/Modules/Vouchers/Services/RollbacksRedemption.php
new file mode 100644
index 00000000..6bc3bf23
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/RollbacksRedemption.php
@@ -0,0 +1,38 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+ /**
+ * @param string $redemptionId
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(string $redemptionId)
+ {
+ try {
+ $result = $this->voucherifyClient->redemptions->rollback($redemptionId);
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ Log::error('RollbacksRedemption error:' . $e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyCustomer.php b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyCustomer.php
new file mode 100644
index 00000000..a15fe9c3
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyCustomer.php
@@ -0,0 +1,66 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+ /**
+ * @param CreateVoucherifyCustomerObject $object
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(CreateVoucherifyCustomerObject $object)
+ {
+ try {
+
+ $customerObj = [
+ "source_id" => $object->getUser()->id,
+ "name" => $object->getUser()->name,
+ "email" => $object->getUser()->email,
+ "address" => [
+ "city" => '',
+ "country" => '',
+ "line_1" => '',
+ "line_2" => '',
+ "postal_code" => '',
+ "state" => '',
+ ],
+ ];
+
+ if ($object->getIsNew()) {
+ $customerObj['metadata']["new_customer"] = date('Y-m-d H:i:s');
+ }
+ if ($object->getCompanyId()) {
+ $customerObj['metadata']["exchange_company_id"] = $object->getCompanyId();
+ $customerObj['metadata']["exchange_user_id"] = $object->getUser()->id;
+ }
+ if ($object->getAcquisitionChannel()) {
+ $customerObj['metadata']["acquisition"] = $object->getAcquisitionChannel();
+ }
+
+ $result = $this->voucherifyClient->customers->create($customerObj);
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ // throw $e;
+ Log::error($e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyOrder.php b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyOrder.php
new file mode 100644
index 00000000..90e5d682
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyOrder.php
@@ -0,0 +1,62 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+
+ /**
+ * @param CreateVoucherifyOrderObject $obj
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(CreateVoucherifyOrderObject $obj)
+ {
+ try {
+ $orderObj = [
+ "source_id" => $obj->getTransactionId(),
+ "customer" => [
+ "source_id" => $obj->getEmployee()->id,
+ "name" => $obj->getEmployee()->name,
+ "email" => $obj->getEmployee()->email,
+ "metadata" => [
+ "exchange_company_id" => $obj->getCompanyId(),
+ "exchange_user_id" => $obj->getEmployee()->id
+ ]
+ ],
+ "amount" => $obj->getAmount() * 100, //converting it to cents
+ ];
+
+ if ($obj->getIsNoVoucher()) {
+ $orderObj['metadata']["no_voucher"] = true;
+ }
+
+ if ($obj->getIsTopUpWallet()) {
+ $orderObj['metadata']["is_wallet_top_up"] = true;
+ }
+
+ $result = $this->voucherifyClient->orders->create($orderObj);
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ Log::error($e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyVoucher.php
new file mode 100644
index 00000000..ad95946b
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/Voucherify/CreatesVoucherifyVoucher.php
@@ -0,0 +1,59 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+ /**
+ * @param User $user
+ * @param int $amount
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(User $user, int $amount)
+ {
+ $startDate = Carbon::now();
+ $expirationDate = $startDate->copy()->addMonths(12)->endOfDay();
+ try {
+ $result = $this->voucherifyClient->vouchers->create([
+ "code" => Str::random(10),
+ "type" => "DISCOUNT_VOUCHER",
+ "discount" => [
+ "type" => "AMOUNT",
+ "amount_off" => $amount * 100,
+ ],
+ "redemption" => [
+ "quantity" => 1
+ ],
+ "metadata" => [
+ "email" => $user->email
+ ],
+ "start_date" => $startDate->toIso8601String(),
+ "expiration_date" => $expirationDate->toIso8601String()
+ ]);
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ Log::error($e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/FetchesVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/FetchesVoucherifyVoucher.php
new file mode 100644
index 00000000..8c4d33b7
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/Voucherify/FetchesVoucherifyVoucher.php
@@ -0,0 +1,47 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+ /**
+ * @param User $user
+ * @param string $voucherifyVoucherCode
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(User $user, string $voucherifyVoucherCode)
+ {
+ try {
+ $result = $this->voucherifyClient->vouchers->get($voucherifyVoucherCode);
+
+ if (isset($result->metadata) && isset($result->metadata->email)) {
+ if($user->email != $result->metadata->email){
+ $result->reason = 'Invalid Code';
+ }
+ }
+
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ Log::error($e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/RedeemsVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/RedeemsVoucherifyVoucher.php
new file mode 100644
index 00000000..7c44889b
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/Voucherify/RedeemsVoucherifyVoucher.php
@@ -0,0 +1,51 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+ /**
+ * @param RedeemVoucherifyVoucherObject $redeemVoucherifyVoucherObject
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(RedeemVoucherifyVoucherObject $redeemVoucherifyVoucherObject)
+ {
+ try {
+ $result = $this->voucherifyClient->redemptions->redeem($redeemVoucherifyVoucherObject->getPromoCode(), [
+ "customer" => [
+ "source_id" => $redeemVoucherifyVoucherObject->getEmployee()->id,
+ "name" => $redeemVoucherifyVoucherObject->getEmployee()->name,
+ "email" => $redeemVoucherifyVoucherObject->getEmployee()->email,
+ "metadata" => [
+ "exchange_company_id" => $redeemVoucherifyVoucherObject->getCompanyId(),
+ "exchange_user_id" => $redeemVoucherifyVoucherObject->getEmployee()->id
+ ]
+ ],
+ "order" => [
+ "source_id" => $redeemVoucherifyVoucherObject->getTransactionId(),
+ "amount" => $redeemVoucherifyVoucherObject->getAmount() * 100 //converting it to cents
+ ]
+ ]);
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ throw $e;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/UpdatesVoucherifyOrder.php b/app/Classes/Modules/Vouchers/Services/Voucherify/UpdatesVoucherifyOrder.php
new file mode 100644
index 00000000..80aae605
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/Voucherify/UpdatesVoucherifyOrder.php
@@ -0,0 +1,43 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+
+ /**
+ * @param UpdateVoucherifyOrderObject $obj
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(UpdateVoucherifyOrderObject $obj)
+ {
+ try {
+ $result = $this->voucherifyClient->orders->update([
+ "id" => $obj->getId(),
+ "status" => $obj->getStatus(),
+ ]);
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ Log::error($e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Vouchers/Services/Voucherify/ValidatesVoucherifyVoucher.php b/app/Classes/Modules/Vouchers/Services/Voucherify/ValidatesVoucherifyVoucher.php
new file mode 100644
index 00000000..3fa322d5
--- /dev/null
+++ b/app/Classes/Modules/Vouchers/Services/Voucherify/ValidatesVoucherifyVoucher.php
@@ -0,0 +1,70 @@
+voucherifyClient = createVoucherifyClient();
+ }
+
+ /**
+ * @param ValidateVoucherifyVoucherObject $validateVoucherifyVoucherObject
+ * @return null|object
+ * @throws \Voucherify\ClientException
+ */
+ public function execute(ValidateVoucherifyVoucherObject $validateVoucherifyVoucherObject)
+ {
+ try {
+ $validateVoucherObj = [
+ "customer" => [
+ "source_id" => $validateVoucherifyVoucherObject->getUser()->id,
+ "name" => $validateVoucherifyVoucherObject->getUser()->name,
+ "email" => $validateVoucherifyVoucherObject->getUser()->email,
+ "metadata" => [
+ "exchange_company_id" => $validateVoucherifyVoucherObject->getCompanyId(),
+ "exchange_user_id" => $validateVoucherifyVoucherObject->getUser()->id
+ ]
+ ]
+ ];
+ if ($validateVoucherifyVoucherObject->getAmount()) {
+ $validateVoucherObj['order'] = [
+ "amount" => $validateVoucherifyVoucherObject->getAmount() * 100 //converting it to cents
+ ];
+ }
+
+ $result = $this->voucherifyClient->validations->validateVoucher($validateVoucherifyVoucherObject->getVoucherCode(), $validateVoucherObj);
+
+ if (isset($result->metadata) && isset($result->metadata->email)) {
+ if($validateVoucherifyVoucherObject->getUser()->email != $result->metadata->email){
+ $result->reason = 'Invalid Code';
+ }
+ }
+
+ if (isset($result->reason)) {
+ Helper::debugLogger('ValidatesVoucherifyVoucher error: '. $result->reason);
+ $result->reason = 'Invalid Code';
+ }
+
+ return $result;
+ } catch (\Voucherify\ClientException $e) {
+ // throw $e;
+ Log::error($e);
+ return null;
+ }
+ }
+}
diff --git a/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php b/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php
index 9bbe6073..86e344ec 100644
--- a/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php
+++ b/app/Classes/Modules/Wallets/ControllersLogic/TopUpWalletLogic.php
@@ -13,6 +13,7 @@ use App\Classes\Modules\Wallets\Services\GeneratesWalletCode;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
+use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
@@ -56,6 +57,8 @@ class TopUpWalletLogic extends AbstractControllerLogic
/** @var CreatesTransaction */
private $createsTransaction;
+ /** @var BookingToVoucherifyProcessor */
+ private $bookingToVoucherifyProcessor;
/**
* TopUpWalletLogic constructor.
@@ -65,8 +68,9 @@ class TopUpWalletLogic extends AbstractControllerLogic
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatesBillplzBill $createsBillplzBill
* @param CreatesTransaction $createsTransaction
+ * @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor
*/
- public function __construct(FetchesCompany $fetchesCompany, GeneratesWalletCode $generatesWalletCode, CreatesWallet $createsWallet, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesBillplzBill $createsBillplzBill, CreatesTransaction $createsTransaction)
+ public function __construct(FetchesCompany $fetchesCompany, GeneratesWalletCode $generatesWalletCode, CreatesWallet $createsWallet, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesBillplzBill $createsBillplzBill, CreatesTransaction $createsTransaction, BookingToVoucherifyProcessor $bookingToVoucherifyProcessor)
{
$this->fetchesCompany = $fetchesCompany;
$this->generatesWalletCode = $generatesWalletCode;
@@ -74,6 +78,7 @@ class TopUpWalletLogic extends AbstractControllerLogic
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createsBillplzBill = $createsBillplzBill;
$this->createsTransaction = $createsTransaction;
+ $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor;
}
/**
@@ -102,12 +107,14 @@ class TopUpWalletLogic extends AbstractControllerLogic
throw new MalformedRequestException('Top up credit value must be greater than zero.');
}
- $billPlzBill = $this->createsBillplzBill->execute($user->name, $user->email, 'This payment is credit topup for company ref. ' . $company->reference, $amount, $billNumber, $request->input('bank_code'), true);
+ $billPlzBill = $this->createsBillplzBill->execute($company->name, $user->email, 'This payment is credit topup for company ref. ' . $company->reference, $amount, $billNumber, $request->input('bank_code'), true);
$transaction_object = new TransactionObject($billNumber, TransactionType::TOP_UP, 1, $company->id, 1, PaymentMethodType::PAYMENT_GATEWAY, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, [], $billPlzBill->id);
$transaction = $this->createsTransaction->execute($wallet, $transaction_object);
+ $this->bookingToVoucherifyProcessor->execute($company->employees()->first(), $transaction, $company->id, $amount, 0);
+
return $this->resourceResponse(new WalletTransactionResource($transaction));
}
}
diff --git a/app/Classes/Modules/Wallets/Processors/CreditWalletProcessor.php b/app/Classes/Modules/Wallets/Processors/CreditWalletProcessor.php
index d86a4465..65f3b9b6 100644
--- a/app/Classes/Modules/Wallets/Processors/CreditWalletProcessor.php
+++ b/app/Classes/Modules/Wallets/Processors/CreditWalletProcessor.php
@@ -78,7 +78,7 @@ class CreditWalletProcessor
$transaction_object = new TransactionObject($billNumber, $transactionType === 2 ? TransactionType::DEBIT_NOTE : TransactionType::CREDIT_NOTE, 1, $wallet->owner->id, 1, PaymentMethodType::CASH, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], $reference);
$transaction = $this->createsTransaction->execute($wallet, $transaction_object);
-
+
$updateWalletAmount = $transactionType === 2 ? ($wallet->amount - $transaction->amount) : ($wallet->amount + $transaction->amount);
$walletObject = new WalletObject($wallet->owner->id, $wallet->currency_id, $wallet->code, $updateWalletAmount);
diff --git a/app/Classes/Modules/Wallets/Services/RecalculatesWalletBalance.php b/app/Classes/Modules/Wallets/Services/RecalculatesWalletBalance.php
new file mode 100644
index 00000000..651bee4a
--- /dev/null
+++ b/app/Classes/Modules/Wallets/Services/RecalculatesWalletBalance.php
@@ -0,0 +1,48 @@
+updatesWalletBalance = $updatesWalletBalance;
+ }
+ /**
+ * @param Wallet $model
+ * @param $amount
+ * @return \Illuminate\Database\Eloquent\Model
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ */
+ public function execute(Wallet $wallet)
+ {
+
+ $i = 0;
+ $topups = 0;
+ $credit = 0;
+ $payments = 0;
+ $debit = 0;
+
+ foreach ($wallet->transactions as $transaction) {
+ if (!in_array((int) $transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) continue;
+ if ((int) $transaction->type === TransactionType::TOP_UP) {
+ $topups += (float) $transaction->amount;
+ }
+ if ((int) $transaction->type === TransactionType::CREDIT_NOTE) $credit += (float) $transaction->amount;
+ if ((int) $transaction->type === TransactionType::PAYMENT) $payments += (float) $transaction->amount;
+ if ((int) $transaction->type === TransactionType::DEBIT_NOTE) $debit += (float) $transaction->amount;
+ }
+ $auditBalance = ($topups + $credit) - ($payments + $debit);
+
+ return $auditBalance;
+ }
+}
diff --git a/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php b/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php
index a724cf24..4812660a 100644
--- a/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php
+++ b/app/Classes/Modules/Wallets/Services/UpdatesWalletBalance.php
@@ -3,10 +3,9 @@
namespace App\Classes\Modules\Wallets\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
-use App\Classes\General\Eloquent\AbstractUpdateRelationshipRecord;
-use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject;
+use App\Classes\ValueObjects\Constants\ApprovalStatus;
+use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Wallet;
-use App\Models\Company;
class UpdatesWalletBalance extends AbstractUpdateRecord
{
@@ -16,10 +15,24 @@ class UpdatesWalletBalance extends AbstractUpdateRecord
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
- public function execute(Wallet $model, $amount) {
+ public function execute(Wallet $model, $amount)
+ {
+ $i = 0;
+ $topups = 0;
+ $credit = 0;
+ $payments = 0;
+ $debit = 0;
- $model->amount = $model->amount + $amount;
+ foreach ($model->transactions as $transaction) {
+ if (!in_array((int) $transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) continue;
+ if ((int) $transaction->type === TransactionType::TOP_UP) $topups += (float) $transaction->amount;
+ if ((int) $transaction->type === TransactionType::CREDIT_NOTE) $credit += (float) $transaction->amount;
+ if ((int) $transaction->type === TransactionType::PAYMENT) $payments += (float) $transaction->amount;
+ if ((int) $transaction->type === TransactionType::DEBIT_NOTE) $debit += (float) $transaction->amount;
+ }
+ $auditBalance = ($topups + $credit) - ($payments + $debit);
+
+ $model->amount = $auditBalance;
return $this->handler($model);
-
}
}
diff --git a/app/Classes/Notifications/PaymentProofUploadedEmail.php b/app/Classes/Notifications/PaymentProofUploadedEmail.php
new file mode 100644
index 00000000..1fdc8e14
--- /dev/null
+++ b/app/Classes/Notifications/PaymentProofUploadedEmail.php
@@ -0,0 +1,54 @@
+user = $user;
+ $this->booking = $booking;
+ $this->file = $file;
+ }
+
+
+ public function toMail()
+ {
+ $attachedFile = null;
+ $file_info = $this->file->getFileAttribute($this->file)->file->file_info;
+ foreach ($file_info as $fileCount => $fileVal) {
+ if (isset($fileVal->original)) {
+ $file_path = $fileVal->original->file;
+ $attachedFile = storage_path('app/documents/' . $file_path);
+ }
+ }
+
+ return (new MailMessage)
+ ->subject('Transfer Completed (REF: ' . $this->booking->marking . ')')
+ // ->attach($attachedFile) // todo-new: add attachement
+ ->view('emails.accounts.payment_proof_email', ['user' => $this->user, 'booking' => $this->booking]);
+ }
+
+
+}
diff --git a/app/Classes/ValueObjects/Constants/ApprovalStatus.php b/app/Classes/ValueObjects/Constants/ApprovalStatus.php
index 154db2ca..ba7660d4 100644
--- a/app/Classes/ValueObjects/Constants/ApprovalStatus.php
+++ b/app/Classes/ValueObjects/Constants/ApprovalStatus.php
@@ -19,4 +19,15 @@ final class ApprovalStatus {
public const EXPIRED = 6;
public const REFUNDED = 7;
+
+ public const APPROVAL_STATUS_ID = [
+ self::PENDING_SUBMISSION => "Pending Submission",
+ self::PENDING_VERIFICATION => "Pending Verification",
+ self::APPROVED => "Approved",
+ self::COMPLETED => "Completed",
+ self::REJECTED => "Rejected",
+ self::SUSPENDED => "Suspended",
+ self::EXPIRED => "Expired",
+ self::REFUNDED => "Refunded",
+ ];
}
diff --git a/app/Classes/ValueObjects/Constants/BusinessType.php b/app/Classes/ValueObjects/Constants/BusinessType.php
index 831568e9..69ea3202 100644
--- a/app/Classes/ValueObjects/Constants/BusinessType.php
+++ b/app/Classes/ValueObjects/Constants/BusinessType.php
@@ -12,4 +12,11 @@ final class BusinessType {
public const TRANSFER_AGENT = 4;
+ public const BUSINESS_TYPE_LIST = [
+ self::FREIGHT_FORWARDER => 'Freight Forwarder',
+ self::IMPORTER => 'Importer',
+ self::CURRENCY_VENDOR => 'Currency Vendor',
+ self::TRANSFER_AGENT => 'Transfer Agent',
+ ];
+
}
diff --git a/app/Classes/ValueObjects/Constants/CashBack.php b/app/Classes/ValueObjects/Constants/CashBack.php
new file mode 100644
index 00000000..0fdc3df9
--- /dev/null
+++ b/app/Classes/ValueObjects/Constants/CashBack.php
@@ -0,0 +1,55 @@
+ [
+ 'min_value' => 0,
+ 'max_value' => 2000,
+ 'weight' => [
+ '0' => 80,
+ '1' => 18,
+ '2' => 2,
+ ],
+ 'percent' => [
+ '0' => 0.002,
+ '1' => 0.005,
+ '2' => 0.02,
+ ]
+ ],
+ '1' => [
+ 'min_value' => 2001,
+ 'max_value' => 10000,
+ 'weight' => [
+ '0' => 85,
+ '1' => 10,
+ '2' => 5,
+ ],
+ 'percent' => [
+ '0' => 0.002,
+ '1' => 0.005,
+ '2' => 0.02,
+ ]
+ ],
+ '2' => [
+ 'min_value' => 10001,
+ 'max_value' => 100000,
+ 'weight' => [
+ '0' => 70,
+ '1' => 20,
+ '2' => 10,
+ ],
+ 'percent' => [
+ '0' => 0.002,
+ '1' => 0.005,
+ '2' => 0.02,
+ ]
+ ],
+ ];
+}
diff --git a/app/Classes/ValueObjects/Constants/CompanyType.php b/app/Classes/ValueObjects/Constants/CompanyType.php
index 61918925..f16e51c5 100644
--- a/app/Classes/ValueObjects/Constants/CompanyType.php
+++ b/app/Classes/ValueObjects/Constants/CompanyType.php
@@ -8,4 +8,14 @@ final class CompanyType {
public const COMPANY_BUSINESS = 1;
+ public const COMPANY_TYPE_ID = [
+ self::PERSONAL_BUSINESS => 'PERSONAL_BUSINESS',
+ self::COMPANY_BUSINESS => 'COMPANY_BUSINESS',
+ ];
+
+ public const COMPANY_TYPE_LIST = [
+ self::PERSONAL_BUSINESS => 'Personal Business',
+ self::COMPANY_BUSINESS => 'Company Business',
+ ];
+
}
diff --git a/app/Classes/ValueObjects/Constants/DocumentType.php b/app/Classes/ValueObjects/Constants/DocumentType.php
index 04edf25c..23f9a94a 100644
--- a/app/Classes/ValueObjects/Constants/DocumentType.php
+++ b/app/Classes/ValueObjects/Constants/DocumentType.php
@@ -17,9 +17,12 @@ final class DocumentType {
public const WALLET_TOP_UP_PAYMENT_PROOF = 'WALLET_TOP_UP_PAYMENT_PROOF';
public const WALLET_REFUND_PAYMENT_PROOF = 'WALLET_REFUND_PAYMENT_PROOF';
+ public const ECOMMERCE_PURCHASE_ORDER = 'ECOMMERCE_PURCHASE_ORDER';
+
public const PROFORMA_INVOICE = 'PROFORMA_INVOICE';
public const PURCHASE_ORDER = 'PURCHASE_ORDER';
public const DELIVER_ORDER = 'DELIVER_ORDER';
public const INVOICE = 'INVOICE';
public const SUPPLIER_DELIVER_ORDER = 'SUPPLIER_DELIVER_ORDER';
+ public const BULK_PURCHASE_ORDER = 'BULK_PURCHASE_ORDER';
}
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/MilestoneCreationOptions.php b/app/Classes/ValueObjects/Constants/MilestoneCreationOptions.php
new file mode 100644
index 00000000..4631b25b
--- /dev/null
+++ b/app/Classes/ValueObjects/Constants/MilestoneCreationOptions.php
@@ -0,0 +1,19 @@
+ 'MILESTONE 1', 'id' => Milestones::MILESTONE_1],
+ ['text' => 'MILESTONE 2', 'id' => Milestones::MILESTONE_2],
+ ['text' => 'MILESTONE 3', 'id' => Milestones::MILESTONE_3],
+ // ['text' => 'MILESTONE 4', 'id' => Milestones::MILESTONE_4],
+ // ['text' => 'MILESTONE 5', 'id' => Milestones::MILESTONE_5],
+ // ['text' => 'MILESTONE 6', 'id' => Milestones::MILESTONE_6],
+ // ['text' => 'MILESTONE 7', 'id' => Milestones::MILESTONE_7],
+ // ['text' => 'MILESTONE 8', 'id' => Milestones::MILESTONE_8],
+ // ['text' => 'MILESTONE 9', 'id' => Milestones::MILESTONE_9],
+ // ['text' => 'MILESTONE 10', 'id' => Milestones::MILESTONE_10],
+ ];
+}
diff --git a/app/Classes/ValueObjects/Constants/Milestones.php b/app/Classes/ValueObjects/Constants/Milestones.php
new file mode 100644
index 00000000..19961d68
--- /dev/null
+++ b/app/Classes/ValueObjects/Constants/Milestones.php
@@ -0,0 +1,17 @@
+ 'Customer Paid',
+ 'description' => '',
+ 'milestone' => 'MILESTONE 1 - Customer Paid',
+ 'reference' => '',
+ 'on_task_completion' => '',
+ 'department' => '',
+ 'status' => PerfexCRMTaskStatus::COMPLETED,
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+
+ public const TASK_1_DAY_TRANSFER_1 = [
+ 'name' => 'Map Bank Transaction Record',
+ 'description' => '○ Purpose: To map a transaction to bank transaction in the bank statement
+ ○ Amount: RM {amount}
+ ○ Link to order page: {link_transfer} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1_DAY_TRANSFER_1',
+ 'on_task_completion' => 'TASK_1_DAY_TRANSFER_2',
+ 'department' => 'Accounts',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1_DAY_TRANSFER_2 = [
+ 'name' => 'Approve Payment',
+ 'description' => '○ Purpose: To verify and approve the customer\'s payment on exchange
+ ○ Initial Status: Not Started
+ ○ Deadline:If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step:
+ i. Change the status of the Issue Exchange Autocount Invoice operation to "In Progress"
+ ii. Change the status of the Order Placed in White Form operation to "In Progress" upon successful completion.
+ ○ Additional details: When the payment method is FPX or Wallet this task is performed automatically by the system.
+ ○ Dependencies: Map Transaction operation must be completed before this operation can begin.
+ ○ 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_5',
+ 'department' => 'Accounts',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+ public const TASK_1_DAY_TRANSFER_3 = [
+ 'name' => 'Issue Exchange Autocount Invoince',
+ 'description' => '○ Purpose: To issue an invoice for the customer\'s payment in accounting software.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Knockoff Invoice operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1_DAY_TRANSFER_3',
+ 'on_task_completion' => 'TASK_1_DAY_TRANSFER_3_1',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1_DAY_TRANSFER_3_1 = [
+ 'name' => 'Issue Exchange Autocount OR',
+ 'description' => '○ {link_autocount_or} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1_DAY_TRANSFER_3_1',
+ 'on_task_completion' => 'TASK_1_DAY_TRANSFER_4',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1_DAY_TRANSFER_4 = [
+ 'name' => 'Knockoff Invoice',
+ 'description' => '○ Purpose: The purpose of this operation is to issue the official receipt and knockoff with invoice for the customer\'s payment.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day.
+ ○ Responsible Department: Accounts
+ ○ Next Step: None
+ ○ Additional Details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Issue Exchange Autocount Invoice operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s payment is applied to the accounting software invoice and invoice is marked as paid. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1_DAY_TRANSFER_4',
+ 'on_task_completion' => 'TASK_1_DAY_TRANSFER_5',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1_DAY_TRANSFER_5 = [
+ 'name' => 'Order Placed in White Form',
+ 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ Amount: RM {amount}
+ ○ Currency: {currency}
+ ○ Service Type: {service_type}
+ ○ Bank-in details:
+ ○ Reference: {bank_details.reference}
+ ○ Account Holder Name: {bank_details.holder_name}
+ ○ Account No.: {bank_details.account_no}
+ ○ Bank Name: {bank_details.bank_name}
+ ○ Bank Branch: {bank_details.bank_branch} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1_DAY_TRANSFER_5',
+ 'on_task_completion' => 'TASK_1_DAY_TRANSFER_6',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+ public const TASK_1_DAY_TRANSFER_6 = [
+ 'name' => 'Upload China Bank Slip',
+ 'description' => '○ Purpose: To confirm that the customer order has been transferred to the customer\'s supplier.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Operations
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Order Placed in White Form operation must be completed before this operation can begin.
+ ○ Outcomes: The customer can download the payment transfer proof to send to their supplier. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1_DAY_TRANSFER_6',
+ 'on_task_completion' => '',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+
+ public const TASK_3_DAY_TRANSFER_1 = [
+ 'name' => 'Map Bank Transaction Record',
+ 'description' => '○ Purpose: To map a transaction to bank transaction in the bank statement
+ ○ Initial Status: In Progress
+ ○ Deadline: If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Approve payment status to "In Progress" upon successful completion of the operation.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ Outcomes: Bank transaction is mapped successfully, allowing the next steps in the process to be initiated. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_3_DAY_TRANSFER_1',
+ 'on_task_completion' => 'TASK_3_DAY_TRANSFER_2',
+ 'department' => 'Accounts',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_3_DAY_TRANSFER_2 = [
+ 'name' => 'Approve Payment',
+ 'description' => '○ Purpose: To verify and approve the customer\'s payment on exchange
+ ○ Amount: RM {amount}
+ ○ Link to order page: {link_transfer} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_3_DAY_TRANSFER_2',
+ 'on_task_completion' => 'TASK_3_DAY_TRANSFER_5',
+ 'department' => 'Accounts',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+ public const TASK_3_DAY_TRANSFER_3 = [
+ 'name' => 'Issue Exchange Autocount Invoince',
+ 'description' => '○ Purpose: To issue an invoice for the customer\'s payment in accounting software.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Knockoff Invoice operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_3_DAY_TRANSFER_3',
+ 'on_task_completion' => 'TASK_3_DAY_TRANSFER_3_1',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_3_DAY_TRANSFER_3_1 = [
+ 'name' => 'Issue Exchange Autocount OR',
+ 'description' => '○ {link_autocount_or} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_3_DAY_TRANSFER_3_1',
+ 'on_task_completion' => 'TASK_3_DAY_TRANSFER_4',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_3_DAY_TRANSFER_4 = [
+ 'name' => 'Knockoff Invoice',
+ 'description' => '○ Purpose: The purpose of this operation is to issue the official receipt and knockoff with invoice for the customer\'s payment.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day.
+ ○ Responsible Department: Accounts
+ ○ Next Step: None
+ ○ Additional Details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Issue Exchange Autocount Invoice operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s payment is applied to the accounting software invoice and invoice is marked as paid. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_3_DAY_TRANSFER_4',
+ 'on_task_completion' => 'TASK_3_DAY_TRANSFER_5',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_3_DAY_TRANSFER_5 = [
+ 'name' => 'Order Placed in White Form',
+ 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ Amount: RM {amount}
+ ○ Currency: {currency}
+ ○ Service Type: {service_type}
+ ○ Bank-in details:
+ ○ Reference: {bank_details.reference}
+ ○ Account Holder Name: {bank_details.holder_name}
+ ○ Account No.: {bank_details.account_no}
+ ○ Bank Name: {bank_details.bank_name}
+ ○ Bank Branch: {bank_details.bank_branch} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_3_DAY_TRANSFER_5',
+ 'on_task_completion' => 'TASK_3_DAY_TRANSFER_6',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+ public const TASK_3_DAY_TRANSFER_6 = [
+ 'name' => 'Upload China Bank Slip',
+ 'description' => '○ Purpose: To confirm that the customer order has been transferred to the customer\'s supplier.
+ ○ Initial Status: Not Started
+ ○ Deadline: After 3 days
+ ○ Responsible department: Operations
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Order Placed in White Form operation must be completed before this operation can begin.
+ ○ Outcomes: The customer can download the payment transfer proof to send to their supplier. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_3_DAY_TRANSFER_6',
+ 'on_task_completion' => '',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+
+
+ public const TASK_1688_PAYMENT_1 = [
+ 'name' => 'Map Bank Transaction Record',
+ 'description' => '○ Purpose: To map a transaction to bank transaction in the bank statement
+ ○ Initial Status: In Progress
+ ○ Deadline: If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Approve payment status to "In Progress" upon successful completion of the operation.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ Outcomes: Bank transaction is mapped successfully, allowing the next
+ steps in the process to be initiated. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_1',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_2',
+ 'department' => 'Accounts',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_2 = [
+ 'name' => 'Approve Payment',
+ 'description' => '○ Purpose: To verify and approve the customer\'s payment on exchange
+ ○ Initial Status: Not Started
+ ○ Deadline:If payment is created before 4:30 pm, it must be made on the same day. If payment is created after 4:30 pm, it must be made the next day
+ ○ Responsible department: Accounts
+ ○ Next step:
+ i. Change the status of the Issue Exchange Autocount Invoice operation to "In Progress"
+ ii. Change the status of the Order Placed in White Form operation to "In Progress" upon successful completion.
+ ○ Additional details: When the payment method is FPX or Wallet this task is performed automatically by the system.
+ ○ Dependencies: Map Transaction operation must be completed before this operation can begin.
+ ○ 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_5',
+ 'department' => 'Accounts',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_3 = [
+ 'name' => 'Issue Exchange Autocount Invoice',
+ 'description' => '○ Purpose: To issue an invoice for the customer\'s payment in accounting software.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day
+ ○ Responsible department: Accounts
+ ○ Next step: Change the status of the Knockoff Invoice operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_3',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_3_1',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_3_1 = [
+ 'name' => 'Issue Exchange Autocount OR',
+ 'description' => '○ Purpose:
+ ○ Outcomes: An invoice will be issued in accounting software for the customer\'s payment. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_3_1',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_4',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_4 = [
+ 'name' => 'Knockoff Invoice',
+ 'description' => '○ Purpose: The purpose of this operation is to issue the official receipt and knockoff with invoice for the customer\'s payment.
+ ○ Initial Status: Not Started
+ ○ Deadline: Next day.
+ ○ Responsible Department: Accounts
+ ○ Next Step: None
+ ○ Additional Details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Issue Exchange Autocount Invoice operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s payment is applied to the accounting software invoice and invoice is marked as paid. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_4',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_5',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_5 = [
+ 'name' => 'Order Placed in White Form',
+ 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same Day
+ ○ Responsible department: Operations
+ ○ Next step: Change the status of Send White Form to Operation
+ Department operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Payment operation must be completed before this operation can begin.
+ ○ 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_7',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_6 = [
+ 'name' => 'Send White Form to Operation Department',
+ 'description' => '○ Purpose: To give confirmation to the operation department to process the order.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same day
+ ○ Responsible department: Operations
+ ○ Next step: Change the status of Upload China Bank Slip operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Order Placed in White Form operation must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s order is confirmed and placed in a white form. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_6',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_7',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_7 = [
+ 'name' => 'Authorize Customer\'s 1688 Account',
+ 'description' => '○ Purpose: To authorize the alipay account to make payment to the customer\'s 1688 account.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same day
+ ○ Responsible department: Operations
+ ○ Next step: Change the status of Make Payment for Customer 1688
+ Order operation to "In Progress" upon successful completion.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Send White Form to Operation Department operation
+ must be completed before this operation can begin.
+ ○ Outcomes: The customer\'s 1688 account is authorized to use the alipay account for making payments. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_7',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_8',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 1,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+ public const TASK_1688_PAYMENT_8 = [
+ 'name' => 'Make Payment for Customer 1688 Order',
+ 'description' => '○ Purpose: To confirm that the order has been placed with the supplier.
+ ○ 1688 Username: {bank_details.1688_username}
+ ○ 1688 Password: {bank_details.1688_password}
+ ○ Branch Code: {bank_details.payment_pin}
+ ○ Amount to Transfer: RM {amount} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_8',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_9',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::HIGH,
+ 'duedate' => 1,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+ public const TASK_1688_PAYMENT_9 = [
+ 'name' => 'Upload China Bank Slip',
+ 'description' => '○ Purpose: To confirm that the customer order has been transferred to the customer\'s supplier.
+ ○ Link to order page: {link_transfer} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_9',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_10',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => true
+ ];
+ public const TASK_1688_PAYMENT_10 = [
+ 'name' => 'Upload 1688 Purchase Order PDF',
+ 'description' => '○ Purpose: To store a copy of the original purchase order document for bookkeeping.
+ ○ Link to order page: {link_transfer} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_10',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_11',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::MEDIUM,
+ 'duedate' => 1,
+ 'is_allow_multiple' => true,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_11 = [
+ 'name' => 'Fill Up Purchase Order',
+ 'description' => '○ Purpose: To store the purchase order details to generate the invoice.
+ ○ Link to order page: {link_transfer} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_11',
+ 'on_task_completion' => 'TASK_1688_PAYMENT_12',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::LOW,
+ 'duedate' => 7,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_12 = [
+ 'name' => 'Approve Purchase Order',
+ 'description' => '○ Purpose: To check that the customer submitted a purchase order that complies with our company’s guidelines.
+ ○ link: {link_transfer} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_12',
+ 'on_task_completion' => '',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::LOW,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+ public const TASK_1688_PAYMENT_13 = [
+ 'name' => 'Complete Order bookkeeping',
+ 'description' => '○ Purpose: To complete the bookkeeping for the booking.
+ ○ Initial Status: Not Started
+ ○ Deadline: Same day
+ ○ Responsible department: Operations
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: Approve Purchase Order and Upload China Bank Slip operation must be completed before this operation can begin.
+ ○ Outcomes: The order is placed with a supplier and the customer’s order is confirmed. ',
+ 'milestone' => '',
+ 'reference' => 'TASK_1688_PAYMENT_13',
+ 'on_task_completion' => '',
+ 'department' => 'Operations',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+
+
+
+ public const TASK_PURCHASE_ORDER_1 = [
+ 'name' => 'Approve Purchase Order',
+ 'description' => '○ Purpose: To check that the customer submitted a purchase order that complies with our company’s guidelines.
+ ○ Initial Status: In Progress
+ ○ Deadline: Next day
+ ○ Responsible department: Operation
+ ○ Next step: Change the status of the Approve payment status to "In Progress" upon successful completion of the operation.
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ 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' => '',
+ 'department' => 'Operations',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::LOW,
+ 'duedate' => 1,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+
+ public const TASK_PURCHASE_ORDER_2 = [
+ 'name' => 'Complete Order bookkeeping',
+ 'description' => '○ Purpose: To complete the bookkeeping for the booking.
+ ○ Link to order page: {link_transfer} ',
+ 'milestone' => '',
+ 'reference' => 'TASK_PURCHASE_ORDER_2',
+ 'on_task_completion' => '',
+ 'department' => 'Accounts',
+ 'status' => '',
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+
+
+ public const TASK_IDENTIFICATION_1 = [
+ 'name' => 'Approve Identification Verification (Exchange)',
+ 'description' => '○ Purpose: To ensure the customer submit information matches the information in their identification document.
+ ○ Initial Status: In Progress
+ ○ Deadline: Same Day
+ ○ Responsible department: Operation
+ ○ Next step: None
+ ○ Additional details: ** Any specific requirements or notes for the operation.**
+ ○ Dependencies: None
+ ○ Outcomes: Customer account identification is verified ',
+ 'milestone' => '',
+ 'reference' => 'TASK_IDENTIFICATION_1',
+ 'on_task_completion' => '',
+ 'department' => 'Operations',
+ 'status' => PerfexCRMTaskStatus::IN_PROGRESS,
+ 'priority' => PerfexCRMTaskPriority::DEFAULT,
+ 'duedate' => 0,
+ 'is_allow_multiple' => false,
+ 'is_on_task_completion_update' => false
+ ];
+}
diff --git a/app/Classes/ValueObjects/Constants/RewardCreationOptions.php b/app/Classes/ValueObjects/Constants/RewardCreationOptions.php
new file mode 100644
index 00000000..c67dde49
--- /dev/null
+++ b/app/Classes/ValueObjects/Constants/RewardCreationOptions.php
@@ -0,0 +1,17 @@
+ 'No', 'id' => 0],
+ ['text' => 'Yes', 'id' => 1],
+ ];
+
+ const OPTIONS_TYPE = [
+ ['text' => 'User Specific', 'id' => RewardType::REWARD_INDIVIDUAL],
+ ['text' => 'Amount', 'id' => RewardType::REWARD_AMOUNT],
+ ['text' => 'Code', 'id' => RewardType::REWARD_CODE],
+ ];
+}
diff --git a/app/Classes/ValueObjects/Constants/RewardType.php b/app/Classes/ValueObjects/Constants/RewardType.php
new file mode 100644
index 00000000..1add2ecd
--- /dev/null
+++ b/app/Classes/ValueObjects/Constants/RewardType.php
@@ -0,0 +1,10 @@
+ "Shadow Admin",
+ self::SUPER_ADMIN => "Super Admin",
+ self::ADMIN => "Admin",
+ self::USER => "User",
+ ];
+
}
\ No newline at end of file
diff --git a/app/Classes/ValueObjects/Constants/SegmentConstants.php b/app/Classes/ValueObjects/Constants/SegmentConstants.php
index dd6b093a..56f796cf 100644
--- a/app/Classes/ValueObjects/Constants/SegmentConstants.php
+++ b/app/Classes/ValueObjects/Constants/SegmentConstants.php
@@ -10,6 +10,8 @@ class SegmentConstants
public const CUSTOM_SEGMENT = 2;
+ public const LABEL_SEGMENT = 3;
+
public const SYSTEM_PRIMARY_CURRENCY = 'SYSTEM_PRIMARY_CURRENCY';
public const SUPPLIER_CURRENCIES = 'SUPPLIER_CURRENCIES';
diff --git a/app/Classes/ValueObjects/Constants/ShippingTransactionType.php b/app/Classes/ValueObjects/Constants/ShippingTransactionType.php
new file mode 100644
index 00000000..0668e8ca
--- /dev/null
+++ b/app/Classes/ValueObjects/Constants/ShippingTransactionType.php
@@ -0,0 +1,39 @@
+ self::EXCHANGE,
+ 'shipping_portal' => self::SHIPPING_PORTAL,
+ 'izyim' => self::SHIPPING_PORTAL,
+ ];
+}
diff --git a/app/Classes/ValueObjects/Constants/TransactionType.php b/app/Classes/ValueObjects/Constants/TransactionType.php
index 4814ac04..026f25ba 100644
--- a/app/Classes/ValueObjects/Constants/TransactionType.php
+++ b/app/Classes/ValueObjects/Constants/TransactionType.php
@@ -29,4 +29,6 @@ final class TransactionType {
public const WITHDRAW = 10;
public const TRANSFER_FEE = 12;
+
+ public const CASH_BACK = 13;
}
diff --git a/app/Classes/ValueObjects/Constants/VoucherifyEntityType.php b/app/Classes/ValueObjects/Constants/VoucherifyEntityType.php
new file mode 100644
index 00000000..e9e0a061
--- /dev/null
+++ b/app/Classes/ValueObjects/Constants/VoucherifyEntityType.php
@@ -0,0 +1,10 @@
+removesCompanyFromSegment = $removesCompanyFromSegment;
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return int
+ */
+ public function handle()
+ {
+ $wallets = Wallet::all();
+
+ $i = 0;
+ foreach ($wallets as $wallet) {
+ $topups = 0;
+ $credit = 0;
+ $payments = 0;
+ $debit = 0;
+
+ foreach ($wallet->transactions as $transaction) {
+ if (!in_array((int) $transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) continue;
+ if ((int) $transaction->type === TransactionType::TOP_UP) {
+ $topups += (float) $transaction->amount;
+ }
+ if ((int) $transaction->type === TransactionType::CREDIT_NOTE) $credit += (float) $transaction->amount;
+ if ((int) $transaction->type === TransactionType::PAYMENT) $payments += (float) $transaction->amount;
+ if ((int) $transaction->type === TransactionType::DEBIT_NOTE) $debit += (float) $transaction->amount;
+ }
+ $auditBalance = ($topups + $credit) - ($payments + $debit);
+ $diffenrence = round((float) $wallet->amount - (($topups + $credit) - ($payments + $debit)), 2);
+ if ((($diffenrence == 0) || ($diffenrence == -0)) and $wallet->amount > -0.01) continue;
+
+ $i++;
+
+ $this->info($i . ". Marking: " . $wallet->owner->reference . "(" . $wallet->id . ")" . PHP_EOL . "Current Balance: " . $wallet->amount . PHP_EOL . "Audit Balance: " . ($auditBalance) . PHP_EOL . "Difference: " . $diffenrence . PHP_EOL);
+
+ Wallet::where('id', $wallet->id)->update(['amount' => $auditBalance]);
+ }
+ }
+}
diff --git a/app/Console/Commands/DeleteOrderCommand.php b/app/Console/Commands/DeleteOrderCommand.php
new file mode 100644
index 00000000..9bcefe47
--- /dev/null
+++ b/app/Console/Commands/DeleteOrderCommand.php
@@ -0,0 +1,86 @@
+argument('bookings_reference');
+ // $bookings_reference = '28546,38599,44487,71086,70133,58580,42831,96028,41188,33894,95877,86732,31894,50962,44215,92894,40968,30303,89762,74693,45271,27169';
+ $bookings_reference = explode(',', $bookings_reference);
+
+ $start = new Carbon();
+ $this->logOutput('Process started');
+
+ foreach ($bookings_reference as $reference) {
+ $booking = Booking::where('marking', $reference)->first();
+
+ if (!$booking) {
+ $this->logOutput('Booking not found: ' . $reference);
+ } else {
+ $payment = $booking->transactions()
+ ->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
+ ->first();
+
+ $payment->status = ApprovalStatus::EXPIRED;
+ $payment->save();
+
+ $this->logOutput('Booking ' . $reference . ' Payment deleted: ' . $payment->id);
+ }
+ }
+
+ $end = new Carbon();
+ $elapsedTime = $start->diff($end)->format('%H:%I:%S');
+
+ $this->logOutput('Process ended. ElapsedTime: ' . $elapsedTime);
+ }
+
+ public function logOutput($text)
+ {
+ if (is_array($text)) {
+ $text = implode(', ', $text);
+ }
+
+ $this->info(Carbon::now() . ' : ' . $text);
+
+ $filePath = storage_path('logs/delete-orders.log');
+ $textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' ' . $text . PHP_EOL;
+ file_put_contents($filePath, $textToAppend, FILE_APPEND);
+ }
+}
diff --git a/app/Console/Commands/RemoveSeasonalSegmentCompany.php b/app/Console/Commands/RemoveSeasonalSegmentCompany.php
new file mode 100644
index 00000000..be41d05d
--- /dev/null
+++ b/app/Console/Commands/RemoveSeasonalSegmentCompany.php
@@ -0,0 +1,57 @@
+removesCompanyFromSegment = $removesCompanyFromSegment;
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return int
+ */
+ public function handle()
+ {
+ $seasonalSegment = SeasonalSegment::where('ending_on', '<=', Carbon::today())->get();
+
+ if (count($seasonalSegment)){
+ foreach ($seasonalSegment as $seasonalSegmentCompany) {
+ $this->removesCompanyFromSegment->execute($seasonalSegmentCompany->company, $seasonalSegmentCompany->segment);
+ $seasonalSegmentCompany->delete();
+ $this->info(Carbon::now() . ' : Deleted id: ' . $seasonalSegmentCompany->id);
+ }
+ }
+ }
+}
diff --git a/app/Console/Commands/debugBillplzFailedPayment.php b/app/Console/Commands/debugBillplzFailedPayment.php
new file mode 100644
index 00000000..ee1cf4b0
--- /dev/null
+++ b/app/Console/Commands/debugBillplzFailedPayment.php
@@ -0,0 +1,75 @@
+where('payment_method', PaymentMethodType::PAYMENT_GATEWAY)->whereNotIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED])->get();
+
+ $i = 0;
+ $totalAmount = 0;
+ foreach ($transactions as $transaction){
+ $response = Http::withBasicAuth(config('billplz.api_key').':', '')->get(config('billplz.base_url').'/api/v3/bills/'.$transaction->payment_reference);
+
+ // dd($response);
+
+ if($response->successful()){
+ $data = $response->json();
+ if($data['paid']){
+ if (!in_array($transaction->status, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])) {
+ $this->returnLog('Paid transaction', $transaction);
+ }
+ }
+ // else {
+ // $totalAmount += $transaction->amount;
+ // $this->returnLog('Unpaid Transaction', $transaction);
+ // }
+ }else{
+ $this->returnLog('billplz error', $transaction);
+ }
+ }
+ }
+
+ public function returnLog($text, $transaction) {
+ $approvalStatusArray = ApprovalStatus::APPROVAL_STATUS_ID;
+ $this->info($text . ' - id: '. $transaction->id . '. Booking Marking: '. $transaction->owner->marking . ' - Date: '.$transaction->created_at->format('d-m-Y').' - Amount: '. $transaction->amount . '. Current Status: ' . $approvalStatusArray[$transaction->status]);
+ }
+}
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index 8edaaa22..e549a23a 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -34,6 +34,10 @@ class Kernel extends ConsoleKernel
// $schedule->command('inspire')->hourly();
$schedule->command('mail:EmailDoToVTCommand')->dailyAt('10:00')->withoutOverlapping();
+ $schedule->command('seasonalSegmantCompany:remove')
+ ->dailyAt('01:00')
+ ->appendOutputTo(storage_path().'/logs/soft-delete-seasonal-segmant-company.log')
+ ->withoutOverlapping();
}
/**
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).' ';
+ $count = 0;
+
+ foreach ($transactions as $row) {
+ $isExist = StatementTransactionOwner::where('statement_transaction_id', $row->id)->first();
+
+ if ($isExist) {
+ continue;
+ }
+
+ $count++;
+ $credit = 0.00;
+ $debit = 0.00;
+
+ // dd(json_encode($row['posting_date']));
+ $date = new DateTime($row['posting_date']);
+ $description = $row['transaction_description_2'];
+
+ if($row['amount'] < 0){
+ $debit = (float) $row['amount'];
+ }
+ else{
+ $credit = (float) $row['amount'];
+ }
+
+ $creditTransactions = [];
+ $debitTransactions = [];
+
+ $system = '';
+ $systemReference = null;
+ $systemAmount = null;
+
+ if($credit){
+ $creditTransactions = $this->getTransactions($date, $credit, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
+ foreach ($creditTransactions as $transaction) {
+ $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no;
+ $systemAmount[] = $transaction->amount;
+ $system[] = 'EXCHANGE';
+ }
+
+ $creditTransactions = $this->getTransactions($date, $credit, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
+ foreach ($creditTransactions as $transaction) {
+ $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no;
+ $systemAmount[] = $transaction->amount;
+ $system[] = 'EXCHANGE';
+ }
+
+ $creditTransactions = $this->getTransactionsFromShippingPortal($credit, $this->getDateRange($row['posting_date']));
+ foreach ($creditTransactions as $transaction) {
+ $systemReference[] = $transaction['order']['reference'];
+ $systemAmount[] = $transaction['amount'];
+ $system[] = 'SHIPPING';
+ }
+ }
+
+ if($debit){
+ $debitTransactions = $this->getTransactions($date, $debit, null, null, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], Group::class);
+
+ if(!count($debitTransactions)) {
+ foreach (['YSN', 'HCK', 'ATVANTIC', 'HIGH HILL'] as $reference){
+ if(str_contains($description, $reference)) {
+ $paymentDate = date('Y-m-d', strtotime('+1 day', strtotime($row['posting_date']))); //$date->addDays(1)->format('Y-m-d');
+
+ if($reference = 'ATVANTIC'){
+ $paymentDate = date('Y-m-d', strtotime($row['posting_date']));//$date->format('Y-m-d');
+ }
+ $issuer = Company::where('name', 'like', '%'.$reference.'%')->get()->pluck('id');
+ $debitTransactions = Group::whereIn('issuer', $issuer)->whereDate('created_at', $paymentDate)->get();
+ break;
+ }
+ }
+
+ }
+
+ foreach ($debitTransactions as $transaction) {
+ $systemReference[] = $transaction->reference;
+ $systemAmount[] = $transaction->amount;
+ $system[] = 'EXCHANGE';
+ }
+ }
+
+
+ $multiple = count($creditTransactions) + count($debitTransactions) > 1 ? $yes : $no;
+
+
+
+
+
+ $systemReference = $systemReference ? implode(',', $systemReference) : null;
+ $systemAmount = $systemAmount ? implode(',', $systemAmount) : null;
+
+ $matches = $systemReference == $row['remarkreferences'] ? $yes : $no;
+
+ $table .= '
+ '.$date->format('d-m-Y').'
+ branch
+ '.$description.'
+ '.$credit.'
+ '.$debit.'
+ '.$row['pay_for'].'
+ '.$system.'
+ '.$systemReference.'
+ '.$row['remarkreferences'].'
+ '.$multiple.'
+ '.$matches.'
+ '.$systemAmount.'
+ ';
+
+ //AccountStatement
+ // $row->statement()->first()->id)
+
+ $statementTransactionsDetail = new StatementTransactionOwner([
+ 'date' => $date,
+ 'statement_transaction_id' => $row->id,
+ 'description' => is_null($description) ? "" : $description,
+ 'credit' => $credit,
+ 'debit' => $debit,
+ 'pay_for' => $system,
+ 'system_references' => is_null($systemReference) ? "" : $systemReference,
+ 'remark_references' => is_null($row['remarkreferences']) ? "" : $row['remarkreferences'],
+ 'is_multiple' => $multiple == "Yes" ? 1 : 0,
+ 'is_matches' => $matches == "Yes" ? 1 : 0,
+ 'system_amounts'=> is_null($systemAmount) ? "" : $systemAmount,
+ ]);
+
+ $statementTransactionsDetail->save();
+
+ if($count == 10){
+ break;
+ }
+ }
+
+ $table .= '
';
+
+ return $table;
+ }
+
+ private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $model = Transaction::class) {
+ $query = $model::whereIn('status', $statuses)
+ ->where(function ($query) use ($ownerType, $paymentMethod, $type) {
+ if ($ownerType) {
+ $query->where('owner_type', $ownerType);
+ }
+
+ if ($paymentMethod) {
+ $query->where('payment_method', '!=', $paymentMethod);
+ }
+
+ if ($type) {
+ $query->where('type', $type);
+ }
+ })
+ ->whereDate('created_at', $date->format('Y-m-d'))
+ ->where('amount', '>', ($amount - 0.01))
+ ->where('amount', '<', ($amount + 0.01));
+
+ return $query->get();
+ }
+
+ private function getDateRange(string $dateStr) {
+ // Create a DateTime object from the input string
+ $date = strtotime($dateStr);
+
+ // Get the first day of the month
+ $today = date('Y-m-d', $date);
+
+ // Get the first day of the next month
+ $nextDay = date('Y-m-d', strtotime('+1 day', $date));
+
+ return [
+ 'start_date' => $today,
+ 'end_date' => $nextDay,
+ ];
+ }
+
+ private function getTransactionsFromShippingPortal($amount, $dateRange){
+
+ $client = new \GuzzleHttp\Client();
+ $response = $client->request('GET', 'https://izyim.cief-malaysia.com/public/api/v1/list?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2],"type":2,"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).'}');
+ $body = $response->getBody();
+ $data = json_decode($body, true);
+ $payload = $data['payload'];
+ $transactions2 = $payload['data'];
+ // $filters = [
+ // ['field' => 'created_at', 'value' => '2023-03-01 08:07:00'],
+ // ];
+ // $transactions2 = $this->getTransactions3($transactions2, $filters);
+ return $transactions2;
+ }
+
+}
diff --git a/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php b/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php
new file mode 100644
index 00000000..9f242704
--- /dev/null
+++ b/app/Http/Controllers/Accounting/GroupApproveStatementTransactionController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php b/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php
new file mode 100644
index 00000000..dd45e571
--- /dev/null
+++ b/app/Http/Controllers/Accounting/UpdateStatementTransactionStatusController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Accounts/FetchUserByEmailController.php b/app/Http/Controllers/Accounts/FetchUserByEmailController.php
new file mode 100644
index 00000000..0a60d4c7
--- /dev/null
+++ b/app/Http/Controllers/Accounts/FetchUserByEmailController.php
@@ -0,0 +1,19 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Accounts/UpdateUserRoleController.php b/app/Http/Controllers/Accounts/UpdateUserRoleController.php
new file mode 100644
index 00000000..aef992c8
--- /dev/null
+++ b/app/Http/Controllers/Accounts/UpdateUserRoleController.php
@@ -0,0 +1,19 @@
+execute($request);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Accounts/UserAuthenticationController.php b/app/Http/Controllers/Accounts/UserAuthenticationController.php
index fdf4442d..1ce36699 100644
--- a/app/Http/Controllers/Accounts/UserAuthenticationController.php
+++ b/app/Http/Controllers/Accounts/UserAuthenticationController.php
@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Accounts;
use App\Classes\Modules\Accounts\ControllersLogic\AuthenticateUserLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Log;
+use TimeHunter\LaravelGoogleReCaptchaV3\Facades\GoogleReCaptchaV3;
class UserAuthenticationController
{
diff --git a/app/Http/Controllers/Bookings/DeleteBookingController.php b/app/Http/Controllers/Bookings/DeleteBookingController.php
index a5629052..dfbd8a01 100644
--- a/app/Http/Controllers/Bookings/DeleteBookingController.php
+++ b/app/Http/Controllers/Bookings/DeleteBookingController.php
@@ -2,7 +2,7 @@
namespace App\Http\Controllers\Bookings;
-use App\Classes\Modules\Accounts\ControllersLogic\DeleteBookingLogic;
+use App\Classes\Modules\Bookings\ControllersLogic\DeleteBookingLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
diff --git a/app/Http/Controllers/Bookings/DeletePurchaseOrderPdfController.php b/app/Http/Controllers/Bookings/DeletePurchaseOrderPdfController.php
new file mode 100644
index 00000000..0113c0d5
--- /dev/null
+++ b/app/Http/Controllers/Bookings/DeletePurchaseOrderPdfController.php
@@ -0,0 +1,16 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php b/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php
index d2a7d85c..95427363 100644
--- a/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php
+++ b/app/Http/Controllers/Bookings/DownloadBookingDocumentController.php
@@ -15,7 +15,7 @@ class DownloadBookingDocumentController
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function download(Request $request, DownloadBookingDocumentLogic $logic) {
- $logic->execute($request);
+ return $logic->execute($request);
}
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Bookings/UpdateBookingOwnerController.php b/app/Http/Controllers/Bookings/UpdateBookingOwnerController.php
new file mode 100644
index 00000000..1ced189c
--- /dev/null
+++ b/app/Http/Controllers/Bookings/UpdateBookingOwnerController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Bookings/UploadPurchaseOrderController.php b/app/Http/Controllers/Bookings/UploadPurchaseOrderController.php
new file mode 100644
index 00000000..524691be
--- /dev/null
+++ b/app/Http/Controllers/Bookings/UploadPurchaseOrderController.php
@@ -0,0 +1,16 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Companies/ListBusinessTypesController.php b/app/Http/Controllers/Companies/ListBusinessTypesController.php
new file mode 100644
index 00000000..ecf1f0c9
--- /dev/null
+++ b/app/Http/Controllers/Companies/ListBusinessTypesController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Companies/ListCompanyTypesController.php b/app/Http/Controllers/Companies/ListCompanyTypesController.php
new file mode 100644
index 00000000..78c6abf9
--- /dev/null
+++ b/app/Http/Controllers/Companies/ListCompanyTypesController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Companies/UpdateCompanyNameAndDebtorController.php b/app/Http/Controllers/Companies/UpdateCompanyNameAndDebtorController.php
new file mode 100644
index 00000000..18599e7f
--- /dev/null
+++ b/app/Http/Controllers/Companies/UpdateCompanyNameAndDebtorController.php
@@ -0,0 +1,19 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Companies/UpdateCompanyProfileController.php b/app/Http/Controllers/Companies/UpdateCompanyProfileController.php
new file mode 100644
index 00000000..088c6ffd
--- /dev/null
+++ b/app/Http/Controllers/Companies/UpdateCompanyProfileController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Companies/UpdateCompanyStatusController.php b/app/Http/Controllers/Companies/UpdateCompanyStatusController.php
new file mode 100644
index 00000000..43b3ba3c
--- /dev/null
+++ b/app/Http/Controllers/Companies/UpdateCompanyStatusController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Currencies/History/ListCurrencyRateHistory.php b/app/Http/Controllers/Currencies/History/ListCurrencyRateHistory.php
new file mode 100644
index 00000000..7e7db955
--- /dev/null
+++ b/app/Http/Controllers/Currencies/History/ListCurrencyRateHistory.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Exports/ExportAnalyticToExcelController.php b/app/Http/Controllers/Exports/ExportAnalyticToExcelController.php
new file mode 100644
index 00000000..2a93a991
--- /dev/null
+++ b/app/Http/Controllers/Exports/ExportAnalyticToExcelController.php
@@ -0,0 +1,37 @@
+headers->set('Authorization', 'Bearer '.$token);
+ }
+
+ public function bookingData(ExportsAnalyticBookingTransactions $exportsAnalyticBookingTransactions, Request $request){
+ $response = $exportsAnalyticBookingTransactions->download('bookingData.csv', Excel::CSV, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
+ ob_end_clean();
+ return $response;
+ }
+
+ public function billingData(ExportsAnalyticBillingTransactions $exportsAnalyticBillingTransactions, Request $request){
+ $response = $exportsAnalyticBillingTransactions->download('billingData.csv', Excel::CSV, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
+ ob_end_clean();
+ return $response;
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php
index 39ea3dc5..74384157 100644
--- a/app/Http/Controllers/Exports/ExportCustomersToExcelController.php
+++ b/app/Http/Controllers/Exports/ExportCustomersToExcelController.php
@@ -5,10 +5,12 @@ namespace App\Http\Controllers\Exports;
use App\Classes\Modules\Exports\Services\ExportsCustomers;
use App\Classes\Modules\Exports\Services\ExportsTransactions;
+use App\Classes\Modules\Exports\Services\ExportsBookingTransactions;
+use App\Classes\Modules\Exports\Services\ExportsLeadsTransactions;
use App\Classes\Modules\Exports\Services\ExportsNullDebtors;
use App\Classes\Modules\Exports\Services\ExportsPaymentTransactions;
-
use App\Classes\Modules\Exports\Services\ExportsWalletTransactions;
+use App\Classes\Modules\Exports\Services\ExportsInvoiceTransactions;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -54,4 +56,23 @@ class ExportCustomersToExcelController
ob_end_clean();
return $response;
}
+
+ public function invoiceTransactions(Request $request){
+ $exportsTransactions = new ExportsInvoiceTransactions($request);
+ $response = $exportsTransactions->download('invoice-transactions.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
+ ob_end_clean();
+ return $response;
+ }
+
+ public function bookingTransactions(ExportsBookingTransactions $exportsBookingTransactions, Request $request){
+ $response = $exportsBookingTransactions->download('bookingTransactions.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
+ ob_end_clean();
+ return $response;
+ }
+
+ public function leadsData(ExportsLeadsTransactions $exportsLeadsTransactions, Request $request){
+ $response = $exportsLeadsTransactions->download('leadsData.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
+ ob_end_clean();
+ return $response;
+ }
}
\ No newline at end of file
diff --git a/app/Http/Controllers/Imports/ImportBankRecordController.php b/app/Http/Controllers/Imports/ImportBankRecordController.php
new file mode 100644
index 00000000..bd0272d2
--- /dev/null
+++ b/app/Http/Controllers/Imports/ImportBankRecordController.php
@@ -0,0 +1,157 @@
+ 'MBB Cyber',
+ 1 => 'MBB SS2',
+ ];
+
+ $yes = 'Yes';
+ $no = 'No';
+
+ $table = ''.implode(' ', $headers).' ';
+
+ $collection = Excel::toCollection(new ImportsBankRecord(), 'daily_transaction_nov.xlsx');
+
+ foreach ($collection as $key => $sheet){
+ $branch = $branches[$key];
+ foreach ($sheet as $row) {
+ $date = Carbon::instance(Date::excelToDateTimeObject($row['date']));
+ $description = $row['description'];
+ $credit = (float) $row['credit'];
+ $debit = (float) $row['debit'];
+ $creditTransactions = [];
+ $debitTransactions = [];
+
+ $systemReference = null;
+ $systemAmount = null;
+
+ if($credit){
+ $creditTransactions = $this->getTransactions($date, $credit, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
+ foreach ($creditTransactions as $transaction) {
+ $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no;
+ $systemAmount[] = $transaction->amount;
+ }
+
+ $creditTransactions = $this->getTransactions($date, $credit, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
+ foreach ($creditTransactions as $transaction) {
+ $systemReference[] = $transaction->owner instanceof Booking ? $transaction->owner->marking : $transaction->bill_no;
+ $systemAmount[] = $transaction->amount;
+ }
+ }
+
+ if($debit){
+ $debitTransactions = $this->getTransactions($date, $debit, null, null, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], Group::class);
+
+ if(!count($debitTransactions)) {
+ foreach (['YSN', 'HCK', 'ATVANTIC', 'HIGH HILL'] as $reference){
+ if(str_contains($description, $reference)) {
+ $paymentDate = $date->addDays(1)->format('Y-m-d');
+ if($reference = 'ATVANTIC'){
+ $paymentDate = $date->format('Y-m-d');
+ }
+ $issuer = Company::where('name', 'like', '%'.$reference.'%')->get()->pluck('id');
+ $debitTransactions = Group::whereIn('issuer', $issuer)->whereDate('created_at', $paymentDate)->get();
+ break;
+ }
+ }
+
+ }
+
+ foreach ($debitTransactions as $transaction) {
+ $systemReference[] = $transaction->reference;
+ $systemAmount[] = $transaction->amount;
+ }
+ }
+
+
+ $multiple = count($creditTransactions) + count($debitTransactions) > 1 ? $yes : $no;
+
+
+
+
+
+ $systemReference = $systemReference ? implode(',', $systemReference) : null;
+ $systemAmount = $systemAmount ? implode(',', $systemAmount) : null;
+
+ $matches = $systemReference == $row['remarkreferences'] ? $yes : $no;
+
+ $table .= '
+ '.$date->format('d-m-Y').'
+ '.$branch.'
+ '.$description.'
+ '.$credit.'
+ '.$debit.'
+ '.$row['pay_for'].'
+ '.$systemReference.'
+ '.$row['remarkreferences'].'
+ '.$multiple.'
+ '.$matches.'
+ '.$systemAmount.'
+ ';
+ }
+ }
+
+ $table .= '
';
+
+ echo $table;
+ }
+
+ private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $model = Transaction::class) {
+ $query = $model::whereIn('status', $statuses)
+ ->where(function ($query) use ($ownerType, $paymentMethod, $type) {
+ if ($ownerType) {
+ $query->where('owner_type', $ownerType);
+ }
+
+ if ($paymentMethod) {
+ $query->where('payment_method', '!=', $paymentMethod);
+ }
+
+ if ($type) {
+ $query->where('type', $type);
+ }
+ })
+ ->whereDate('created_at', $date->format('Y-m-d'))
+ ->where('amount', '>', ($amount - 0.01))
+ ->where('amount', '<', ($amount + 0.01));
+
+ return $query->get();
+ }
+}
diff --git a/app/Http/Controllers/Imports/ImportHoneyTrapController.php b/app/Http/Controllers/Imports/ImportHoneyTrapController.php
new file mode 100644
index 00000000..8f016ed4
--- /dev/null
+++ b/app/Http/Controllers/Imports/ImportHoneyTrapController.php
@@ -0,0 +1,133 @@
+input('files'), '', ApprovalStatus::APPROVED, 'imports');
+ $file = json_decode($object->getFiles()[0])->file_info->original->file;
+
+ $import = new GenericImport();
+ Excel::import($import, $file);
+ $excelRows = $import->rows;
+ $excelRows = $excelRows->toArray();
+
+ $returnArray = [];
+ // $segment_name = 'honey trap campaign';
+ // $segment = Segment::where('name', $segment_name)->first();
+ $input_segment_id = $request->input('segment_id');
+ if (!$input_segment_id) {
+ $row['status'] = 'Failed';
+ $row['message'] = 'segment_id cannot be empty';
+ $returnArray[] = $row;
+ return response()->json($returnArray);
+ }
+
+ $segment = Segment::where('id', $input_segment_id)->first();
+ if (!$segment) {
+ $row['status'] = 'Failed';
+ $row['message'] = 'Segment not found';
+ $returnArray[] = $row;
+ return response()->json($returnArray);
+ }
+
+ $segment_id = $segment->id;
+
+ foreach ($excelRows as $row) {
+
+ if (is_null($row['email']) || empty($row['email'])) {
+ continue;
+ }
+
+ if (is_null($row['end_date']) || empty($row['end_date'])) {
+ // use csv import must have an end_date
+ $row['status'] = 'failed';
+ $row['message'] = 'End Date is required';
+ $returnArray[] = $row;
+ continue;
+ }
+
+ $row['end_date'] = $end_date = $this->changeExcelDate($row['end_date']);
+ $start_date = $row['start_date'] ? $this->changeExcelDate($row['start_date']) : Carbon::now();
+ $row['start_date'] = $start_date;
+
+ $end_date = Carbon::parse($end_date);
+ // Check if the end_date is in the past
+ if ($end_date->isPast()) {
+ // end_date must be after today's date
+ $row['status'] = 'failed';
+ $row['message'] = "End Date must be after today's date";
+ $returnArray[] = $row;
+ continue;
+ }
+
+ $user = User::where('email', $row['email'])->first();
+ if (!$user) {
+ // if user not found
+ $row['status'] = 'failed';
+ $row['message'] = 'Email not found';
+ $returnArray[] = $row;
+ continue;
+ }
+
+ $company = $user->company->first();
+ if (!$company) {
+ // if company not found
+ $row['status'] = 'failed';
+ $row['message'] = 'Company not found';
+ $returnArray[] = $row;
+ continue;
+ }
+
+ $company_seasonal_honey_trap_count = SeasonalSegment::where('company_id', $company->id)->where('segment_id', $segment_id)->get();
+ if (count($company_seasonal_honey_trap_count)) {
+ // seasonal segment already exists
+ $row['status'] = 'failed';
+ $row['message'] = 'Company is already in the Honey Trap Segment';
+ $returnArray[] = $row;
+ continue;
+ }
+
+ $seasonalSegmentObject = new SeasonalSegmentObject($company->id, $segment_id, $start_date, $end_date ?? null);
+
+ (App()->make(createsSeasonalSegment::class))->execute($seasonalSegmentObject);
+ (App()->make(assignSegmentProcessor::class))->execute($company, $segment_id);
+
+ // success added honey trap seasonal segment
+ $row['status'] = 'success';
+ $row['message'] = '';
+ $returnArray[] = $row;
+ continue;
+ }
+
+ return response()->json($returnArray);
+ }
+
+ public function changeExcelDate($date)
+ {
+ $unixTime = (($date - 25569) * 86400);
+ $date = new DateTime("@$unixTime");
+ return $date->format('Y-m-d'); // Change the format to 'Y-m-d'
+ }
+}
diff --git a/app/Http/Controllers/Imports/ImportStatementInvoiceController.php b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php
new file mode 100644
index 00000000..a59f9463
--- /dev/null
+++ b/app/Http/Controllers/Imports/ImportStatementInvoiceController.php
@@ -0,0 +1,99 @@
+input('files'), '', ApprovalStatus::APPROVED, 'imports');
+ $file = json_decode($object->getFiles()[0])->file_info->original->file;
+
+ $import = new GenericImport();
+ Excel::import($import, $file);
+ $excelRows = $import->rows;
+ $excelRows = $excelRows->toArray();
+
+ foreach ($excelRows as $row) {
+ dd($row);
+ // $row['debtor_code']
+
+ // attempt 1 - try map by amount and date
+ // $transactionDate = $this->changeExcelDate($row['date']);
+ // $transaction = Transaction::where('original_amount', $row['total'])->whereDate('created_at', $transactionDate)->get();
+ // if ($transaction) {
+ // // check company
+ // // $company = Company::where('debtor', $row['debtor_code'])->first();
+ // // dd($company);
+ // // try to verify is it the correct transaction
+ // }
+
+ // Shipping Info
+ // TOPUP -> map with transaction.bill_no
+ if (str_starts_with($row['shipping_info'], 'TOPUP')) {
+ // find in exchange first, if cannont then find in izyim
+ // (App()->make(ChecksBillNumber::class))->execute($bill_no, 'exchange');
+ }
+
+ // if 5 digits -> exchange booking reference
+ // find transation
+ // find statement_transaction_owners, and fill up the details
+
+ // if <5 digits, find the transaction id (order number in izyim), find the payment in izyim
+ // find transation
+ // find statement_transaction_owners, and fill up the details
+
+ // dd([
+ // 'type' => $statementTransactionOwnerType,
+ // 'system' => $system,
+ // // 'owner_type' => Transaction::class,
+ // // todo-new: make sure owner_type is a class
+ // 'owner_type' => $owner_type,
+ // 'owner_id' => $owner_id,
+ // 'owner_reference' => $owner_reference
+ // ]);
+
+ // $bankStatementTransaction->owners()->firstOrCreate([
+ // 'type' => $statementTransactionOwnerType,
+ // 'system' => $system,
+ // // 'owner_type' => Transaction::class,
+ // // todo-new: make sure owner_type is a class
+ // 'owner_type' => $owner_type,
+ // 'owner_id' => $owner_id,
+ // 'owner_reference' => $owner_reference
+ // ]);
+
+
+ }
+ }
+
+ public function changeExcelDate($date)
+ {
+ $unixTime = (($date - 25569) * 86400);
+ $date = new DateTime("@$unixTime");
+ return $date->format('Y-m-d'); // Change the format to 'Y-m-d'
+ }
+}
diff --git a/app/Http/Controllers/Imports/ImportStatementReceiptsController.php b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php
new file mode 100644
index 00000000..22111881
--- /dev/null
+++ b/app/Http/Controllers/Imports/ImportStatementReceiptsController.php
@@ -0,0 +1,50 @@
+input('files'), '', ApprovalStatus::APPROVED, 'imports');
+ $file = json_decode($object->getFiles()[0])->file_info->original->file;
+
+ $import = new GenericImport();
+ Excel::import($import, $file);
+ $excelRows = $import->rows;
+ $excelRows = $excelRows->toArray();
+
+ foreach ($excelRows as $row) {
+ // if has date column
+ // $transactionDate = $this->changeExcelDate($row['date']);
+ }
+ }
+
+ public function changeExcelDate($date)
+ {
+ $unixTime = (($date - 25569) * 86400);
+ $date = new DateTime("@$unixTime");
+ return $date->format('Y-m-d'); // Change the format to 'Y-m-d'
+ }
+}
diff --git a/app/Http/Controllers/Imports/ImportUpdateDebtorController.php b/app/Http/Controllers/Imports/ImportUpdateDebtorController.php
index 72f998d4..1acc7b11 100644
--- a/app/Http/Controllers/Imports/ImportUpdateDebtorController.php
+++ b/app/Http/Controllers/Imports/ImportUpdateDebtorController.php
@@ -8,9 +8,12 @@ use App\Classes\General\ExcelHandel;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\User;
+use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
+use Maatwebsite\Excel\Excel as ExcelFileTypes;
class ImportUpdateDebtorController
{
@@ -24,4 +27,4 @@ class ImportUpdateDebtorController
Excel::import(new ImportsDebtor(), json_decode($object->getFiles()[0])->file_info->original->file);
return [];
}
-}
\ No newline at end of file
+}
diff --git a/app/Http/Controllers/Milestones/CreateMilestoneController.php b/app/Http/Controllers/Milestones/CreateMilestoneController.php
new file mode 100644
index 00000000..d5d2ba0c
--- /dev/null
+++ b/app/Http/Controllers/Milestones/CreateMilestoneController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Milestones/DeleteMilestoneController.php b/app/Http/Controllers/Milestones/DeleteMilestoneController.php
new file mode 100644
index 00000000..084ff35e
--- /dev/null
+++ b/app/Http/Controllers/Milestones/DeleteMilestoneController.php
@@ -0,0 +1,19 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Milestones/ListMilestoneProgressController.php b/app/Http/Controllers/Milestones/ListMilestoneProgressController.php
new file mode 100644
index 00000000..03e2aa10
--- /dev/null
+++ b/app/Http/Controllers/Milestones/ListMilestoneProgressController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Milestones/ListMilestonesController.php b/app/Http/Controllers/Milestones/ListMilestonesController.php
new file mode 100644
index 00000000..b592c0d4
--- /dev/null
+++ b/app/Http/Controllers/Milestones/ListMilestonesController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Milestones/UpdateMilestoneController.php b/app/Http/Controllers/Milestones/UpdateMilestoneController.php
new file mode 100644
index 00000000..e3dce17d
--- /dev/null
+++ b/app/Http/Controllers/Milestones/UpdateMilestoneController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Notifications/ListNotificationsController.php b/app/Http/Controllers/Notifications/ListNotificationsController.php
new file mode 100644
index 00000000..58455fc2
--- /dev/null
+++ b/app/Http/Controllers/Notifications/ListNotificationsController.php
@@ -0,0 +1,19 @@
+execute($request);
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/Rewards/CreateRewardController.php b/app/Http/Controllers/Rewards/CreateRewardController.php
new file mode 100644
index 00000000..55ea98c9
--- /dev/null
+++ b/app/Http/Controllers/Rewards/CreateRewardController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Rewards/DeleteRewardController.php b/app/Http/Controllers/Rewards/DeleteRewardController.php
new file mode 100644
index 00000000..bc8e90e9
--- /dev/null
+++ b/app/Http/Controllers/Rewards/DeleteRewardController.php
@@ -0,0 +1,19 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Rewards/ListRewardsController.php b/app/Http/Controllers/Rewards/ListRewardsController.php
new file mode 100644
index 00000000..2d02d898
--- /dev/null
+++ b/app/Http/Controllers/Rewards/ListRewardsController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Rewards/ListRewardsDetailsController.php b/app/Http/Controllers/Rewards/ListRewardsDetailsController.php
new file mode 100644
index 00000000..bd853b00
--- /dev/null
+++ b/app/Http/Controllers/Rewards/ListRewardsDetailsController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderDocumentController.php b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderDocumentController.php
new file mode 100644
index 00000000..eeeff585
--- /dev/null
+++ b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderDocumentController.php
@@ -0,0 +1,21 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderTransactionController.php b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderTransactionController.php
new file mode 100644
index 00000000..695b8487
--- /dev/null
+++ b/app/Http/Controllers/Transactions/CreateBulkPurchaseOrderTransactionController.php
@@ -0,0 +1,21 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Transactions/DeleteGroupController.php b/app/Http/Controllers/Transactions/DeleteGroupController.php
new file mode 100644
index 00000000..14ba54f9
--- /dev/null
+++ b/app/Http/Controllers/Transactions/DeleteGroupController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Transactions/FetchCompanyTransactionStatementController.php b/app/Http/Controllers/Transactions/FetchCompanyTransactionStatementController.php
new file mode 100644
index 00000000..a1d8c3cb
--- /dev/null
+++ b/app/Http/Controllers/Transactions/FetchCompanyTransactionStatementController.php
@@ -0,0 +1,21 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Transactions/GenerateCreditNotePdfController.php b/app/Http/Controllers/Transactions/GenerateCreditNotePdfController.php
new file mode 100644
index 00000000..11d27cdd
--- /dev/null
+++ b/app/Http/Controllers/Transactions/GenerateCreditNotePdfController.php
@@ -0,0 +1,15 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Transactions/ListGroupsController.php b/app/Http/Controllers/Transactions/ListGroupsController.php
new file mode 100644
index 00000000..730633e9
--- /dev/null
+++ b/app/Http/Controllers/Transactions/ListGroupsController.php
@@ -0,0 +1,21 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Transactions/UpdateGroupController.php b/app/Http/Controllers/Transactions/UpdateGroupController.php
new file mode 100644
index 00000000..db0b3a17
--- /dev/null
+++ b/app/Http/Controllers/Transactions/UpdateGroupController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+}
diff --git a/app/Http/Controllers/Vouchers/CreateVoucherController.php b/app/Http/Controllers/Vouchers/CreateVoucherController.php
new file mode 100644
index 00000000..f89c93ac
--- /dev/null
+++ b/app/Http/Controllers/Vouchers/CreateVoucherController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Vouchers/ListUserVouchersController.php b/app/Http/Controllers/Vouchers/ListUserVouchersController.php
new file mode 100644
index 00000000..df8cb610
--- /dev/null
+++ b/app/Http/Controllers/Vouchers/ListUserVouchersController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Vouchers/ValidateVoucherController.php b/app/Http/Controllers/Vouchers/ValidateVoucherController.php
new file mode 100644
index 00000000..86c4c4e1
--- /dev/null
+++ b/app/Http/Controllers/Vouchers/ValidateVoucherController.php
@@ -0,0 +1,20 @@
+execute($request);
+ }
+
+}
diff --git a/app/Http/Controllers/Wallets/WalletReportController.php b/app/Http/Controllers/Wallets/WalletReportController.php
new file mode 100644
index 00000000..998bba53
--- /dev/null
+++ b/app/Http/Controllers/Wallets/WalletReportController.php
@@ -0,0 +1,29 @@
+ [
+ 'walletSum' => (float) Wallet::all()->sum('amount'),
+ 'outgoingSum' => (float) Transaction::where('type', TransactionType::PAYMENT)->where('owner_type', Wallet::class)->sum('amount'),
+ 'incomingSum' => (float) Transaction::whereIn('type', [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE])->where('owner_type', Wallet::class)->sum('amount'),
+ ]]
+ ))->handler();
+ }
+}
diff --git a/app/Http/Resources/AnnouncementResource.php b/app/Http/Resources/AnnouncementResource.php
index 26e1c2f9..2e6048e0 100644
--- a/app/Http/Resources/AnnouncementResource.php
+++ b/app/Http/Resources/AnnouncementResource.php
@@ -20,8 +20,8 @@ class AnnouncementResource extends JsonResource
'title' => $this->title,
'description' => $this->description,
'segments' => $this->segments,
- 'starting_on' => Carbon::parse($this->starting_on)->format('Y-m-d'),
- 'ending_on' => Carbon::parse($this->ending_on)->format('Y-m-d'),
+ 'starting_on' => Carbon::parse($this->starting_on)->format('d-m-Y'),
+ 'ending_on' => Carbon::parse($this->ending_on)->format('d-m-Y'),
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'),
];
}
diff --git a/app/Http/Resources/BankResource.php b/app/Http/Resources/BankResource.php
index 35fec923..f3de3038 100644
--- a/app/Http/Resources/BankResource.php
+++ b/app/Http/Resources/BankResource.php
@@ -21,6 +21,7 @@ class BankResource extends JsonResource
'reference' => $this->reference,
'bank_name' => $this->bank_name,
'bank_branch' => $this->bank_branch,
+ 'swift' => $this->swift,
'holder_name' => $this->holder_name,
'account_no' => $this->account_no,
'country_id' => $this->country_id,
diff --git a/app/Http/Resources/BankStatementTransactionOwnerResource.php b/app/Http/Resources/BankStatementTransactionOwnerResource.php
new file mode 100644
index 00000000..468d9ec5
--- /dev/null
+++ b/app/Http/Resources/BankStatementTransactionOwnerResource.php
@@ -0,0 +1,60 @@
+system === 'EXCHANGE') {
+ if($this->owner_type === Transaction::class){
+ if($this->type === StatementTransactionOwnerType::SALES){
+ $referenceLink = route('booking.details', $this->owner_reference);
+ }
+
+ if($this->type === StatementTransactionOwnerType::WALLET_TOP_UP){
+ $referenceLink = route('booking.details', $this->owner_reference);
+ }
+ }
+ }
+
+ if($this->system === 'SHIPPING_PORTAL') {
+ if($this->owner_type === Transaction::class){
+ if($this->type === StatementTransactionOwnerType::SALES){
+ $referenceLink = 'https://izyim.cief-malaysia.com/order/show/'. $this->owner_reference;
+ }
+ if($this->type === StatementTransactionOwnerType::WALLET_TOP_UP){
+ $referenceLink = 'https://izyim.cief-malaysia.com/wallet/'. $this->owner_reference .'/details';
+ }
+ }
+ }
+
+ return [
+ 'id' => $this->id,
+ 'type' => $this->type,
+ 'system' => $this->system,
+ 'owner_type' => $this->owner_type,
+ 'owner_id' => $this->owner_id,
+ 'reference' => $this->owner_reference,
+ 'reference_link' => $referenceLink,
+ 'invoice_reference' => $this->invoice_reference,
+ 'receipt_reference' => $this->receipt_reference,
+ 'status' => $this->status
+ ];
+ }
+}
diff --git a/app/Http/Resources/BankStatementTransactionResource.php b/app/Http/Resources/BankStatementTransactionResource.php
new file mode 100644
index 00000000..993b8c93
--- /dev/null
+++ b/app/Http/Resources/BankStatementTransactionResource.php
@@ -0,0 +1,41 @@
+ $this->id,
+ 'account_number' => $this->statement->account->number,
+ 'account_type' => $this->statement->account->type,
+ 'account_name' => $this->statement->account->name,
+ 'account_statement_id' => $this->statement->id,
+ 'account_statement_date_from' => $this->statement->date_from,
+ 'account_statement_date_to' => $this->statement->date_to,
+ 'posting_date' => $this->posting_date->format('d-m-Y g:i A'),
+ 'amount' => $this->amount,
+ 'transaction_description_1' => $this->transaction_description,
+ 'transaction_description_2' => $this->transaction_description_2,
+ 'transaction_description_3' => $this->transaction_description_3,
+ 'transaction_description_4' => $this->transaction_description_4,
+ 'transaction_description_5' => $this->transaction_description_5,
+ 'owners' => [
+ 'approved' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::APPROVED])->get()),
+ 'pending_verification' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION])->get()),
+ 'rejected' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::REJECTED])->get())
+ ]
+ ];
+ }
+}
diff --git a/app/Http/Resources/BookingResource.php b/app/Http/Resources/BookingResource.php
index 0eeb9670..f3a7881d 100644
--- a/app/Http/Resources/BookingResource.php
+++ b/app/Http/Resources/BookingResource.php
@@ -11,6 +11,7 @@ use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\DocumentType;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
+use Illuminate\Support\Facades\Log;
class BookingResource extends JsonResource
{
@@ -43,6 +44,7 @@ class BookingResource extends JsonResource
'invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::INVOICE)->first()),
'supplier_delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::SUPPLIER_DELIVER_ORDER)->first()),
'proforma_invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::PROFORMA_INVOICE)->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::EXPIRED])->orderByDesc('id')->first()),
+ 'ecommerce_purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->first()),
],
'status' => $this->status,
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'),
@@ -55,7 +57,7 @@ class BookingResource extends JsonResource
->whereDate('expires_on', '>=', Carbon::now())
->get()
),
- 'expired_payment_attempts' => TransactionResource::collection($this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '<', Carbon::now())->get()),
+ 'expired_payment_attempts' => TransactionResource::collection($this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString())->get()),
'payment_history' => TransactionResource::collection($this->transactions()->where(function($query){
$query->where(function($query){
$query->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED]);
diff --git a/app/Http/Resources/CompanyResource.php b/app/Http/Resources/CompanyResource.php
index fceab5bc..85ecee16 100644
--- a/app/Http/Resources/CompanyResource.php
+++ b/app/Http/Resources/CompanyResource.php
@@ -37,6 +37,7 @@ class CompanyResource extends JsonResource
'id' => $this->id,
'name' => $this->name,
'reference' => $this->reference,
+ 'debtor' => $this->debtor,
'type' => (int) $this->type,
'business_type' => (int) $this->business_type,
'status' => (int) $this->status,
@@ -60,8 +61,9 @@ class CompanyResource extends JsonResource
'default' => new BankResource($this->banks->where('type', BankAccountType::EXTERNAL)->where('default', true)->first())
],
'segments' => SegmentResource::collection($this->segments),
+ 'seasonalSegment' => $this->whenLoaded('seasonalSegments', SeasonalSegmentResource::collection($this->seasonalSegments)),
'services' => (new FetchesCompanyServices())->getServices($this->servicesConfigurations()),
- 'wallet' => new WalletResource($this->wallets()->first()),
+ 'wallet' => $this->whenLoaded('wallets', new WalletResource($this->wallets()->with('transactions')->first()), new WalletResource($this->wallets()->first())),
'created_at' => $this->created_at->format('d-m-Y'),
$this->mergeWhen($this->business_type === BusinessType::CURRENCY_VENDOR, [
'currencies' => $segment ? CurrencyResource::collection(Currency::whereIn('id', $segment->detail->currencies)->get()) : [],
diff --git a/app/Http/Resources/CurrencyRateLogResource.php b/app/Http/Resources/CurrencyRateLogResource.php
new file mode 100644
index 00000000..f8560dd8
--- /dev/null
+++ b/app/Http/Resources/CurrencyRateLogResource.php
@@ -0,0 +1,26 @@
+currencyRate;
+
+ return [
+ 'currency_rate_id' => $this->currency_rate_id,
+ 'rate' => $this->selling,
+ 'created_at' => $this->created_at->format('d-m-Y'),
+ 'payment_method_type' => $currencyRate->payment_method_type,
+ ];
+ }
+}
diff --git a/app/Http/Resources/GeneralTypeResource.php b/app/Http/Resources/GeneralTypeResource.php
new file mode 100644
index 00000000..b6dc2348
--- /dev/null
+++ b/app/Http/Resources/GeneralTypeResource.php
@@ -0,0 +1,22 @@
+ $this->id,
+ 'type' => $this->type,
+ ];
+ }
+}
diff --git a/app/Http/Resources/GroupResource.php b/app/Http/Resources/GroupResource.php
new file mode 100644
index 00000000..abd017ec
--- /dev/null
+++ b/app/Http/Resources/GroupResource.php
@@ -0,0 +1,53 @@
+issuerCompany){
+ dd($this->id);
+ }
+ return [
+ 'id' => $this->id,
+ 'original_amount' => (float) $this->original_amount,
+ 'original_currency' => new CurrencyResource($this->original_currency),
+ 'issuer_name' => $this->issuerCompany->name,
+ 'issuer_id' => $this->issuerCompany->id,
+ 'amount' => (float) $this->amount,
+ 'service_charge' => (float) $this->amount,
+ 'currency' => new CurrencyResource($this->currency),
+ 'created_at' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A'),
+ 'currency_rate' => (float) $this->currency_rate,
+ 'transactions' => $this->transactions()->get()->pluck('owner.owner.marking'),
+ 'complete_transactions' => $this->transactions()->whereHasMorph('owner', [Transaction::class], function($query){
+ return $query->whereHas('booking', function($query){
+ return $query->whereHas('transactions', function($query){
+ return $query->where('type', TransactionType::PURCHASE_ORDER)->where('status', '=', ApprovalStatus::APPROVED);
+ });
+ });
+ })->get()->pluck('owner.owner.marking'),
+ 'documents' => [
+ 'currency_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::CURRENCY_VENDOR_ORDER)->first()),
+ 'purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::BULK_PURCHASE_ORDER)->first())
+ ]
+ ];
+ }
+}
diff --git a/app/Http/Resources/MilestoneProgressResource.php b/app/Http/Resources/MilestoneProgressResource.php
new file mode 100644
index 00000000..ed4c465f
--- /dev/null
+++ b/app/Http/Resources/MilestoneProgressResource.php
@@ -0,0 +1,23 @@
+ $this->id,
+ 'milestone_id' => $this->milestone_id,
+ 'created_at' => $this->created_at
+ ];
+ }
+}
diff --git a/app/Http/Resources/MilestoneResource.php b/app/Http/Resources/MilestoneResource.php
new file mode 100644
index 00000000..f336f937
--- /dev/null
+++ b/app/Http/Resources/MilestoneResource.php
@@ -0,0 +1,28 @@
+rewards->pluck('id')->map(function ($id) {
+ return (int) $id;
+ })->toArray();
+
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'description' => $this->description,
+ 'reward_ids' => $rewardIds
+ ];
+ }
+}
diff --git a/app/Http/Resources/MilestoneWIthMiltestoneProgressResource.php b/app/Http/Resources/MilestoneWIthMiltestoneProgressResource.php
new file mode 100644
index 00000000..4b4176b6
--- /dev/null
+++ b/app/Http/Resources/MilestoneWIthMiltestoneProgressResource.php
@@ -0,0 +1,30 @@
+route('user_id');
+ if(!$userId){
+ $userId = Auth::user()->id;
+ }
+
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'description' => $this->description,
+ 'milestone_progress' => new MilestoneProgressResource($this->progress->where('user_id', $userId)->first()),
+ ];
+ }
+}
diff --git a/app/Http/Resources/NotificationResource.php b/app/Http/Resources/NotificationResource.php
new file mode 100644
index 00000000..564b7b31
--- /dev/null
+++ b/app/Http/Resources/NotificationResource.php
@@ -0,0 +1,30 @@
+ $this->id,
+ 'title' => $this->title,
+ 'description' => $this->description,
+ 'long_ago' => $this->created_at->diffForHumans(),
+ 'created_at' => $this->created_at->format('d-m-Y')
+ ];
+
+ }
+}
diff --git a/app/Http/Resources/PaymentTransactionResource.php b/app/Http/Resources/PaymentTransactionResource.php
new file mode 100644
index 00000000..456ece3d
--- /dev/null
+++ b/app/Http/Resources/PaymentTransactionResource.php
@@ -0,0 +1,56 @@
+type, [TransactionType::BILL, TransactionType::REFUND])? $this->owner->owner : $this->owner;
+
+ $booking_marking = '';
+ switch ($this->owner_type) {
+ case Booking::class:
+ $booking_marking = $booking->marking;
+ break;
+ case Wallet::class:
+ $booking_marking = $this->booking->marking;
+ break;
+ }
+
+ return [
+ 'id' => $this->id,
+ 'booking_marking' => $booking_marking,
+ 'type' => (int) $this->type,
+ 'bill_no' => $this->bill_no,
+ 'payment_reference' => $this->payment_reference,
+ 'payment_method' => (float) $this->payment_method,
+ 'recipient_bank_account' => new BankResource($booking->bank),
+ 'issuer_name' => $this->issuerCompany->name,
+ 'issuer_id' => $this->issuerCompany->id,
+ 'amount' => (double) $this->amount,
+ 'original_amount' => (double) $this->original_amount,
+ 'currency' => new CurrencyResource($this->currency),
+ 'original_currency' => new CurrencyResource($this->original_currency),
+ 'service_charge' => (double) $this->service_charge,
+ 'tax' => (double) $this->tax,
+ 'currency_rate' => (double) $this->currency_rate,
+ 'status' => (int) $this->status,
+ 'updated_at' => Carbon::parse($this->updated_at)->format('d-m-Y h:i:s A'),
+ 'created_at' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A')
+ ];
+ }
+}
diff --git a/app/Http/Resources/RewardDetailsResource.php b/app/Http/Resources/RewardDetailsResource.php
new file mode 100644
index 00000000..2e18cdb6
--- /dev/null
+++ b/app/Http/Resources/RewardDetailsResource.php
@@ -0,0 +1,36 @@
+route('user_id');
+ if(!$userId){
+ $userId = Auth::user()->id;
+ }
+
+ $userReward = $this->users->where('user_id', $userId)->first();
+
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'description' => $this->description,
+ 'is_active' => $this->is_active,
+ 'milestones' => $this->milestones,
+ 'milestones_progress' => MilestoneWIthMiltestoneProgressResource::collection($this->milestones),
+ 'user_rewards' => new UserRewardResource($userReward),
+ 'voucher' => $userReward ? $userReward->voucher : null
+ ];
+ }
+}
diff --git a/app/Http/Resources/RewardResource.php b/app/Http/Resources/RewardResource.php
new file mode 100644
index 00000000..e400fc6a
--- /dev/null
+++ b/app/Http/Resources/RewardResource.php
@@ -0,0 +1,28 @@
+ $this->id,
+ 'name' => $this->name,
+ 'description' => $this->description,
+ 'is_active' => $this->is_active,
+ 'type' => $this->type,
+ 'value' => $this->value,
+ 'order' => $this->order,
+ 'milestones' => MilestoneResource::collection($this->milestones)
+ ];
+ }
+}
diff --git a/app/Http/Resources/SeasonalSegmentResource.php b/app/Http/Resources/SeasonalSegmentResource.php
new file mode 100644
index 00000000..8a38f5be
--- /dev/null
+++ b/app/Http/Resources/SeasonalSegmentResource.php
@@ -0,0 +1,37 @@
+ $this->id,
+ 'segment_name' => ucwords($this->segment->name),
+ 'ending_on' => $this->ending_on->format('d-m-Y'),
+ ];
+ }
+}
diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php
index 30ba223c..27fd17ab 100644
--- a/app/Http/Resources/TransactionResource.php
+++ b/app/Http/Resources/TransactionResource.php
@@ -30,6 +30,7 @@ class TransactionResource extends JsonResource
'payment_method' => (float) $this->payment_method,
'recipient_bank_account' => new BankResource($booking->bank),
'issuer_name' => $this->issuerCompany->name,
+ 'issuer_id' => $this->issuerCompany->id,
'amount' => (double) $this->amount,
'original_amount' => (double) $this->original_amount,
'currency' => new CurrencyResource($this->currency),
@@ -47,7 +48,8 @@ class TransactionResource extends JsonResource
'interval' => [
'value' => $days->gt(Carbon::now()) ? '+' : '-',
'duration' => $days->diff(Carbon::now())->format('%d'),
- ]
+ ],
+ 'redemption' => new VoucherRedemptionResource($this->voucherRedemption)
];
}
}
diff --git a/app/Http/Resources/UserCompanyResource.php b/app/Http/Resources/UserCompanyResource.php
new file mode 100644
index 00000000..09e9f2b7
--- /dev/null
+++ b/app/Http/Resources/UserCompanyResource.php
@@ -0,0 +1,25 @@
+ $this->id,
+ 'name' => $this->name,
+ 'reference' => $this->company()->first()->reference,
+ 'type' => (int) $this->type,
+ 'status' => (int) $this->status
+ ];
+ }
+}
diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php
index 3d052d02..97f4ab38 100644
--- a/app/Http/Resources/UserResource.php
+++ b/app/Http/Resources/UserResource.php
@@ -2,6 +2,7 @@
namespace App\Http\Resources;
+use App\Classes\ValueObjects\Constants\RoleTypes;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
@@ -14,11 +15,14 @@ class UserResource extends JsonResource
*/
public function toArray($request)
{
+ $userTypeArray = RoleTypes::USER_TYPE_ID;
+
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'type' => (int) $this->type,
+ 'type_name' => ($userTypeArray[(int) $this->type]),
'status' => (int) $this->status
];
}
diff --git a/app/Http/Resources/UserRewardResource.php b/app/Http/Resources/UserRewardResource.php
new file mode 100644
index 00000000..ed693b4c
--- /dev/null
+++ b/app/Http/Resources/UserRewardResource.php
@@ -0,0 +1,25 @@
+ $this->id,
+ 'user_id' => $this->user_id,
+ 'reward' => new RewardResource($this->reward),
+ 'voucher' => new VoucherResource($this->voucher),
+ 'created_at' => $this->created_at
+ ];
+ }
+}
diff --git a/app/Http/Resources/VoucherRedemptionResource.php b/app/Http/Resources/VoucherRedemptionResource.php
new file mode 100644
index 00000000..16ae693f
--- /dev/null
+++ b/app/Http/Resources/VoucherRedemptionResource.php
@@ -0,0 +1,25 @@
+ $this->id,
+ 'voucher_id' => $this->voucher_id,
+ 'transaction_id' => $this->transaction_id,
+ 'redemption_id' => $this->redemption_id,
+ 'value' => (float) $this->value
+ ];
+ }
+}
diff --git a/app/Http/Resources/VoucherResource.php b/app/Http/Resources/VoucherResource.php
new file mode 100644
index 00000000..54193eeb
--- /dev/null
+++ b/app/Http/Resources/VoucherResource.php
@@ -0,0 +1,31 @@
+redemptions->filter(function ($redemption) {
+ return $redemption->transaction && $redemption->transaction->owner;
+ });
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'code' => $this->code,
+ 'type' => $this->type,
+ 'value' => (float) $this->value,
+ 'start_date' => $this->start_date,
+ 'end_date' => $this->end_date,
+ 'is_redeemed' => $filteredRedemptions->count() > 0
+ ];
+ }
+}
diff --git a/app/Http/Resources/WalletResource.php b/app/Http/Resources/WalletResource.php
index 143af1c4..4fa484ad 100644
--- a/app/Http/Resources/WalletResource.php
+++ b/app/Http/Resources/WalletResource.php
@@ -21,8 +21,8 @@ class WalletResource extends JsonResource
'currency_id' => $this->currency_id,
'amount' => (double) $this->amount,
'company_id' => (int) $this->owner->id,
- 'transactions' => WalletTransactionResource::collection($this->transactions()->whereIn('status', [2, 3])->orderBy('id', 'DESC')->get()),
- 'top_up_records' => WalletTransactionResource::collection($this->transactions()->whereNotIn('status', [0])->where('type', TransactionType::TOP_UP)->orderBy('id', 'DESC')->get())
+ 'transactions' => $this->whenLoaded('transactions', WalletTransactionResource::collection($this->transactions()->whereIn('status', [2, 3])->orderBy('id', 'DESC')->get()), []),
+ 'top_up_records' => $this->whenLoaded('transactions', WalletTransactionResource::collection($this->transactions()->whereNotIn('status', [0])->where('type', TransactionType::TOP_UP)->orderBy('id', 'DESC')->get()), []),
];
}
}
diff --git a/app/Http/Resources/WalletTransactionResource.php b/app/Http/Resources/WalletTransactionResource.php
index cfc8ab10..a0e1a930 100644
--- a/app/Http/Resources/WalletTransactionResource.php
+++ b/app/Http/Resources/WalletTransactionResource.php
@@ -7,6 +7,7 @@ use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Transaction;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
+use Illuminate\Support\Facades\Log;
class WalletTransactionResource extends JsonResource
{
@@ -27,7 +28,14 @@ class WalletTransactionResource extends JsonResource
$description = 'Credit Voucher for '.$this->payment_reference;
break;
case 1:
- $marking = Transaction::where('payment_reference', $this->bill_no)->first()->owner->marking;
+ $booking = Transaction::where('payment_reference', $this->bill_no)->first()->owner;
+
+ if(!$booking) {
+ $description = 'Payment for unknown booking, please contact tech support.';
+ break;
+ }
+
+ $marking = $booking->marking;
$description = 'Payment For booking refs.'.''.$marking.' ';
break;
case 11:
@@ -37,6 +45,7 @@ class WalletTransactionResource extends JsonResource
}
return [
+ 'id' => (int) $this->id,
'type' => (int) $this->type,
'marking' => $this->owner->owner->reference,
'bill_no' => $this->bill_no,
diff --git a/app/Models/AbstractModel.php b/app/Models/AbstractModel.php
index 7a7c9c06..47d8754b 100644
--- a/app/Models/AbstractModel.php
+++ b/app/Models/AbstractModel.php
@@ -3,11 +3,37 @@
namespace App\Models;
+use App\Classes\General\Interfaces\Notifiable;
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Traits\LogsActivity;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
-class AbstractModel extends Model
+class AbstractModel extends Model implements Notifiable
{
use LogsActivity;
protected static $logFillable = true;
+
+ /**
+ * @return MorphTo
+ */
+ public function subject(): MorphTo
+ {
+ return $this->MorphTo('subject');
+ }
+
+ /**
+ * @return MorphTo
+ */
+ public function target(): MorphTo
+ {
+ return $this->MorphTo('target');
+ }
+
+ /**
+ * @return MorphTo
+ */
+ public function causer(): MorphTo
+ {
+ return $this->MorphTo('causer');
+ }
}
\ No newline at end of file
diff --git a/app/Models/AccountStatement.php b/app/Models/AccountStatement.php
new file mode 100644
index 00000000..d9e53d60
--- /dev/null
+++ b/app/Models/AccountStatement.php
@@ -0,0 +1,35 @@
+ 'date',
+ 'date_to' => 'date',
+ ];
+
+ public function account()
+ {
+ return $this->belongsTo(StatementAccount::class, 'statement_account_id', 'id');
+ }
+
+ public function transactions()
+ {
+ return $this->hasMany(StatementTransaction::class);
+ }
+}
diff --git a/app/Models/BankLog.php b/app/Models/BankLog.php
new file mode 100644
index 00000000..a516d151
--- /dev/null
+++ b/app/Models/BankLog.php
@@ -0,0 +1,10 @@
+BelongsTo(Company::class, 'company_id');
+ return $this->BelongsTo(Company::class, 'company_id')->withTrashed();
}
/**
diff --git a/app/Models/Company.php b/app/Models/Company.php
index def72720..8b2e6081 100644
--- a/app/Models/Company.php
+++ b/app/Models/Company.php
@@ -62,6 +62,14 @@ class Company extends AbstractModel implements Documentable
return $this->belongsToMany(Segment::class, (new SegmentCompany())->getTable(), 'company_id', 'segment_id');
}
+ /**
+ * @return belongsToMany
+ */
+ public function seasonalSegments()
+ {
+ return $this->hasMany(SeasonalSegment::class);
+ }
+
/**
* @return belongsToMany
*/
diff --git a/app/Models/CurrencyRateLog.php b/app/Models/CurrencyRateLog.php
index 38130956..85b0eecf 100644
--- a/app/Models/CurrencyRateLog.php
+++ b/app/Models/CurrencyRateLog.php
@@ -1,10 +1,19 @@
BelongsTo(CurrencyRate::class, 'currency_rate_id', 'id');
+ }
}
diff --git a/app/Models/Employee.php b/app/Models/Employee.php
index 65dab67d..6d133186 100644
--- a/app/Models/Employee.php
+++ b/app/Models/Employee.php
@@ -31,4 +31,13 @@ class Employee extends AbstractModel
{
return $this->hasOne(User::class, 'user_id', 'id');
}
+
+ /**
+ * @return belongsToMany
+ */
+ public function milestones()
+ {
+ return $this->belongsToMany(Milestone::class, 'milestone_progress', 'user_id', 'milestone_id')
+ ->withPivot('created_at');
+ }
}
diff --git a/app/Models/Group.php b/app/Models/Group.php
new file mode 100644
index 00000000..01dd257e
--- /dev/null
+++ b/app/Models/Group.php
@@ -0,0 +1,63 @@
+belongsToMany(Transaction::class, GroupTransaction::class);
+ }
+
+ /**
+ * @return MorphMany
+ */
+ public function documents(): morphMany
+ {
+ return $this->morphMany(Document::class, 'owner');
+ }
+
+ /**
+ * @return BelongsTo
+ */
+ public function currency(): BelongsTo
+ {
+ return $this->BelongsTo(Currency::class, 'currency_id', 'id');
+ }
+
+
+ /**
+ * @return HasManyDeep
+ */
+ public function transferFees(): HasManyDeep
+ {
+ return $this->HasManyDeep(Transaction::class, [GroupTransaction::class, Transaction::class.' as alias'], ['group_id', ['owner_type', 'owner_id'], ['owner_type', 'owner_id']], ['id', null, null]);
+ }
+
+ /**
+ * @return BelongsTo
+ */
+ public function issuerCompany(): BelongsTo
+ {
+ return $this->BelongsTo( Company::class, 'issuer', 'id');
+ }
+
+ /**
+ * @return BelongsTo
+ */
+ public function original_currency(): BelongsTo
+ {
+ return $this->BelongsTo(Currency::class, 'original_currency_id', 'id');
+ }
+}
diff --git a/app/Models/GroupTransaction.php b/app/Models/GroupTransaction.php
new file mode 100644
index 00000000..9ab72d18
--- /dev/null
+++ b/app/Models/GroupTransaction.php
@@ -0,0 +1,27 @@
+BelongsTo(Group::class, 'group_id', 'id');
+ }
+
+ /**
+ * @return BelongsTo
+ */
+ public function transaction(): BelongsTo
+ {
+ return $this->BelongsTo(Transaction::class, 'transaction_id', 'id');
+ }
+}
diff --git a/app/Models/Milestone.php b/app/Models/Milestone.php
new file mode 100644
index 00000000..391410cc
--- /dev/null
+++ b/app/Models/Milestone.php
@@ -0,0 +1,22 @@
+hasMany(MilestoneProgress::class, 'milestone_id');
+ }
+
+ public function rewards()
+ {
+ return $this->belongsToMany(Reward::class, MilestoneReward::class);
+ }
+}
diff --git a/app/Models/MilestoneProgress.php b/app/Models/MilestoneProgress.php
new file mode 100644
index 00000000..7e03cba0
--- /dev/null
+++ b/app/Models/MilestoneProgress.php
@@ -0,0 +1,23 @@
+belongsTo(Milestone::class, 'milestone_id');
+ }
+
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'user_id');
+ }
+}
diff --git a/app/Models/MilestoneReward.php b/app/Models/MilestoneReward.php
new file mode 100644
index 00000000..1d5f04e3
--- /dev/null
+++ b/app/Models/MilestoneReward.php
@@ -0,0 +1,8 @@
+BelongsTo(Package::class, 'package_id', 'id');
+ }
+}
diff --git a/app/Models/Reward.php b/app/Models/Reward.php
new file mode 100644
index 00000000..7dbdc5a9
--- /dev/null
+++ b/app/Models/Reward.php
@@ -0,0 +1,27 @@
+belongsToMany(Milestone::class, MilestoneReward::class);
+ }
+
+ /**
+ * @return HasMany
+ */
+ public function users(): HasMany
+ {
+ return $this->HasMany(UserReward::class, 'reward_id', 'id');
+ }
+}
diff --git a/app/Models/SeasonalSegment.php b/app/Models/SeasonalSegment.php
new file mode 100644
index 00000000..30bb1796
--- /dev/null
+++ b/app/Models/SeasonalSegment.php
@@ -0,0 +1,48 @@
+BelongsTo(Company::class, 'company_id', 'id');
+ }
+
+ public function segment()
+ {
+ return $this->belongsTo(Segment::class);
+ }
+}
diff --git a/app/Models/StatementAccount.php b/app/Models/StatementAccount.php
new file mode 100644
index 00000000..741d2c5f
--- /dev/null
+++ b/app/Models/StatementAccount.php
@@ -0,0 +1,23 @@
+hasMany(AccountStatement::class);
+ }
+}
diff --git a/app/Models/StatementTransaction.php b/app/Models/StatementTransaction.php
new file mode 100644
index 00000000..80d6759f
--- /dev/null
+++ b/app/Models/StatementTransaction.php
@@ -0,0 +1,50 @@
+ 'datetime',
+ ];
+
+ public function account()
+ {
+ return $this->hasOneDeep(StatementAccount::class, [AccountStatement::class], ['id', 'id'], ['account_statement_id', 'statement_account_id']);
+ }
+
+ public function statement()
+ {
+ return $this->belongsTo(AccountStatement::class, 'account_statement_id', 'id');
+ }
+
+ public function owners()
+ {
+ return $this->hasMany(StatementTransactionOwner::class);
+ }
+}
diff --git a/app/Models/StatementTransactionOwner.php b/app/Models/StatementTransactionOwner.php
new file mode 100644
index 00000000..52e8d1fc
--- /dev/null
+++ b/app/Models/StatementTransactionOwner.php
@@ -0,0 +1,29 @@
+belongsTo(StatementTransaction::class, 'statement_transaction_id', 'id');
+ }
+}
diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php
index 429449a6..05fbfde7 100644
--- a/app/Models/Transaction.php
+++ b/app/Models/Transaction.php
@@ -4,6 +4,8 @@ namespace App\Models;
use App\Classes\General\Interfaces\Documentable;
use App\Classes\General\Interfaces\Transactionable;
+use App\Classes\General\Interfaces\Voucherifiable;
+use App\Classes\General\Traits\LogData;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use Carbon\Carbon;
@@ -18,10 +20,15 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Staudenmeir\EloquentHasManyDeep\HasTableAlias;
-class Transaction extends AbstractModel implements Documentable, Transactionable
+class Transaction extends AbstractModel implements Documentable, Transactionable, Voucherifiable
{
use HasTableAlias;
use SoftDeletes;
+ use LogData;
+
+ protected $casts = [
+ 'type' => 'int'
+ ];
protected $table = 'transactions';
@@ -102,6 +109,22 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
return $this->HasMany(TransactionDetail::class, 'transaction_id', 'id');
}
+ /**
+ * @return HasOne
+ */
+ public function groupTransaction(): HasOne
+ {
+ return $this->HasOne(GroupTransaction::class, 'transaction_id');
+ }
+
+ /**
+ * @return HasOne
+ */
+ public function voucherRedemption(): HasOne
+ {
+ return $this->HasOne(VoucherRedemption::class, 'transaction_id', 'id');
+ }
+
public function convert_original_amount()
{
if($this->booking()->first()->fix_currency_id !== 1) {
@@ -178,4 +201,13 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
{
return $query->whereIn('status', [ApprovalStatus::APPROVED]);
}
+
+ /**
+ * @return MorphMany
+ */
+ public function voucherifyEntities(): MorphMany
+ {
+ return $this->morphMany(VoucherEntityMapping::class, 'owner');
+ }
+
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 6fd8b6b0..4bc7ec17 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -2,8 +2,11 @@
namespace App\Models;
+use App\Classes\General\Interfaces\Voucherifiable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Database\Eloquent\Relations\MorphMany;
+use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Permission\Traits\HasRoles;
@@ -22,7 +25,8 @@ class User extends AbstractModel implements
JWTSubject,
AuthenticatableContract,
AuthorizableContract,
- CanResetPasswordContract
+ CanResetPasswordContract,
+ Voucherifiable
{
use HasRoles, Notifiable, Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail, SoftDeletes;
@@ -65,4 +69,36 @@ class User extends AbstractModel implements
{
return $this->belongsToMany(Company::class, (new Employee())->getTable(), 'user_id', 'company_id');
}
+
+ // /**
+ // * @return HasMany
+ // */
+ // public function redemptions(): HasMany
+ // {
+ // return $this->HasMany(VoucherRedemption::class, 'user_id', 'id');
+ // }
+
+ /**
+ * @return HasMany
+ */
+ public function milestoneProgress(): HasMany
+ {
+ return $this->HasMany(MilestoneProgress::class, 'user_id', 'id');
+ }
+
+ /**
+ * @return MorphMany
+ */
+ public function voucherifyEntities(): MorphMany
+ {
+ return $this->morphMany(VoucherEntityMapping::class, 'owner');
+ }
+
+ /**
+ * @return HasMany
+ */
+ public function rewards(): HasMany
+ {
+ return $this->HasMany(UserReward::class, 'user_id', 'id');
+ }
}
diff --git a/app/Models/UserReward.php b/app/Models/UserReward.php
new file mode 100644
index 00000000..40309447
--- /dev/null
+++ b/app/Models/UserReward.php
@@ -0,0 +1,28 @@
+belongsTo(Reward::class, 'reward_id');
+ }
+
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'user_id');
+ }
+
+ public function voucher()
+ {
+ return $this->belongsTo(Voucher::class, 'voucher_id');
+ }
+}
diff --git a/app/Models/Voucher.php b/app/Models/Voucher.php
new file mode 100644
index 00000000..b8df7dca
--- /dev/null
+++ b/app/Models/Voucher.php
@@ -0,0 +1,19 @@
+HasMany(VoucherRedemption::class, 'voucher_id', 'id');
+ }
+}
diff --git a/app/Models/VoucherEntityMapping.php b/app/Models/VoucherEntityMapping.php
new file mode 100644
index 00000000..2df0eacd
--- /dev/null
+++ b/app/Models/VoucherEntityMapping.php
@@ -0,0 +1,23 @@
+morphTo();
+ }
+
+}
diff --git a/app/Models/VoucherRedemption.php b/app/Models/VoucherRedemption.php
new file mode 100644
index 00000000..aa37487b
--- /dev/null
+++ b/app/Models/VoucherRedemption.php
@@ -0,0 +1,36 @@
+BelongsTo(Voucher::class, 'voucher_id', 'id');
+ }
+
+ /**
+ * @return BelongsTo
+ */
+ public function transaction(): BelongsTo
+ {
+ return $this->BelongsTo(Transaction::class, 'transaction_id', 'id');
+ }
+
+ /**
+ * @return BelongsTo
+ */
+ public function user(): BelongsTo
+ {
+ return $this->BelongsTo(User::class, 'user_id', 'id');
+ }
+}
diff --git a/app/Models/Wallet.php b/app/Models/Wallet.php
index 8c3114fd..00677513 100644
--- a/app/Models/Wallet.php
+++ b/app/Models/Wallet.php
@@ -3,6 +3,7 @@
namespace App\Models;
use App\Classes\General\Interfaces\Transactionable;
+use App\Classes\General\Traits\LogData;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -12,6 +13,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
class Wallet extends AbstractModel implements Transactionable
{
use SoftDeletes;
+ use LogData;
protected $table = 'wallets';
/**
diff --git a/composer.json b/composer.json
index 32f52d83..b8b505d5 100644
--- a/composer.json
+++ b/composer.json
@@ -10,30 +10,37 @@
"require": {
"php": "^7.2.5",
"ext-fileinfo": "*",
- "ext-json": "^1.6",
+ "ext-json": "*",
"ext-zip": "*",
"barryvdh/laravel-dompdf": "^0.9.0",
"carlos-meneses/laravel-mpdf": "^2.1",
"doctrine/dbal": "^2.12.1",
"fideloper/proxy": "^4.2",
"fruitcake/laravel-cors": "^1.0",
- "guzzlehttp/guzzle": "^6.3",
+ "guzzlehttp/guzzle": "^7.0.1",
"intervention/image": "^2.5",
- "laravel/framework": "^7.0",
+ "laravel/framework": "^8.0",
"laravel/tinker": "^2.0",
"maatwebsite/excel": "^3.1",
+ "mpdf/mpdf": "^8.1",
"rinvex/countries": "^6.1",
+ "rspective/voucherify": " v2.0.*",
+ "smalot/pdfparser": "^2.2",
"spatie/laravel-activitylog": "^3.14",
"spatie/laravel-permission": "^3.17",
"staudenmeir/eloquent-has-many-deep": "^1.7",
- "tymon/jwt-auth": "^1.0"
+ "timehunter/laravel-google-recaptcha-v3": "~2.5",
+ "tymon/jwt-auth": "^1.0",
+ "webklex/laravel-pdfmerger": "^1.3",
+ "ext-bcmath": "*"
},
"require-dev": {
- "facade/ignition": "^2.0",
+ "facade/ignition": "^2.3.6",
"fzaninotto/faker": "^1.9.1",
+ "laravel/dusk": "^6.23",
"mockery/mockery": "^1.3.1",
- "nunomaduro/collision": "^4.1",
- "phpunit/phpunit": "^8.5"
+ "nunomaduro/collision": "^5.0",
+ "phpunit/phpunit": "^9.0"
},
"config": {
"optimize-autoloader": true,
@@ -52,6 +59,9 @@
"classmap": [
"database/seeds",
"database/factories"
+ ],
+ "files": [
+ "app/Classes/General/VoucherifyHelper.php"
]
},
"autoload-dev": {
diff --git a/config/app.php b/config/app.php
index c080002d..fd0bbe8f 100644
--- a/config/app.php
+++ b/config/app.php
@@ -178,7 +178,9 @@ return [
// Third Parties
Spatie\Permission\PermissionServiceProvider::class,
Barryvdh\DomPDF\ServiceProvider::class,
- Meneses\LaravelMpdf\LaravelMpdfServiceProvider::class
+ Mccarlosen\LaravelMpdf\LaravelMpdfServiceProvider::class,
+ TimeHunter\LaravelGoogleReCaptchaV3\Providers\GoogleReCaptchaV3ServiceProvider::class,
+ Webklex\PDFMerger\Providers\PDFMergerServiceProvider::class
],
@@ -232,7 +234,9 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'PDF' => Barryvdh\DomPDF\Facade::class,
- 'MPDF' => Meneses\LaravelMpdf\Facades\LaravelMpdf::class,
+ 'MPDF' => Mccarlosen\LaravelMpdf\Facades\LaravelMpdf::class,
+ 'GoogleReCaptchaV3'=> TimeHunter\LaravelGoogleReCaptchaV3\Facades\GoogleReCaptchaV3::class,
+ 'PDFMerger' => Webklex\PDFMerger\Facades\PDFMergerFacade::class
],
diff --git a/config/database.php b/config/database.php
index aeabec6f..ac0b718b 100644
--- a/config/database.php
+++ b/config/database.php
@@ -63,6 +63,26 @@ return [
]) : [],
],
+ 'dusk' => [
+ 'driver' => 'mysql',
+ 'url' => env('DATABASE_URL'),
+ 'host' => env('DB_HOST', '127.0.0.1'),
+ 'port' => env('DB_PORT', '3306'),
+ 'database' => env('DB_DATABASE', 'forge'),
+ 'username' => env('DB_USERNAME', 'forge'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'unix_socket' => env('DB_SOCKET', ''),
+ 'charset' => 'utf8mb4',
+ 'collation' => 'utf8mb4_unicode_ci',
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'strict' => false,
+ 'engine' => null,
+ 'options' => extension_loaded('pdo_mysql') ? array_filter([
+ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
+ ]) : [],
+ ],
+
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
diff --git a/config/googlerecaptchav3.php b/config/googlerecaptchav3.php
new file mode 100644
index 00000000..29f3b1aa
--- /dev/null
+++ b/config/googlerecaptchav3.php
@@ -0,0 +1,166 @@
+ 'curl',
+ /*
+ |--------------------------------------------------------------------------
+ | Enable/Disable Service
+ |--------------------------------------------------------------------------
+ | Type: bool
+ |
+ | This option is used to disable/enable the service
+ |
+ | Supported: true, false
+ |
+ */
+ 'is_service_enabled' => true,
+ /*
+ |--------------------------------------------------------------------------
+ | Host Name
+ |--------------------------------------------------------------------------
+ | Type: string
+ | Default will be empty, assign value only if you want domain check with Google response
+ | Google reCAPTCHA host name, https://www.google.com/recaptcha/admin
+ |
+ */
+ 'host_name' => '',
+ /*
+ |--------------------------------------------------------------------------
+ | Secret Key
+ |--------------------------------------------------------------------------
+ | Type: string
+ | Google reCAPTCHA credentials, https://www.google.com/recaptcha/admin
+ |
+ */
+ 'secret_key' => env('RECAPTCHA_V3_SECRET_KEY', ''),
+ /*
+ |--------------------------------------------------------------------------
+ | Site Key
+ |--------------------------------------------------------------------------
+ | Type: string
+ | Google reCAPTCHA credentials, https://www.google.com/recaptcha/admin
+ |
+ */
+ 'site_key' => env('RECAPTCHA_V3_SITE_KEY', ''),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Badge Style
+ |--------------------------------------------------------------------------
+ | Type: boolean
+ | Support:
+ | - true: the badge will be shown inline within the form, also you can customise your style
+ | - false: the badge will be shown in the bottom right side
+ |
+ */
+ 'inline' => false,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Background Badge Style
+ |--------------------------------------------------------------------------
+ | Type: boolean
+ | Support:
+ | - true: the background badge will be displayed at the bottom right of page
+ | - false: the background badge will be invisible
+ |
+ */
+ 'background_badge_display' => false,
+ /*
+ |--------------------------------------------------------------------------
+ | Background Mode
+ |--------------------------------------------------------------------------
+ | Type: boolean
+ | Support:
+ | - true: the script will run on every page if you put init() on the global page
+ | - false: the script will only be running if there is action defined
+ |
+ */
+ 'background_mode' => true,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Score Comparision
+ |--------------------------------------------------------------------------
+ | Type: bool
+ | If you enable it, the package will do score comparision from your setting
+ */
+ 'is_score_enabled' => true,
+ /*
+ |--------------------------------------------------------------------------
+ | Setting
+ |--------------------------------------------------------------------------
+ | Type: array
+ | Define your score threshold, define your action
+ | action: Google reCAPTCHA required parameter
+ | threshold: score threshold
+ | score_comparison: true/false, if this is true, the system will do score comparision against your threshold for the action
+ */
+ 'setting' => [
+ [
+ 'action' => 'login',
+ 'threshold' => 0.6,
+ 'score_comparison' => true,
+ ],
+ ],
+
+ /*
+ |--------------------------------------------------------------------------
+ | Setting
+ |--------------------------------------------------------------------------
+ | Type: array
+ | Define a list of ip that you want to skip
+ */
+ 'skip_ips' => [
+
+ ],
+ /*
+ |--------------------------------------------------------------------------
+ | Options
+ |--------------------------------------------------------------------------
+ | Custom option field for your request setting, which will be used for RequestClientInterface
+ |
+ */
+ 'options' => [
+
+ ],
+ /*
+ |--------------------------------------------------------------------------
+ | API JS Url
+ |--------------------------------------------------------------------------
+ | Type: string
+ | Google reCAPTCHA API JS URL
+ | use:
+ */
+ 'api_js_url' => 'https://www.google.com/recaptcha/api.js',
+ /*
+ |--------------------------------------------------------------------------
+ | Site Verify Url
+ |--------------------------------------------------------------------------
+ | Type: string
+ | Google reCAPTCHA API
+ | please use "www.recaptcha.net" in your code in circumstances when "www.google.com" is not accessible. e.g China
+ | e.g. https://www.recaptcha.net/recaptcha/api.js
+ */
+ 'site_verify_url' => 'https://www.google.com/recaptcha/api/siteverify',
+
+ /*
+ |--------------------------------------------------------------------------
+ | Language
+ |--------------------------------------------------------------------------
+ | Type: string
+ | https://developers.google.com/recaptcha/docs/language
+ */
+ 'language' => 'en',
+];
diff --git a/config/perfexcrm.php b/config/perfexcrm.php
new file mode 100644
index 00000000..7644a489
--- /dev/null
+++ b/config/perfexcrm.php
@@ -0,0 +1,7 @@
+ env('PERFEXCRM_BASE_URL', 'http://192.168.1.101:8084'), //cief todo: Update crm api domain here
+ 'api_key' => env('PERFEXCRM_API_KEY', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZXhjaGFuZ2Utc2hpcHBpbmciLCJuYW1lIjoiRXhjaGFuZ2UgYW5kIFNoaXBwaW5nIFBvcnRhbCIsIkFQSV9USU1FIjoxNjc1MDg2Mzc4fQ.SGAHWl5stcxQwp55TBGeMRVTdlLeWQIbsvJh5glyVvs'),
+ 'is_enabled' => env('PERFEXCRM_IS_ENABLED', 'true'),
+];
diff --git a/config/voucherify.php b/config/voucherify.php
new file mode 100644
index 00000000..c97fab5b
--- /dev/null
+++ b/config/voucherify.php
@@ -0,0 +1,8 @@
+ env('VOUCHERIFY_APPLICATION_ID', ''),
+ 'client_secret_key' => env('VOUCHERIFY_CLIENT_SECRET_KEY', ''),
+ 'version' => env('VOUCHERIFY_VERSION', ''),
+ 'url' => env('VOUCHERIFY_URL', ''),
+];
diff --git a/database/migrations/2020_11_29_212614_create_companies_wallet_table.php b/database/migrations/2020_11_29_212614_create_companies_wallet_table.php
index 7a7ade13..3837a0e1 100644
--- a/database/migrations/2020_11_29_212614_create_companies_wallet_table.php
+++ b/database/migrations/2020_11_29_212614_create_companies_wallet_table.php
@@ -15,15 +15,13 @@ class CreateCompaniesWalletTable extends Migration
{
Schema::create('wallets', function (Blueprint $table) {
$table->id();
-
- $table->foreignId('company_id')->unsigned();
+ $table->morphs('owner');
$table->string('code');
$table->foreignId('currency_id')->unsigned();
$table->decimal('amount', 20, 5)->default(0.00);
$table->softDeletes();
$table->timestamps();
- $table->foreign('company_id')->references('id')->on('companies');
$table->foreign('currency_id')->references('id')->on('currencies');
});
}
diff --git a/database/migrations/2020_12_01_102314_create_transactions_table.php b/database/migrations/2020_12_01_102314_create_transactions_table.php
index a07bf413..366b9a27 100644
--- a/database/migrations/2020_12_01_102314_create_transactions_table.php
+++ b/database/migrations/2020_12_01_102314_create_transactions_table.php
@@ -17,7 +17,7 @@ class CreateTransactionsTable extends Migration
{
Schema::create('transactions', function (Blueprint $table) {
$table->id();
- $table->foreignId('booking_id')->unsigned();
+ $table->morphs('owner');
$table->string('type')->default(TransactionType::PAYMENT);
$table->foreignId('issuer')->unsigned();
$table->foreignId('receiver')->unsigned();
@@ -37,13 +37,12 @@ class CreateTransactionsTable extends Migration
$table->softDeletes();
$table->timestamps();
- $table->foreign('booking_id')->references('id')->on('bookings');
$table->foreign('currency_id')->references('id')->on('currencies');
$table->foreign('issuer')->references('id')->on('companies');
$table->foreign('receiver')->references('id')->on('companies');
$table->foreign('recipient_bank_account_id')->references('id')->on('banks');
$table->foreign('original_currency_id')->references('id')->on('currencies');
-
+
});
}
diff --git a/database/migrations/2020_12_01_212314_create_wallet_transaction_table.php b/database/migrations/2020_12_01_212314_create_wallet_transaction_table.php
deleted file mode 100644
index f87780c7..00000000
--- a/database/migrations/2020_12_01_212314_create_wallet_transaction_table.php
+++ /dev/null
@@ -1,51 +0,0 @@
-id();
-
- $table->foreignId('wallet_id')->unsigned();
- $table->foreignId('transaction_id')->unsigned()->nullable();
- $table->foreignId('bill_no')->unsigned();
- $table->integer('type')->default(TransactionType::PAYMENT);
- $table->decimal('amount', 14, 5)->default(0.00);
- $table->foreignId('currency_id')->unsigned();
- $table->decimal('original_amount', 14, 5)->default(0.00);
- $table->foreignId('original_currency_id')->unsigned();
- $table->decimal('currency_rate', 14, 5)->default(0.00);
- $table->integer('status')->default(ApprovalStatus::PENDING_SUBMISSION);
- $table->softDeletes();
- $table->timestamps();
-
- $table->foreign('transaction_id')->references('id')->on('transactions');
- $table->foreign('wallet_id')->references('id')->on('wallets');
- $table->foreign('currency_id')->references('id')->on('currencies');
- $table->foreign('original_currency_id')->references('id')->on('currencies');
-
- });
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- Schema::dropIfExists('wallet_transaction');
- }
-}
diff --git a/database/migrations/2020_12_02_131123_create_receipt_details_table.php b/database/migrations/2020_12_02_131123_create_receipt_details_table.php
deleted file mode 100644
index 0a008293..00000000
--- a/database/migrations/2020_12_02_131123_create_receipt_details_table.php
+++ /dev/null
@@ -1,38 +0,0 @@
-id();
- $table->foreignId('receipt_id')->unsigned();
- $table->decimal('price', 14, 5)->default(0.00);
- $table->decimal('amount', 14, 5)->default(0.00);
- $table->timestamps();
-
- $table->foreign('receipt_id')->references('id')->on('receipts');
-
- });
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- Schema::dropIfExists('receipt_detail');
- }
-}
diff --git a/database/migrations/2021_10_02_082442_alter_wallet_company_id.php b/database/migrations/2021_10_02_082442_alter_wallet_company_id.php
deleted file mode 100644
index c1eba98a..00000000
--- a/database/migrations/2021_10_02_082442_alter_wallet_company_id.php
+++ /dev/null
@@ -1,44 +0,0 @@
-dropForeign('wallets_company_id_foreign');
- $table->dropColumn('company_id');
- });
- }
-
- if (!Schema::hasColumn('wallets', 'owner_id')) {
- Schema::table('wallets', function (Blueprint $table) {
- $table->morphs('owner');
- });
-
- //In-case the model name lengthy
- Schema::table('wallets', function (Blueprint $table) {
- $table->string('owner_type', 250)->change();
- });
- }
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- //
- }
-}
diff --git a/database/migrations/2021_10_02_083144_alter_transaction_booking_id.php b/database/migrations/2021_10_02_083144_alter_transaction_booking_id.php
deleted file mode 100644
index a37da4e4..00000000
--- a/database/migrations/2021_10_02_083144_alter_transaction_booking_id.php
+++ /dev/null
@@ -1,45 +0,0 @@
-morphs('owner');
- });
-
- //In-case the model name lengthy
- Schema::table('transactions', function (Blueprint $table) {
- $table->string('owner_type', 250)->change();
- });
-
- DB::statement("UPDATE transactions SET owner_type='App\\\\Models\\\\Booking', owner_id = booking_id");
-
- Schema::table('transactions', function (Blueprint $table) {
- $table->dropForeign('transactions_booking_id_foreign');
- $table->dropColumn('booking_id');
- });
- }
- }
-
- /**
- * Reverse the migrations.
- *
- * @return void
- */
- public function down()
- {
- //
- }
-}
diff --git a/database/migrations/2020_12_02_131122_create_receipts_table.php b/database/migrations/2022_03_20_170628_create_groups_table.php
similarity index 57%
rename from database/migrations/2020_12_02_131122_create_receipts_table.php
rename to database/migrations/2022_03_20_170628_create_groups_table.php
index 16ec2684..569627c2 100644
--- a/database/migrations/2020_12_02_131122_create_receipts_table.php
+++ b/database/migrations/2022_03_20_170628_create_groups_table.php
@@ -5,7 +5,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class CreateReceiptsTable extends Migration
+class CreateGroupsTable extends Migration
{
/**
* Run the migrations.
@@ -14,10 +14,11 @@ class CreateReceiptsTable extends Migration
*/
public function up()
{
- Schema::create('receipts', function (Blueprint $table) {
+ Schema::create('groups', function (Blueprint $table) {
$table->id();
- $table->foreignId('transaction_id')->unsigned();
- $table->string('bill_no')->unique();
+ $table->string('reference')->unique();
+ $table->foreignId('issuer')->unsigned();
+ $table->foreignId('receiver')->unsigned();
$table->decimal('amount', 14, 5)->default(0.00);
$table->decimal('original_amount', 14, 5)->default(0.00);
$table->foreignId('currency_id')->unsigned();
@@ -25,17 +26,9 @@ class CreateReceiptsTable extends Migration
$table->decimal('currency_rate', 14, 5)->default(0.00);
$table->decimal('tax', 14, 5)->default(0.00);
$table->decimal('service_charge', 14, 5)->default(0.00);
- $table->timestamp('transaction_date')->useCurrent();
- $table->integer('status')->default(ApprovalStatus::COMPLETED);
- $table->softDeletes();
+ $table->integer('status')->default(ApprovalStatus::PENDING_VERIFICATION);
$table->timestamps();
-
- $table->foreign('transaction_id')->references('id')->on('transactions');
- $table->foreign('currency_id')->references('id')->on('currencies');
- $table->foreign('original_currency_id')->references('id')->on('currencies');
-
});
-
}
/**
@@ -45,6 +38,6 @@ class CreateReceiptsTable extends Migration
*/
public function down()
{
- Schema::dropIfExists('receipt');
+ Schema::dropIfExists('groups');
}
}
diff --git a/database/migrations/2021_10_02_082211_drop_wallet_transactions_table.php b/database/migrations/2022_03_20_170641_create_group_transactions_table.php
similarity index 51%
rename from database/migrations/2021_10_02_082211_drop_wallet_transactions_table.php
rename to database/migrations/2022_03_20_170641_create_group_transactions_table.php
index f16eeba3..53f33356 100644
--- a/database/migrations/2021_10_02_082211_drop_wallet_transactions_table.php
+++ b/database/migrations/2022_03_20_170641_create_group_transactions_table.php
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class DropWalletTransactionsTable extends Migration
+class CreateGroupTransactionsTable extends Migration
{
/**
* Run the migrations.
@@ -13,7 +13,11 @@ class DropWalletTransactionsTable extends Migration
*/
public function up()
{
- Schema::dropIfExists('wallet_transaction');
+ Schema::create('group_transactions', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('group_id')->unsigned();
+ $table->foreignId('transaction_id')->unsigned();
+ });
}
/**
@@ -23,6 +27,6 @@ class DropWalletTransactionsTable extends Migration
*/
public function down()
{
- //
+ Schema::dropIfExists('group_transactions');
}
}
diff --git a/database/migrations/2022_04_03_225513_create_bank_logs_table.php b/database/migrations/2022_04_03_225513_create_bank_logs_table.php
new file mode 100644
index 00000000..02fecbd3
--- /dev/null
+++ b/database/migrations/2022_04_03_225513_create_bank_logs_table.php
@@ -0,0 +1,51 @@
+id();
+ $table->foreignId('bank_id')->unsigned();
+ $table->foreignId('company_id')->unsigned();
+ $table->string('reference')->nullable();
+ $table->string('bank_name');
+ $table->string('holder_name');
+ $table->string('account_no');
+ $table->string('bank_branch')->nullable();
+ $table->string('swift')->nullable();
+ $table->string('snap')->nullable();
+ $table->integer('type')->default(BankAccountType::EXTERNAL);
+ $table->integer('default')->default(false);
+ $table->integer('status')->default(ApprovalStatus::APPROVED);
+ $table->foreignId('country_id')->unsigned();
+ $table->softDeletes();
+ $table->timestamps();
+
+ $table->foreign('bank_id')->references('id')->on('banks');
+ $table->foreign('country_id')->references('id')->on('countries');
+ $table->foreign('company_id')->references('id')->on('companies');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('bank_logs');
+ }
+}
diff --git a/database/migrations/2022_04_15_211421_create_notifications_table.php b/database/migrations/2022_04_15_211421_create_notifications_table.php
new file mode 100644
index 00000000..d14cdc89
--- /dev/null
+++ b/database/migrations/2022_04_15_211421_create_notifications_table.php
@@ -0,0 +1,40 @@
+id();
+ $table->string('title');
+ $table->text('description');
+ $table->morphs('subject');
+ $table->morphs('target');
+ $table->morphs('causer');
+ $table->integer('status')->default(ApprovalStatus::APPROVED);
+ $table->softDeletes();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('notifications');
+ }
+}
diff --git a/database/migrations/2023_03_16_223623_seasonal_segment_table.php b/database/migrations/2023_03_16_223623_seasonal_segment_table.php
new file mode 100644
index 00000000..4b804086
--- /dev/null
+++ b/database/migrations/2023_03_16_223623_seasonal_segment_table.php
@@ -0,0 +1,41 @@
+id();
+ $table->foreignId('company_id')->unsigned();
+ $table->string('status')->default(ApprovalStatus::APPROVED);
+ $table->foreignId('segment_id')->unsigned();
+ $table->timestamp('starting_on')->nullable();
+ $table->timestamp('ending_on')->nullable();
+ $table->softDeletes();
+ $table->timestamps();
+
+ $table->foreign('segment_id')->references('id')->on('segments');
+ $table->foreign('company_id')->references('id')->on('companies');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ //
+ }
+}
diff --git a/database/migrations/2023_03_26_190633_create_statement_accounts_table.php b/database/migrations/2023_03_26_190633_create_statement_accounts_table.php
new file mode 100644
index 00000000..348c5ec0
--- /dev/null
+++ b/database/migrations/2023_03_26_190633_create_statement_accounts_table.php
@@ -0,0 +1,36 @@
+id();
+ $table->string('number')->unique();
+ $table->string('type');
+ $table->string('name');
+ $table->string('currency');
+ $table->timestamps();
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('statement_accounts');
+ }
+}
diff --git a/database/migrations/2023_03_26_190827_create_account_statements_table.php b/database/migrations/2023_03_26_190827_create_account_statements_table.php
new file mode 100644
index 00000000..43945692
--- /dev/null
+++ b/database/migrations/2023_03_26_190827_create_account_statements_table.php
@@ -0,0 +1,39 @@
+id();
+ $table->unsignedBigInteger('statement_account_id');
+ $table->date('date_from');
+ $table->date('date_to');
+ $table->float('total_amount');
+ $table->float('begin_balance');
+ $table->float('end_balance');
+ $table->timestamps();
+
+ $table->foreign('statement_account_id')->references('id')->on('statement_accounts');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('account_statements');
+ }
+}
diff --git a/database/migrations/2023_03_26_190934_create_statement_transactions_table.php b/database/migrations/2023_03_26_190934_create_statement_transactions_table.php
new file mode 100644
index 00000000..29cacb80
--- /dev/null
+++ b/database/migrations/2023_03_26_190934_create_statement_transactions_table.php
@@ -0,0 +1,47 @@
+id();
+ $table->unsignedBigInteger('account_statement_id');
+ $table->dateTime('transaction_date')->nullable();
+ $table->dateTime('posting_date');
+ $table->string('transaction_description')->nullable();
+ $table->string('transaction_description_2')->nullable();
+ $table->string('transaction_description_3')->nullable();
+ $table->string('transaction_description_4')->nullable();
+ $table->string('transaction_description_5')->nullable();
+ $table->string('transaction_ref')->nullable();
+ $table->float('amount', 15, 2)->unsigned(false);
+ $table->string('teller_id')->nullable();
+ $table->string('branch_channel');
+ $table->string('transaction_code');
+ $table->string('end_balance')->nullable();
+ $table->timestamps();
+
+ $table->foreign('account_statement_id')->references('id')->on('account_statements');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('statement_transactions');
+ }
+}
diff --git a/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php b/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php
new file mode 100644
index 00000000..5d22c520
--- /dev/null
+++ b/database/migrations/2023_04_07_212512_create_statement_transaction_owners_table.php
@@ -0,0 +1,45 @@
+id();
+ $table->foreignId('statement_transaction_id');
+ $table->integer('type')->default(StatementTransactionOwnerType::UNKNOWN);
+ $table->string('system')->nullable();
+ $table->string('owner_type')->nullable();
+ $table->bigInteger('owner_id')->nullable();
+ $table->string('owner_reference')->nullable();
+ $table->string('invoice_reference')->nullable();
+ $table->string('receipt_reference')->nullable();
+ $table->string('is_auto_mapped')->default(false);
+ $table->integer('status')->default(ApprovalStatus::PENDING_VERIFICATION);
+
+ $table->foreign('statement_transaction_id')->references('id')->on('statement_transactions');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('statement_transaction_owners');
+ }
+}
diff --git a/database/migrations/2023_05_27_050522_create_vouchers_table.php b/database/migrations/2023_05_27_050522_create_vouchers_table.php
new file mode 100644
index 00000000..a769a699
--- /dev/null
+++ b/database/migrations/2023_05_27_050522_create_vouchers_table.php
@@ -0,0 +1,35 @@
+id();
+ $table->string('name')->nullable();
+ $table->string('code');
+ $table->string('type')->nullable();
+ $table->decimal('value', 8, 2)->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('vouchers');
+ }
+}
diff --git a/database/migrations/2023_05_27_050523_create_voucher_redemptions_table.php b/database/migrations/2023_05_27_050523_create_voucher_redemptions_table.php
new file mode 100644
index 00000000..7e46bc98
--- /dev/null
+++ b/database/migrations/2023_05_27_050523_create_voucher_redemptions_table.php
@@ -0,0 +1,41 @@
+id();
+ $table->unsignedBigInteger('voucher_id');
+ $table->unsignedBigInteger('transaction_id');
+ $table->string('redemption_id');
+ $table->decimal('value', 8, 2)->nullable();
+ // $table->unsignedBigInteger('user_id');
+ $table->timestamps();
+
+ // Define foreign key constraints
+ $table->foreign('voucher_id')->references('id')->on('vouchers');
+ $table->foreign('transaction_id')->references('id')->on('transactions');
+ // $table->foreign('user_id')->references('id')->on('users');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('voucher_redemptions');
+ }
+}
diff --git a/database/migrations/2023_06_12_191228_create_rewards_table.php b/database/migrations/2023_06_12_191228_create_rewards_table.php
new file mode 100644
index 00000000..04ea1133
--- /dev/null
+++ b/database/migrations/2023_06_12_191228_create_rewards_table.php
@@ -0,0 +1,38 @@
+id();
+ $table->string('name');
+ $table->text('description')->nullable();
+ $table->boolean('is_active')->default(true);
+ $table->integer('type')->default(0);
+ $table->string('value');
+ $table->integer('order')->default(9999);
+ $table->softDeletes();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('rewards');
+ }
+}
diff --git a/database/migrations/2023_06_13_062800_create_milestones_table.php b/database/migrations/2023_06_13_062800_create_milestones_table.php
new file mode 100644
index 00000000..8785e734
--- /dev/null
+++ b/database/migrations/2023_06_13_062800_create_milestones_table.php
@@ -0,0 +1,37 @@
+id();
+ $table->string('name');
+ $table->string('description');
+ // $table->unsignedBigInteger('reward_id');
+ $table->softDeletes();
+ $table->timestamps();
+
+ // $table->foreign('reward_id')->references('id')->on('rewards')->onDelete('cascade');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('milestones');
+ }
+}
diff --git a/database/migrations/2023_06_13_213411_create_milestone_progress_table.php b/database/migrations/2023_06_13_213411_create_milestone_progress_table.php
new file mode 100644
index 00000000..3476eb5a
--- /dev/null
+++ b/database/migrations/2023_06_13_213411_create_milestone_progress_table.php
@@ -0,0 +1,39 @@
+id();
+ // $table->unsignedBigInteger('company_id');
+ $table->unsignedBigInteger('user_id');
+ $table->unsignedBigInteger('milestone_id');
+ $table->softDeletes();
+ $table->timestamps();
+
+ // $table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade');
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ $table->foreign('milestone_id')->references('id')->on('milestones')->onDelete('cascade');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('milestone_progress');
+ }
+}
diff --git a/database/migrations/2023_06_20_192419_create_user_rewards_table.php b/database/migrations/2023_06_20_192419_create_user_rewards_table.php
new file mode 100644
index 00000000..42288a87
--- /dev/null
+++ b/database/migrations/2023_06_20_192419_create_user_rewards_table.php
@@ -0,0 +1,39 @@
+id();
+ $table->unsignedBigInteger('user_id');
+ $table->unsignedBigInteger('reward_id');
+ $table->unsignedBigInteger('voucher_id');
+ $table->softDeletes();
+ $table->timestamps();
+
+ $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ // $table->foreign('reward_id')->references('id')->on('rewards')->onDelete('cascade');
+ $table->foreign('voucher_id')->references('id')->on('vouchers')->onDelete('cascade');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('user_rewards');
+ }
+}
diff --git a/database/migrations/2023_07_02_210132_create_milestone_reward.php b/database/migrations/2023_07_02_210132_create_milestone_reward.php
new file mode 100644
index 00000000..2a00d7df
--- /dev/null
+++ b/database/migrations/2023_07_02_210132_create_milestone_reward.php
@@ -0,0 +1,35 @@
+unsignedBigInteger('milestone_id');
+ $table->unsignedBigInteger('reward_id');
+ // $table->timestamps();
+
+ $table->foreign('milestone_id')->references('id')->on('milestones')->onDelete('cascade');
+ $table->foreign('reward_id')->references('id')->on('rewards')->onDelete('cascade');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('milestone_reward');
+ }
+}
diff --git a/database/migrations/2023_07_05_081135_create_voucher_entity_mappings.php b/database/migrations/2023_07_05_081135_create_voucher_entity_mappings.php
new file mode 100644
index 00000000..5bae07b0
--- /dev/null
+++ b/database/migrations/2023_07_05_081135_create_voucher_entity_mappings.php
@@ -0,0 +1,36 @@
+id();
+ $table->string('owner_type');
+ $table->unsignedBigInteger('owner_id');
+ $table->string('voucherify_entity_type');
+ $table->string('voucherify_entity_id');
+ $table->softDeletes();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('voucher_entity_mappings');
+ }
+}
diff --git a/database/migrations/2023_07_08_155628_add_start_date_and_end_date_to_vouchers_table.php b/database/migrations/2023_07_08_155628_add_start_date_and_end_date_to_vouchers_table.php
new file mode 100644
index 00000000..a88e8e0d
--- /dev/null
+++ b/database/migrations/2023_07_08_155628_add_start_date_and_end_date_to_vouchers_table.php
@@ -0,0 +1,33 @@
+timestamp('start_date')->nullable();
+ $table->timestamp('end_date')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('vouchers', function (Blueprint $table) {
+ $table->dropColumn(['start_date', 'end_date']);
+ });
+ }
+}
diff --git a/database/seeds/AdminUserPermissionsTableSeeder.php b/database/seeds/AdminUserPermissionsTableSeeder.php
index 132184d7..0c3d5acf 100644
--- a/database/seeds/AdminUserPermissionsTableSeeder.php
+++ b/database/seeds/AdminUserPermissionsTableSeeder.php
@@ -19,7 +19,7 @@ class AdminUserPermissionsTableSeeder extends Seeder
app()['cache']->forget('spatie.permission.cache');
// admin permissions
- $permissions = [
+ $permissions = [
['name' => 'view document', 'guard_name' => 'web'],
['name' => 'add document', 'guard_name' => 'web'],
['name' => 'edit document', 'guard_name' => 'web'],
@@ -63,7 +63,14 @@ class AdminUserPermissionsTableSeeder extends Seeder
['name' => 'view booking', 'guard_name' => 'web'],
['name' => 'add booking', 'guard_name' => 'web'],
['name' => 'edit booking', 'guard_name' => 'web'],
- ['name' => 'delete booking', 'guard_name' => 'web']
+ ['name' => 'delete booking', 'guard_name' => 'web'],
+
+ ['name' => 'add milestone', 'guard_name' => 'web'],
+ ['name' => 'add reward', 'guard_name' => 'web'],
+ ['name' => 'edit milestone', 'guard_name' => 'web'],
+ ['name' => 'delete milestone', 'guard_name' => 'web'],
+ ['name' => 'delete reward', 'guard_name' => 'web'],
+
];
foreach ($permissions as $permission){
diff --git a/database/seeds/CompaniesTableSeeder.php b/database/seeds/CompaniesTableSeeder.php
index 5e665a9e..51122432 100644
--- a/database/seeds/CompaniesTableSeeder.php
+++ b/database/seeds/CompaniesTableSeeder.php
@@ -25,5 +25,15 @@ class CompaniesTableSeeder extends Seeder
$company->save();
+ $bank = new \App\Models\Bank();
+ $bank->company_id = $company->id;
+ $bank->bank_name = 'Maybank';
+ $bank->holder_name = 'CIEF Worldwide Snd Bhd';
+ $bank->account_no = '63465345345';
+ $bank->country_id = 1;
+ $bank->status = ApprovalStatus::APPROVED;
+ $bank->type = \App\Classes\ValueObjects\Constants\BankAccountType::PERSONAL;
+ $bank->save();
+
}
}
diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php
index a1928a7a..87d72f71 100644
--- a/database/seeds/DatabaseSeeder.php
+++ b/database/seeds/DatabaseSeeder.php
@@ -4,6 +4,7 @@ use Database\Seeders\BanksTableDevelopmentSeeder;
use Database\Seeders\CompaniesTableDevelopmentSeeder;
use Database\Seeders\CurrenciesTableDevelopmentSeeder;
use Database\Seeders\CurrencyRatesTableDevelopmentSeeder;
+use Database\Seeders\DummyDataSeeder;
use Database\Seeders\SegmentConstantsTableDevelopmentSeeder;
use Database\Seeders\SegmentsTableDevelopmentSeeder;
use Database\Seeders\ServiceTypesTableDevelopmentSeeder;
@@ -34,16 +35,17 @@ class DatabaseSeeder extends Seeder
$this->call(CompaniesTableSeeder::class);
// Admin
- $this->call(AdminUserTableSeeder::class);
+// $this->call(AdminUserTableSeeder::class);
$this->call(AdminUserPermissionsTableSeeder::class);
if(App()->environment('local')){
- $this->call(CompaniesTableDevelopmentSeeder::class);
- $this->call(BanksTableDevelopmentSeeder::class);
+// $this->call(CompaniesTableDevelopmentSeeder::class);
+// $this->call(BanksTableDevelopmentSeeder::class);
$this->call(ServiceTypesTableDevelopmentSeeder::class);
$this->call(SegmentsTableDevelopmentSeeder::class);
$this->call(SegmentConstantsTableDevelopmentSeeder::class);
$this->call(CurrencyRatesTableDevelopmentSeeder::class);
+ $this->call(DummyDataSeeder::class);
}
DB::commit();
diff --git a/database/seeds/DummyDataSeeder.php b/database/seeds/DummyDataSeeder.php
new file mode 100644
index 00000000..76378265
--- /dev/null
+++ b/database/seeds/DummyDataSeeder.php
@@ -0,0 +1,689 @@
+faker = $faker;
+ $this->createsUser = $createsUser;
+ $this->createsCompany = $createsCompany;
+ $this->createsContact = $createsContact;
+ $this->createsAddress = $createsAddress;
+ $this->assignEmployeeProcessor = $assignEmployeeProcessor;
+ $this->createsDocument = $createsDocument;
+ $this->createsFiles = $createsFiles;
+ $this->generatesWalletCode = $generatesWalletCode;
+ $this->createsWallet = $createsWallet;
+ $this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
+ $this->createsTransaction = $createsTransaction;
+ $this->updatesTransactionStatus = $updatesTransactionStatus;
+ $this->updatesWalletBalance = $updatesWalletBalance;
+ $this->createsBank = $createsBank;
+ $this->generatesBookingMarking = $generatesBookingMarking;
+ $this->createsBooking = $createsBooking;
+ $this->fetchBookingQuotation = $fetchBookingQuotation;
+ $this->approvesDocument = $approvesDocument;
+ $this->rejectsDocument = $rejectsDocument;
+ $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
+ $this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
+ $this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor;
+ $this->assignSegmentProcessor = $assignSegmentProcessor;
+ $this->setsBankToDefault = $setsBankToDefault;
+ }
+
+
+ /**
+ * Run the database seeds.
+ *
+ * @return void
+ * @throws AccessForbiddenException
+ * @throws MalformedRequestException
+ * @throws RequestValidationException
+ * @throws MpdfException
+ */
+ public function run()
+ {
+ // Local Development Default Password Hash
+ $password = '123456abcabc';
+
+ // At the moment we only have 3 different user roles:
+ // RoleTypes::SUPER_ADMIN : Full access, at the moment is not attached to a company but should be in the future.
+ // RoleTypes::ADMIN : Full access except for some sensitive features that require higher level of approval, at the moment is not attached to a company but should be in the future.
+ // RoleTypes::USER : This is the customer, can only access their own orders only, must be attached to a company.
+
+ // User Status
+ // ApprovalStatus::PENDING_VERIFICATION : This should be the default status before the user verifies their email status, but currently this is not being implemented.
+ // ApprovalStatus::APPROVED : This is the status of users with verified emails.
+ // ApprovalStatus::SUSPENDED : This is the status if the users is blocked from the system, but currently this is not being implemented.
+
+
+ // =============================================== //
+ // Create CIEF Entities //
+ // =============================================== //
+
+ // create super admin
+ $userObject = new RegistrationObject($this->faker->name, 'super_admin@exchange.com', $password, $password,RoleTypes::SUPER_ADMIN, ApprovalStatus::APPROVED);
+ $this->createsUser->execute($userObject);
+
+ Auth()->login(User::find(1), true);
+
+ // create admin
+ $userObject = new RegistrationObject($this->faker->name, 'admin@exchange.com', $password, $password,RoleTypes::ADMIN, ApprovalStatus::APPROVED);
+ $this->createsUser->execute($userObject);
+
+ // create CIEF
+ $company_object = new CompanyObject('CIEF Worldwide Sdn Bhd', 'CIEF',CompanyType::COMPANY_BUSINESS,ApprovalStatus::APPROVED);
+ /** @var Company $company */
+ $company = $this->createsCompany->execute($company_object);
+
+ // =============================================== //
+ // Create Supplier Entities //
+ // =============================================== //
+ // supplier entities consist of 2 type of company module [BusinessType::FREIGHT_FORWARDER, BusinessType::FREIGHT_FORWARDER, BusinessType::WAREHOUSE]
+ // in this use case we are creating 3 supplier, with each supplier having 6 company modules, 1 BusinessType::FREIGHT_FORWARDER and 5 BusinessType::WAREHOUSE. 1 warehouse for each location.
+
+ for ($i = 1; $i <= 3; $i++) {
+ $supplierName = $this->faker->company;
+ $supplierReference = $this->faker->bothify('??-????');
+
+ $company_object = new CompanyObject($supplierName, $supplierReference,BusinessType::CURRENCY_VENDOR, CompanyType::COMPANY_BUSINESS, ApprovalStatus::APPROVED);
+ /** @var Company $company */
+ $company = $this->createsCompany->execute($company_object);
+ $bank_object = new BankObject($company->id, $this->faker->numberBetween(2, 3),
+ $this->faker->company, $this->faker->name, $this->faker->bankAccountNumber,
+ $this->faker->city, null, null,
+ 2, $this->faker->company);
+
+ $bank = $this->createsBank->execute($bank_object);
+ $this->setsBankToDefault->execute($bank);
+
+ }
+
+
+ // =============================================== //
+ // Create Customer //
+ // =============================================== //
+ // 1. create user
+ // 2. create company
+
+ // 3. Attach Employee
+ // 4. create contact
+ //
+ // 5. create Address
+
+ // 6. identification verification
+
+ // =============================================== //
+ // Wallet //
+ // =============================================== //
+
+ // 7. top up wallet
+
+ // =============================================== //
+ // Order Workflow //
+ // =============================================== //
+
+ // 8. create recipient bank
+ // 9. create booking
+ // 10. make payment (Manual, FPX, Wallet)
+ // 11. approve payment (For manual payments only) * N
+ // 12. create supplier order
+ // 13. upload china payment proof (outsource * N)
+ // 14. create purchase order (maybe outsource)
+ // 15. approve purchase order ()
+ // 16. generate invoice
+
+ // generate random number of users
+ for($userLoop=1; $userLoop <= 20; $userLoop++) {
+
+ // === //
+ // 1 // ========== //
+ // Create user //
+ // ================= //
+ $customerName = $this->faker->name;
+ $customerEmail = $this->faker->email;
+ $userObject = new RegistrationObject($customerName, $customerEmail, $password, $password, RoleTypes::USER, ApprovalStatus::APPROVED);
+ /** @var User $user */
+ $user = $this->createsUser->execute($userObject);
+
+ // === //
+ // 2 // ========== //
+ // Create Company //
+ // ================= //
+
+ // company reference is called marking, it is the human readable id.
+
+ // CompanyTypes
+ // CompanyType::COMPANY_BUSINESS : For SME Business Entities and requires SSM for identity verification.
+ // CompanyType::PERSONAL_BUSINESS : For Personal Entities and requires IC for identity verification, and the company name will follow the customer name in this case.
+
+ // Company Status
+ // ApprovalStatus::APPROVED : This is the default status of registered company.
+ // ApprovalStatus::SUSPENDED : This is the status if the company is blocked from releasing packages from warehouse due to pending verification.
+
+ $isCompany = $this->faker->numberBetween(0, 1);
+ $companyName = $isCompany ? $this->faker->company : $customerName;
+
+ $company_object = new CompanyObject($companyName,
+ mt_rand(1000, 9999).(new GeneratesInitials())->name($companyName)->length(3)->generate(),
+ $isCompany ? CompanyType::COMPANY_BUSINESS : CompanyType::PERSONAL_BUSINESS,
+ ApprovalStatus::APPROVED);
+
+ /** @var Company $company */
+ $company = $this->createsCompany->execute($company_object);
+ $this->assignSegmentProcessor->execute($company);
+
+
+ // === //
+ // 3 // ===========//
+ // Attach Employee //
+ // ==================//
+ // employees are attached to company modules not companies, because an employee maybe working for one or many "Departments".
+ $Object = new EmploymentObject($company, $user);
+ $this->assignEmployeeProcessor->execute($Object);
+
+ // === //
+ // 4 // ========== //
+ // Create Contact //
+ // ================= //
+ // Contacts uses eloquent polymorphic relationship to declare its owner. and for this use case it will be attached to the company not the company module.
+ $contactObject = new ContactObject($company->id, $customerName, (int) $this->faker->randomNumber(7), $customerEmail, null, 1);
+ $this->createsContact->execute($contactObject);
+
+ // === //
+ // 5 // ========== //
+ // Create Address //
+ // ================= //
+ // Addresses uses eloquent polymorphic relationship to declare its owner. and for this use case it will be attached to the company module.
+ // an address has at least 1 contact for the PIC.
+ // there is 1 type of address we use:
+ // AddressType::BILLING : for the invoice billing address
+
+ // create delivery address
+ $addressObject = new AddressObject($this->faker->streetAddress, '', 1, $this->faker->numberBetween(1, 15), $this->faker->numberBetween(1, 100), $this->faker->postcode);
+ /** @var Address $address */
+ $address = $this->createsAddress->execute($company, $addressObject);
+
+ // === //
+ // 6 // ====================== //
+ // identification verification //
+ // ============================== //
+ // please refer to company types section for more insight
+ $object = new DocumentObject($isCompany ? DocumentType::SSM_REGISTRATION : DocumentType::IDENTITY_CARD, ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAANGklEQVR4nOzXDa/fdX3G8R44Ww54BIFV2wFyoxUoKmsFhA0zEGQj1jOMo5o5IQPmYE5wrSvjdhbHAGWt0BWEwmChuHEjSF2LrY6tlmFjJbblprQstD21UFzbrBhX1tKyR3ElJtfr9QCu78k/v5N3PoOzbv/SmKR/mv94dP+FN9dH95+7+J7o/sjse6P7y3d/Orq/6qKTovtLN94f3Z9w39nR/XeFv/+nrlse3V/xhbXR/Xuv/kx0f3TDjuj+oltviO7PHJf9/veJrgPwK0sAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQafOeyu6MPPP9by6L7f7n/yuj+rWN/Et3/xiu/Gd3/xQduju7ve+KE6P7wc6dG9yde/lx0f86J2e/nO38yLrq/ftXT0f0/m7wzuj9jw4vR/V2n74jub7v8zOi+CwCglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKDWw40dD0Qc2PP+O6P6cJauj+zP+dVl0//izT47uv+eazdH9bf+3I7r/xhFXR/dnzjwmun/lJ56N7n/vA/Oi+5NmnxDdf3jqtuj+8j0PRPfnzPtUdP/Jt++K7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSgwsfXhl94KuHrIjuHzvmxuj+g6+9EN1//Pz/jO5P/2x2f/dTL0X3f+e9S6L7f3zJHdH9jafOjO6/7/LDo/s3bL4+un/rnNOi+yMXDUX3Bw6YFt2/+IyTovsuAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACg1OCEe34UfWDn+SPR/bcWrY3u//DMbdH94w4diu4/sPTo6P7tNx0Z3V92wrzo/k8/uzu6f8DwndH9v5i1ILq/+UO3Rfd3Lz4nuv+VRx6K7s9Y+nJ0f+HKsdF9FwBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUGrw9+84JfrAof89P7r/wf8aG90/d8//RvdvXf/30f3JI8ui+8ceeUd0/+DPr4nuf3jX56L7p168Krr/rcfOiu6PHvhqdH/xLauj+8+Mbozur/vpj6P7E8edH913AQCUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQYXb9kafeDSf/lWdP/fdx4c3X9k60vR/b13Xxjdnzb1Z9H90ctWRfffmvRGdP+8e96M7m+avT26/45vnh3d/9rNY6P7Tz/5w+j+e6bMj+4f8rHro/vHH579/l0AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpwb037R994NK5a6P7/7zgvuj+JTNPiO7/7fKjo/t7H7wmuj/8+pnR/f2nbojuH33tndH9333536L7E25+Kro//NuTo/vLT1oe3f+jNZdG9y+ffHB0f972e6L7LgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoNTgYcd8LvrA9IGd0f3jh6+K7l8/d010f5/hrdH98euGovuf+OvbovsHnPpSdP/p6X8e3d8ztD26v+bJydH9GQfcG91/+MWPRve/vPSX0f2P/OmC6P7AdYdF910AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpgXMG3x59YPXKWdH9xc9+P7q/3/SjovvLjjs3uv83E4+N7p904Lej+9MXfCq6//ULvxvdf2D2KdH9afOuiu5PnPm26P7QM1Oy+yPZ7/PItdui+1v+Y1103wUAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQaeOKXH48+sGnNtOj+mGkrovNvm3tddH/MoWOj8y/cckx0/6D5743u/+ydI9H9O/d+JLr/yuwLovtbHhqI7r//wuz3+Z2PfTC6/5VXVkX3/+G+c6L7Xz9vRnTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBp4/ZC7og/84cLx0f1fX7gyuj/xy6PR/eueuDG6P/6rS6L7t//g8ej+Xa9Niu6/9sSM6P4bmx6L7t+14+Do/se/NBzd3/7Rq6L7a976dHR/xruy/1+jO/aN7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSg48/PyX6wBenDkX3Z607Lbo/d9f7o/tblmd//5u+Oyu6f/rP50X3B1/dEN2/5O6V0f0rLr0tur/omp9E918deT26/+Dpe6P7N179d9H9TdeORvd33XdOdN8FAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUGpgzcW/0gQ9PPyK6P/jtB6P7nzxrU3T/i/dvjO6vPuWQ6P6Z7/616P76T94Q3X/37qHo/rzfmx/dv2V8dv/+/S6L7j/6B0uj+wftuyS6v+KK7O+/++Xs3+8CACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKDTx24TeiDzz68MnR/UU7D4/uH/ah7P6PH5oT3T/viCuj+18b2RTd//7J+0f3L7nipuj+lAueie6PmXpUdH7mpDOi+y/ueiS6f9kP/iq6/+yEn0f3p1x0UXTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBo86urN0Qemjk6N7n9v+Nzo/uHXfj66P3fF1uj+Pvv9T3T/wBOj82MeOuvR6P64cVdE9/9x4KDo/m+cdmV0f8HSPdH9910wKbo//gv3Rve/+YszovufufO46L4LAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAo9f8BAAD//3aYjHM9JD/iAAAAAElFTkSuQmCC'],
+ $isCompany ? $this->faker->bothify('SSM-#######') : $this->faker->bothify('############'), ApprovalStatus::APPROVED, 'identifications');
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($company, $object);
+ $this->createsFiles->execute($document, $object);
+
+ // === //
+ // 7 // ========= //
+ // top up wallet //
+ // ================ //
+ // when a customer tries to top up their wallet, if the wallet doesn't already exist it will be automatically created.
+ // wallet credit can be used to pay for transfer orders to enjoy better conversion rates.
+ // wallet top-ups can only be performed using FPX at the moment. but super admin can manually credit or debit credit to a customer's wallet
+
+ // The transaction table is considered the most confusing part of our database because it is being used by multiple model using a polymorphic relationship
+ // and is used for many use cases in our application which is an unintended flaw, and we are looking for ways to improve it.
+
+
+ // A wallet top up is TransactionType::TOP_UP, there are many types of transactions used by a wallet:
+ // TransactionType::TOP_UP : represent a top-up amount to a wallet;
+ // TransactionType::PAYMENT : represent payment out of the wallet;
+ // TransactionType::CREDIT_NOTE : represent a manual top-up to a wallet, and can only be performed by super admin;
+ // TransactionType::DEBDIT_NOTE : represent a deduction from a wallet, and can only be performed by super admin;
+ // TransactionType::WITHDRAW : represent a customer withdrawing credit out of a wallet to a bank account (refund);
+
+
+ // top up only some customers
+ $shouldTopUp = $this->faker->numberBetween(0, 1);
+ if($shouldTopUp) {
+ $object = new WalletObject($company->id, 1, $this->generatesWalletCode->execute());
+ /** @var Wallet $wallet */
+ $wallet = $this->createsWallet->execute($object, $company);
+
+ $amount = $this->faker->numberBetween(10, 300000);
+ $billNumber = $this->generatesTransactionBillNumber->execute('TOPUP-');
+
+ // create billplz bill using api, we will skip this part in the seed.
+ $billPlzBill = $this->faker->bothify('???#####');
+
+ $transaction_object = new TransactionObject($billNumber, TransactionType::TOP_UP, 1, $company->id, 1, PaymentMethodType::PAYMENT_GATEWAY, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, [], $billPlzBill);
+
+ /** @var Transaction $transaction */
+ $transaction = $this->createsTransaction->execute($wallet, $transaction_object);
+
+
+ // on billplz callback url
+ $status = $this->faker->randomElement([ApprovalStatus::APPROVED, ApprovalStatus::REJECTED]);
+ $this->updatesTransactionStatus->execute($transaction, $status);
+ if($status === ApprovalStatus::APPROVED) {
+ $this->updatesWalletBalance->execute($wallet, $amount);
+ }
+ }
+
+ // === //
+ // 8 // ========================== //
+ // Create Recipient Bank Accounts //
+ // ================================= //
+ // bank accounts are used to store bank account details, and can be used in a variety of ways
+ // Bank types:
+ // 1. PERSONAL : belong to the same entity
+ // 2. EXTERNAL : Doesn't belong to the entity, belongs to an external entity;
+ // 3. ALIPAY : : Is an external entity, but flag the type of bank as alipay e-wallet;
+ //
+ // here are some of the current use cases for banks in our application:
+ // 1. Recipient bank (EXTERNAL) (the account the customer is requesting to transfer funds to)
+ // 2. AliPay Transfer (EXTERNAL) (the account the customer is requesting to transfer funds to when bank type is ALIPAY)
+ // 3. Refund bank (PERSONAL) (the account the customer is requesting his order refunds to be transferred to)
+ $bank_object = new BankObject($company->id, $this->faker->numberBetween(2, 3),
+ $this->faker->company, $this->faker->name, $this->faker->bankAccountNumber,
+ $this->faker->city, null, null,
+ 2, $this->faker->company);
+
+ // todo create multiple bank accounts with multiple types
+ $bank = $this->createsBank->execute($bank_object);
+
+ // generate random number of bookings
+ for($orderLoop=1; $orderLoop <= rand(1, 5); $orderLoop++) {
+ echo 'booking created';
+ // === //
+ // 9 // ========= //
+ // Create Booking //
+ // ================ //
+ // A booking is simply a transfer order to a supplier/manufacturer bank account overseas
+ // to pay for goods they are buying from overseas. the booking is not proceed until the
+ // customer requests to make a payment, when the customer start the payment process he
+ // will receive a quote for the cost to transfer the booked amount (e.g. 100 USD) in RM
+
+ // bookings require 2 actions from the customer to be completed
+ // 1. Make Full payment **
+ // 2. Provide Purchase Order (itemized list of the products they are buying)
+
+ // ** A booking will be the sum of payments transferred to one bank account
+ // but can be partially paid (e.g. 1000 USD can be paid: $300 deposit + $700 balance)
+ // A shipping label can be re-used, and each batch that arrives at the supplier warehouse
+ // is referred to as a packing list. more on this later.
+
+ // service types are configured by the super admin from the settings
+ // it will include things like conversion rates, service charge, etc..
+ // and can be used to place different type of transfer orders (e.g. 1 day transfer, 3 days transfer, 1688 Payment)
+
+ // randomly selects a service type
+ $service = ServiceType::inRandomOrder()->first();
+
+ // Booking human readable id
+ $reference = $this->generatesBookingMarking->execute();
+
+ // random currency booking (CNY, USD)
+ $bookedCurrency = 2;
+ $bank = $company->banks()->inRandomOrder()->first();
+ $object = new BookingObject($service->id, $bank->id, $reference, $this->faker->numberBetween(10, 300000), $bookedCurrency, $bookedCurrency, 1);
+ $booking = $this->createsBooking->execute($company, $object);
+
+
+ // === //
+ // 10 // ====== //
+ // make payment //
+ // ============== //
+ // There are few type of transactions related to a booking:
+ // TransactionType::PAYMENT : is used for 2 type of use cases (1. payments to transfer orders, 2. payment out of wallet) and is attached to a booking;
+ // TransactionType::BILL : is to represent the payment out to CIEF currency supplier (expenses) and is attached to a transaction of type TransactionType::PAYMENT;
+ // TransactionType::TRANSFER_FEE : is to represent the transfer fee charged by CIEF currency supplier is attached to a transaction type TransactionType::BILL;
+ // TransactionType::REFUND : is to represent a request for refund on a payment, and is attached to a transaction type TransactionType::PAYMENT;
+
+ $numberOfPayments = $this->faker->numberBetween(0, 3);
+
+ for ($paymentLoop=0; $paymentLoop <= $numberOfPayments; $paymentLoop++) {
+ echo 'payment created';
+ $shouldSubmit = $this->faker->numberBetween(0, 1);
+ $shouldApprove = $this->faker->numberBetween(0, 1);
+ if ($numberOfPayments > 1){
+
+ $amount = $booking->fix_amount / $numberOfPayments;
+
+ $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $amount)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS['cash']);
+
+ $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject);
+
+ $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-');
+
+ $object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id,
+ $configurations->getConfigurations()->getBankId(), $configurations->getConversionObject()->getPaymentMethod(),
+ $configurations->getTotal(), $configurations->getForeignTotal(), 1,
+ $configurations->getConversionObject()->getCurrencyId(), $configurations->getConfigurations()->getRate(),
+ $configurations->getTax(), $configurations->getServiceCharge(), Carbon::now()->addMinutes(10), ApprovalStatus::PENDING_SUBMISSION, [], null);
+
+ /** @var Transaction $transaction */
+ $transaction = $this->createsTransaction->execute($booking, $object);
+
+ if($shouldSubmit || $shouldApprove) {
+ $object = new DocumentObject( DocumentType::CUSTOMER_PAYMENT_PROOF, ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAANGklEQVR4nOzXDa/fdX3G8R44Ww54BIFV2wFyoxUoKmsFhA0zEGQj1jOMo5o5IQPmYE5wrSvjdhbHAGWt0BWEwmChuHEjSF2LrY6tlmFjJbblprQstD21UFzbrBhX1tKyR3ElJtfr9QCu78k/v5N3PoOzbv/SmKR/mv94dP+FN9dH95+7+J7o/sjse6P7y3d/Orq/6qKTovtLN94f3Z9w39nR/XeFv/+nrlse3V/xhbXR/Xuv/kx0f3TDjuj+oltviO7PHJf9/veJrgPwK0sAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQafOeyu6MPPP9by6L7f7n/yuj+rWN/Et3/xiu/Gd3/xQduju7ve+KE6P7wc6dG9yde/lx0f86J2e/nO38yLrq/ftXT0f0/m7wzuj9jw4vR/V2n74jub7v8zOi+CwCglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKDWw40dD0Qc2PP+O6P6cJauj+zP+dVl0//izT47uv+eazdH9bf+3I7r/xhFXR/dnzjwmun/lJ56N7n/vA/Oi+5NmnxDdf3jqtuj+8j0PRPfnzPtUdP/Jt++K7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSgwsfXhl94KuHrIjuHzvmxuj+g6+9EN1//Pz/jO5P/2x2f/dTL0X3f+e9S6L7f3zJHdH9jafOjO6/7/LDo/s3bL4+un/rnNOi+yMXDUX3Bw6YFt2/+IyTovsuAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACg1OCEe34UfWDn+SPR/bcWrY3u//DMbdH94w4diu4/sPTo6P7tNx0Z3V92wrzo/k8/uzu6f8DwndH9v5i1ILq/+UO3Rfd3Lz4nuv+VRx6K7s9Y+nJ0f+HKsdF9FwBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUGrw9+84JfrAof89P7r/wf8aG90/d8//RvdvXf/30f3JI8ui+8ceeUd0/+DPr4nuf3jX56L7p168Krr/rcfOiu6PHvhqdH/xLauj+8+Mbozur/vpj6P7E8edH913AQCUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQYXb9kafeDSf/lWdP/fdx4c3X9k60vR/b13Xxjdnzb1Z9H90ctWRfffmvRGdP+8e96M7m+avT26/45vnh3d/9rNY6P7Tz/5w+j+e6bMj+4f8rHro/vHH579/l0AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpwb037R994NK5a6P7/7zgvuj+JTNPiO7/7fKjo/t7H7wmuj/8+pnR/f2nbojuH33tndH9333536L7E25+Kro//NuTo/vLT1oe3f+jNZdG9y+ffHB0f972e6L7LgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoNTgYcd8LvrA9IGd0f3jh6+K7l8/d010f5/hrdH98euGovuf+OvbovsHnPpSdP/p6X8e3d8ztD26v+bJydH9GQfcG91/+MWPRve/vPSX0f2P/OmC6P7AdYdF910AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpgXMG3x59YPXKWdH9xc9+P7q/3/SjovvLjjs3uv83E4+N7p904Lej+9MXfCq6//ULvxvdf2D2KdH9afOuiu5PnPm26P7QM1Oy+yPZ7/PItdui+1v+Y1103wUAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQaeOKXH48+sGnNtOj+mGkrovNvm3tddH/MoWOj8y/cckx0/6D5743u/+ydI9H9O/d+JLr/yuwLovtbHhqI7r//wuz3+Z2PfTC6/5VXVkX3/+G+c6L7Xz9vRnTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBp4/ZC7og/84cLx0f1fX7gyuj/xy6PR/eueuDG6P/6rS6L7t//g8ej+Xa9Niu6/9sSM6P4bmx6L7t+14+Do/se/NBzd3/7Rq6L7a976dHR/xruy/1+jO/aN7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSg48/PyX6wBenDkX3Z607Lbo/d9f7o/tblmd//5u+Oyu6f/rP50X3B1/dEN2/5O6V0f0rLr0tur/omp9E918deT26/+Dpe6P7N179d9H9TdeORvd33XdOdN8FAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUGpgzcW/0gQ9PPyK6P/jtB6P7nzxrU3T/i/dvjO6vPuWQ6P6Z7/616P76T94Q3X/37qHo/rzfmx/dv2V8dv/+/S6L7j/6B0uj+wftuyS6v+KK7O+/++Xs3+8CACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKDTx24TeiDzz68MnR/UU7D4/uH/ah7P6PH5oT3T/viCuj+18b2RTd//7J+0f3L7nipuj+lAueie6PmXpUdH7mpDOi+y/ueiS6f9kP/iq6/+yEn0f3p1x0UXTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBo86urN0Qemjk6N7n9v+Nzo/uHXfj66P3fF1uj+Pvv9T3T/wBOj82MeOuvR6P64cVdE9/9x4KDo/m+cdmV0f8HSPdH9910wKbo//gv3Rve/+YszovufufO46L4LAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAo9f8BAAD//3aYjHM9JD/iAAAAAElFTkSuQmCC'],
+ '', ApprovalStatus::PENDING_VERIFICATION, 'payments');
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($transaction, $object);
+ $this->createsFiles->execute($document, $object);
+ $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::PENDING_SUBMISSION);
+ }
+
+ if($shouldApprove) {
+ $this->approvesDocument->execute($document);
+ $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED);
+
+ $shouldReject = $this->faker->numberBetween(0, 1);
+ if($shouldReject){
+ $this->rejectsDocument->execute($document);
+ $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::REJECTED);
+ }
+ }
+ }
+
+ }
+
+ // creating the purchase order can happen before or after the payment is made, the customer needs to fill up the list of product
+ // they are buying and attaching it to the booking, a purchase order is a transaction of type TransactionType::PURCHASE_ORDER
+ $billNumber = $this->generatesTransactionBillNumber->execute('PO-');
+
+
+ $shouldSubmit = $this->faker->numberBetween(0, 1);
+ $shouldApprove = $this->faker->numberBetween(0, 1);
+
+ if($shouldSubmit){
+ $completeSubmission = $this->faker->numberBetween(0, 1);
+
+ $quantity = $this->faker->numberBetween(5, 200);
+ $unitPrice = $booking->fix_amount / $quantity;
+
+ $products = collect([[
+ 'stockCode' => $this->faker->numerify('#####'),
+ 'description' => $this->faker->text,
+ 'quantity' => $completeSubmission ? $quantity : $quantity - $this->faker->numberBetween(1, 4),
+ 'unit_price' => (string) round($unitPrice, 5)
+ ]]);
+
+ $total = $products->first()['quantity'] * (float) $products->first()['unit_price'];
+
+ $object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1,
+ 1, PaymentMethodType::CASH,
+ $total, $total, $booking->fix_currency_id, $booking->fix_currency_id,
+ 1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $products->toArray());
+
+ $transaction = $this->createPurchaseOrderTransactionProcessor->execute($booking, $object);
+ }
+
+ if($transaction->status === ApprovalStatus::PENDING_VERIFICATION) {
+ $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED);
+ }
+
+
+
+ }
+
+ // when processing a customer order, we will place an order with one of our currency supplier which will generate a transaction type TransactionType::BILL
+ // and attach it to the customer payment TransactionType::PAYMENT, and it will update the TransactionType::PAYMENT status to ApprovalStatus::COMPLETED
+ $totalApprovedPayments = Transaction::where('type', TransactionType::PAYMENT)->where('status', 2)->count();
+ $totalWhiteForms = round($totalApprovedPayments / $this->faker->numberBetween(2, 5));
+ $perWhiteForm = $totalApprovedPayments / (round($totalWhiteForms / 2) ?: 1);
+
+ for($orderLoop=1; $orderLoop <= ($totalWhiteForms / 2); $orderLoop++) {
+ $supplier = Company::where('business_type', BusinessType::CURRENCY_VENDOR)->inRandomOrder()->first();
+ $rate = $this->faker->randomFloat(5, 1.3, 1.6);
+ $payments = Transaction::where('type', TransactionType::PAYMENT)->where('status', ApprovalStatus::APPROVED)->inRandomOrder()->limit($perWhiteForm)->get();
+
+ $this->createSupplierTransactionProcessor->execute($supplier, $rate, $payments->toArray());
+
+ $group = new Group();
+ $group->save();
+
+ $issuer = '';
+ $receiver = '';
+ $amount = 0;
+ $original_amount = 0;
+ $currency_id = 0;
+ $original_currency_id = '';
+ $currency_rate = '';
+ $tax = 0;
+ $service_charge = 0;
+
+ foreach ($this->createSupplierTransactionProcessor->getBills() as $key => $row) {
+ $group->transactions()->sync($row->id, false);
+ $issuer = $row->issuer;
+ $receiver = $row->receiver;
+ $amount += $row->amount;
+ $original_amount += $row->original_amount;
+ $currency_id = $row->currency_id;
+ $original_currency_id = $row->original_currency_id;
+ $currency_rate = $row->currency_rate;
+ $tax += $row->tax;
+ $service_charge += $row->service_charge;
+ }
+
+ $group->issuer = $issuer;
+ $group->receiver = $receiver;
+ $group->reference = $this->generatesTransactionBillNumber->execute('SPO-');
+ $group->amount = $amount;
+ $group->original_amount = $original_amount;
+ $group->currency_id = $currency_id;
+ $group->original_currency_id = $original_currency_id;
+ $group->currency_rate = $currency_rate;
+ $group->tax = $tax;
+ $group->service_charge = $service_charge;
+
+ $group->update();
+
+ $pdf = LaravelMpdf::loadView('pages.pdfs.currency_vendor_order', ['transactions' => $this->createSupplierTransactionProcessor->getBills(), 'transferFeeTransactions' => $this->createSupplierTransactionProcessor->getTransferTransactions(), 'supplier' => $supplier]);
+
+ $object = new DocumentObject(
+ DocumentType::CURRENCY_VENDOR_ORDER,
+ [chunk_split('data:application/pdf;base64,'.base64_encode($pdf->output()))],
+ '',
+ ApprovalStatus::COMPLETED,
+ 'currency_vendor_order'
+ );
+
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($group, $object);
+ $this->createsFiles->execute($document, $object);
+
+
+ foreach ($payments as $payment) {
+ $chinaBankSlipUploaded = $this->faker->numberBetween(0, 1);
+
+ if($chinaBankSlipUploaded){
+ $bill = $payment->transactions()->where('type', TransactionType::BILL)->first();
+
+ // when our currency supplier completes the transfer they will send us the bank slip as proof of payment, then the admin user
+ // will upload the bank slip document and attaching it to transaction type TransactionType::BILL
+ $object = new DocumentObject( DocumentType::CUSTOMER_PAYMENT_PROOF, ['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAANGklEQVR4nOzXDa/fdX3G8R44Ww54BIFV2wFyoxUoKmsFhA0zEGQj1jOMo5o5IQPmYE5wrSvjdhbHAGWt0BWEwmChuHEjSF2LrY6tlmFjJbblprQstD21UFzbrBhX1tKyR3ElJtfr9QCu78k/v5N3PoOzbv/SmKR/mv94dP+FN9dH95+7+J7o/sjse6P7y3d/Orq/6qKTovtLN94f3Z9w39nR/XeFv/+nrlse3V/xhbXR/Xuv/kx0f3TDjuj+oltviO7PHJf9/veJrgPwK0sAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQafOeyu6MPPP9by6L7f7n/yuj+rWN/Et3/xiu/Gd3/xQduju7ve+KE6P7wc6dG9yde/lx0f86J2e/nO38yLrq/ftXT0f0/m7wzuj9jw4vR/V2n74jub7v8zOi+CwCglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKDWw40dD0Qc2PP+O6P6cJauj+zP+dVl0//izT47uv+eazdH9bf+3I7r/xhFXR/dnzjwmun/lJ56N7n/vA/Oi+5NmnxDdf3jqtuj+8j0PRPfnzPtUdP/Jt++K7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSgwsfXhl94KuHrIjuHzvmxuj+g6+9EN1//Pz/jO5P/2x2f/dTL0X3f+e9S6L7f3zJHdH9jafOjO6/7/LDo/s3bL4+un/rnNOi+yMXDUX3Bw6YFt2/+IyTovsuAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACg1OCEe34UfWDn+SPR/bcWrY3u//DMbdH94w4diu4/sPTo6P7tNx0Z3V92wrzo/k8/uzu6f8DwndH9v5i1ILq/+UO3Rfd3Lz4nuv+VRx6K7s9Y+nJ0f+HKsdF9FwBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUGrw9+84JfrAof89P7r/wf8aG90/d8//RvdvXf/30f3JI8ui+8ceeUd0/+DPr4nuf3jX56L7p168Krr/rcfOiu6PHvhqdH/xLauj+8+Mbozur/vpj6P7E8edH913AQCUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQYXb9kafeDSf/lWdP/fdx4c3X9k60vR/b13Xxjdnzb1Z9H90ctWRfffmvRGdP+8e96M7m+avT26/45vnh3d/9rNY6P7Tz/5w+j+e6bMj+4f8rHro/vHH579/l0AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpwb037R994NK5a6P7/7zgvuj+JTNPiO7/7fKjo/t7H7wmuj/8+pnR/f2nbojuH33tndH9333536L7E25+Kro//NuTo/vLT1oe3f+jNZdG9y+ffHB0f972e6L7LgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoNTgYcd8LvrA9IGd0f3jh6+K7l8/d010f5/hrdH98euGovuf+OvbovsHnPpSdP/p6X8e3d8ztD26v+bJydH9GQfcG91/+MWPRve/vPSX0f2P/OmC6P7AdYdF910AAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAECpgXMG3x59YPXKWdH9xc9+P7q/3/SjovvLjjs3uv83E4+N7p904Lej+9MXfCq6//ULvxvdf2D2KdH9afOuiu5PnPm26P7QM1Oy+yPZ7/PItdui+1v+Y1103wUAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQaeOKXH48+sGnNtOj+mGkrovNvm3tddH/MoWOj8y/cckx0/6D5743u/+ydI9H9O/d+JLr/yuwLovtbHhqI7r//wuz3+Z2PfTC6/5VXVkX3/+G+c6L7Xz9vRnTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBp4/ZC7og/84cLx0f1fX7gyuj/xy6PR/eueuDG6P/6rS6L7t//g8ej+Xa9Niu6/9sSM6P4bmx6L7t+14+Do/se/NBzd3/7Rq6L7a976dHR/xruy/1+jO/aN7rsAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSg48/PyX6wBenDkX3Z607Lbo/d9f7o/tblmd//5u+Oyu6f/rP50X3B1/dEN2/5O6V0f0rLr0tur/omp9E918deT26/+Dpe6P7N179d9H9TdeORvd33XdOdN8FAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUGpgzcW/0gQ9PPyK6P/jtB6P7nzxrU3T/i/dvjO6vPuWQ6P6Z7/616P76T94Q3X/37qHo/rzfmx/dv2V8dv/+/S6L7j/6B0uj+wftuyS6v+KK7O+/++Xs3+8CACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKDTx24TeiDzz68MnR/UU7D4/uH/ah7P6PH5oT3T/viCuj+18b2RTd//7J+0f3L7nipuj+lAueie6PmXpUdH7mpDOi+y/ueiS6f9kP/iq6/+yEn0f3p1x0UXTfBQBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBo86urN0Qemjk6N7n9v+Nzo/uHXfj66P3fF1uj+Pvv9T3T/wBOj82MeOuvR6P64cVdE9/9x4KDo/m+cdmV0f8HSPdH9910wKbo//gv3Rve/+YszovufufO46L4LAKCUAACUEgCAUgIAUEoAAEoJAEApAQAoJQAApQQAoJQAAJQSAIBSAgBQSgAASgkAQCkBACglAAClBACglAAAlBIAgFICAFBKAABKCQBAKQEAKCUAAKUEAKCUAACUEgCAUgIAUEoAAEoJAEApAQAo9f8BAAD//3aYjHM9JD/iAAAAAElFTkSuQmCC'],
+ '', ApprovalStatus::APPROVED, 'china_bank_slip');
+ /** @var Document $document */
+ $document = $this->createsDocument->execute($bill, $object);
+ $this->createsFiles->execute($document, $object);
+
+ $this->updatesTransactionStatus->execute($bill, ApprovalStatus::APPROVED);
+
+ // the invoicing documents will be generated once they 2 conditions are met:
+ // 1. Full payment completed (completed is flagged when the china payment proof is uploaded)
+ // 2. The purchase order is filled and approved (when the purchase order is not filled for more than 2 months the system will automatically generate a random products for Purchase order to close the order)
+
+ // once the invoice is generated the transaction table will include 2 new transaction type TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVERY
+ // and for documents will be generated and attached to the booking.
+ // once this process is complete the booking status will update to ApprovalStatus::COMPLETED
+ $this->createInvoiceTransactionProcessor->execute($booking);
+
+ }
+ }
+
+
+ }
+ }
+
+ }
+
+}
diff --git a/database/seeds/RecoverGroupTransactionTableSeeder.php b/database/seeds/RecoverGroupTransactionTableSeeder.php
new file mode 100644
index 00000000..02edb6a1
--- /dev/null
+++ b/database/seeds/RecoverGroupTransactionTableSeeder.php
@@ -0,0 +1,116 @@
+createsDocument = $createsDocument;
+ $this->createsFile = $createsFile;
+ $this->generatesGroupTransactionBillNumber = $generatesGroupTransactionBillNumber;
+ }
+
+
+ /**
+ * Run the database seeds.
+ *
+ */
+ public function run()
+ {
+ DB::beginTransaction();
+
+ $transaction_group = Transaction::
+ select('issuer', 'currency_rate', 'type', DB::raw('count(DISTINCT id) as total'), DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d %H:%i') as new_date"))
+ ->where('type', 3)
+ ->groupBy(
+ 'issuer',
+ 'currency_rate',
+ 'new_date'
+ )
+ ->orderBy('id')->get();
+
+ foreach ($transaction_group as $group) {
+ $transactions = Transaction::
+ where('type', 3)
+ ->where('issuer', $group->issuer)
+ ->where('currency_rate', $group->currency_rate)
+ ->where(DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d %H:%i')"), $group->new_date)
+ ->get();
+
+ $group = new Group();
+ $group->save();
+
+ $issuer = '';
+ $date = now();
+ $receiver = '';
+ $amount = 0;
+ $original_amount = 0;
+ $currency_id = 0;
+ $original_currency_id = '';
+ $currency_rate = '';
+ $tax = 0;
+ $service_charge = 0;
+
+ foreach ($transactions as $transaction) {
+ $group->transactions()->sync($transaction->id, false);
+ $issuer = $transaction->issuer;
+ $date = $transaction->created_at;
+ $receiver = $transaction->receiver;
+ $amount += $transaction->amount;
+ $original_amount += $transaction->original_amount;
+ $currency_id = $transaction->currency_id;
+ $original_currency_id = $transaction->original_currency_id;
+ $currency_rate = $transaction->currency_rate;
+ $tax += $transaction->tax;
+ $service_charge += $transaction->service_charge;
+ }
+
+ $group->issuer = $issuer;
+ $group->receiver = $receiver;
+ $group->reference = $this->generatesGroupTransactionBillNumber->execute('SPO-', $date);
+ $group->amount = $amount;
+ $group->original_amount = $original_amount;
+ $group->currency_id = $currency_id;
+ $group->original_currency_id = $original_currency_id;
+ $group->currency_rate = $currency_rate;
+ $group->tax = $tax;
+ $group->service_charge = $service_charge;
+ $group->status = ApprovalStatus::PENDING_SUBMISSION;
+ $group->created_at = $date;
+ $group->updated_at = $date;
+
+ $group->update();
+ }
+
+ DB::commit();
+
+ GenerateGroupTransactionsWhiteForm::dispatch();
+ }
+}
diff --git a/database/seeds/UpdateSuppliersReferenceSeeder.php b/database/seeds/UpdateSuppliersReferenceSeeder.php
new file mode 100644
index 00000000..3764664a
--- /dev/null
+++ b/database/seeds/UpdateSuppliersReferenceSeeder.php
@@ -0,0 +1,42 @@
+get();
+
+ foreach ($suppliers as $supplier) {
+ if(in_array($supplier->name, ['ATVANTIC IMPORT EXPORT SDN BHD', 'Atvantic - JACK'])) $supplier->reference = 'ATVANTIC IMPORT EXPORT SDN BHD (1309816-P)';
+ if(in_array($supplier->name, ['BK GEMILANG SDN BHD', 'BK GEMILANG - JACK'])) $supplier->reference = 'BK GEMILANG SDN BHD (1403513-U)';
+ if(in_array($supplier->name, ['YSN - Teh', 'YSN Solution Trading Sdn Bhd - Teh', 'YSN Solution Trading Sdn Bhd', 'YSN SOLUTION TRADING SDN BHD - Jack'])) $supplier->reference = 'YSN Solution Trading Sdn Bhd (1393892-D)';
+ if(in_array($supplier->name, ['RACK SOLUTION INDUSTRIES SDN BHD'])) $supplier->reference = 'RACK SOLUTION INDUSTRIES SDN BHD (954723-W)';
+ if(in_array($supplier->name, ['RACK SOLUTION INDUSTRIES SDN BHD'])) $supplier->reference = 'RACK SOLUTION INDUSTRIES SDN BHD (954723-W)';
+ if(in_array($supplier->name, ['OFY UNION SDN BHD'])) $supplier->reference = 'OFY UNION SDN BHD (1410695-H)';
+ if(in_array($supplier->name, ['Simply Infantry Sdn. Bhd.', 'SIMPLY INFANTRY SDN. BHD. - JACK'])) $supplier->reference = 'Simply Infantry Sdn. Bhd. (14393131-W)';
+ if(in_array($supplier->name, ['HIGH HILL INTERNATIONAL MARKETING SDN BHD', 'HIGH HILL INTERNATIONAL SDN BHD - JACK'])) $supplier->reference = 'HIGH HILL INTERNATIONAL MARKETING SDN BHD (1419836-X)';
+ if(in_array($supplier->name, ['CNT CARGO SDN BHD', 'CNT CARGO SDN BHD - JACK'])) $supplier->reference = 'CNT CARGO SDN BHD 202101036178(1436478-V)';
+ if(in_array($supplier->name, ['WEST EXPRESS INTERNATIONAL TRADING SDN BHD'])) $supplier->reference = 'WEST EXPRESS INTERNATIONAL TRADING SDN BHD (1432178-T)';
+ if(in_array($supplier->name, ['CIEF WORLDWIDE SDN. BHD.'])) $supplier->reference = 'CIEF WORLDWIDE SDN. BHD. (1134596-M)';
+
+ $supplier->save();
+ }
+
+ DB::commit();
+ }
+}
diff --git a/docker-setup/Dockerfile b/docker-setup/Dockerfile
new file mode 100644
index 00000000..71255f30
--- /dev/null
+++ b/docker-setup/Dockerfile
@@ -0,0 +1,41 @@
+FROM php:7.4-fpm
+
+WORKDIR /var/www/html
+
+RUN pecl install xdebug-2.9.8 && docker-php-ext-enable xdebug
+
+RUN docker-php-ext-install pdo pdo_mysql
+
+RUN apt-get update && apt-get install -y \
+ libfreetype6-dev \
+ libjpeg62-turbo-dev \
+ libpng-dev \
+ libzip-dev \
+ zip \
+ cron \
+ supervisor \
+ nano \
+ && docker-php-ext-configure gd --with-freetype --with-jpeg \
+ && docker-php-ext-install -j$(nproc) gd \
+ && docker-php-ext-install zip \
+ && docker-php-ext-install bcmath
+
+COPY --from=composer:1.9.3 /usr/bin/composer /usr/bin/composer
+
+#NODEJS & NPM
+RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
+RUN apt-get -y install nodejs
+
+RUN chown -R www-data:www-data /var/www
+RUN chmod 755 /var/www
+
+# Configure xdebug
+RUN echo "xdebug.remote_enable=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+RUN echo "xdebug.remote_autostart=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+RUN echo "xdebug.remote_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+RUN echo "xdebug.remote_port=9002" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+RUN echo "xdebug.idekey=VSCODE" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+
+# Moved to docker-setup folder
+# RUN echo 'pm.max_children = 15' >> /usr/local/etc/php-fpm.d/zz-docker.conf && \
+# echo 'pm.max_requests = 500' >> /usr/local/etc/php-fpm.d/zz-docker.conf
diff --git a/docker-setup/docker-compose.yml b/docker-setup/docker-compose.yml
new file mode 100644
index 00000000..4dcfa8c2
--- /dev/null
+++ b/docker-setup/docker-compose.yml
@@ -0,0 +1,53 @@
+version: '3'
+
+networks:
+ exchange-staging:
+
+services:
+ #################################################################
+ nginx:
+ image: nginx:stable-alpine
+ container_name: exchange-ngnix
+ ports:
+ - "8082:80"
+ volumes:
+ - ../:/var/www/html
+ - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
+ depends_on:
+ - php
+ - mysql
+ networks:
+ - exchange-staging
+ #################################################################
+ mysql:
+ image: mysql:5.7.29
+ container_name: exchange-mysql
+ restart: unless-stopped
+ tty: true
+ ports:
+ - 3302:3306
+ environment:
+ MYSQL_ROOT_USER: root
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_DATABASE: exchange-db
+ MYSQL_USER: master
+ MYSQL_PASSWORD: cDe7gcrRBWetaAP
+ volumes:
+ - mysql-data:/var/lib/mysql
+ networks:
+ - exchange-staging
+ #################################################################
+ php:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: exchange-php
+ volumes:
+ - ../:/var/www/html
+ - ./php/default.conf:/usr/local/etc/php-fpm.d/zz-docker.conf
+ networks:
+ - exchange-staging
+ #################################################################
+
+volumes:
+ mysql-data:
diff --git a/docker-setup/nginx/default.conf b/docker-setup/nginx/default.conf
new file mode 100644
index 00000000..c1811261
--- /dev/null
+++ b/docker-setup/nginx/default.conf
@@ -0,0 +1,38 @@
+server {
+ listen 80;
+ index index.php index.html;
+ server_name localhost;
+ error_log /var/log/nginx/error.log;
+ access_log /var/log/nginx/access.log;
+ root /var/www/html/public;
+
+ server_name localhost;
+
+ location / {
+ try_files $uri $uri/ /index.php?$query_string;
+ }
+
+ location ~ \.php$ {
+ try_files $uri =404;
+ fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ fastcgi_pass php:9000;
+ fastcgi_index index.php;
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param PATH_INFO $fastcgi_path_info;
+ fastcgi_intercept_errors on;
+ fastcgi_keep_conn on;
+ fastcgi_param PHP_VALUE "auto_prepend_file= \n allow_url_include=Off";
+
+ # Xdebug configuration
+ # fastcgi_param XDEBUG_MODE debug;
+ # fastcgi_param XDEBUG_CLIENT_HOST host.docker.internal;
+ # fastcgi_param XDEBUG_CLIENT_PORT 9002;
+ # fastcgi_param XDEBUG_IDE_KEY VSCODE;
+
+ proxy_send_timeout 3600;
+ proxy_read_timeout 3600;
+ fastcgi_send_timeout 3600;
+ fastcgi_read_timeout 3600;
+ }
+}
diff --git a/docker-setup/php/default.conf b/docker-setup/php/default.conf
new file mode 100644
index 00000000..dced3233
--- /dev/null
+++ b/docker-setup/php/default.conf
@@ -0,0 +1,8 @@
+[global]
+daemonize = no
+
+[www]
+listen = 9000
+
+pm.max_children = 15
+pm.max_requests = 500
diff --git a/package.json b/package.json
index ced333fa..5d4b92bf 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
},
"devDependencies": {
"axios": "^0.21.0",
+ "chromatic": "^6.5.4",
"cross-env": "^7.0.2",
"del": "^6.0.0",
"fancy-log": "^1.3.0",
diff --git a/resources/assets/images/1688_approved.png b/resources/assets/images/1688_approved.png
new file mode 100644
index 00000000..4275de56
Binary files /dev/null and b/resources/assets/images/1688_approved.png differ
diff --git a/resources/assets/images/1688_logo.png b/resources/assets/images/1688_logo.png
new file mode 100644
index 00000000..dbcc92b1
Binary files /dev/null and b/resources/assets/images/1688_logo.png differ
diff --git a/resources/assets/images/best-rate-300x127.png b/resources/assets/images/best-rate-300x127.png
new file mode 100644
index 00000000..c5e9b1c8
Binary files /dev/null and b/resources/assets/images/best-rate-300x127.png differ
diff --git a/resources/assets/images/favicon/site.webmanifest b/resources/assets/images/favicon/site.webmanifest
index 45dc8a20..97135d16 100644
--- a/resources/assets/images/favicon/site.webmanifest
+++ b/resources/assets/images/favicon/site.webmanifest
@@ -1 +1 @@
-{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
\ No newline at end of file
+{"name":"","short_name":"","icons":[{"src":"images/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"images/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
\ No newline at end of file
diff --git a/resources/assets/sass/modules/_buttons.scss b/resources/assets/sass/modules/_buttons.scss
index 3a9879eb..e7b281f8 100644
--- a/resources/assets/sass/modules/_buttons.scss
+++ b/resources/assets/sass/modules/_buttons.scss
@@ -123,7 +123,11 @@ button:focus{
button:disabled {
cursor: not-allowed;
-}
+}
+
+.not-allowed {
+ cursor: not-allowed;
+}
/*
Alternate buttons
--------------------------------------------------
diff --git a/resources/assets/sass/modules/_typography.scss b/resources/assets/sass/modules/_typography.scss
index 605b8e7c..aa4506a9 100644
--- a/resources/assets/sass/modules/_typography.scss
+++ b/resources/assets/sass/modules/_typography.scss
@@ -316,6 +316,12 @@ hr{
background-color: $color-primary-lighter !important;
}
+.bg-primary-lighter-hover {
+ &:hover {
+ background-color: $color-primary-lighter !important;
+ }
+}
+
/* Complete
------------------------------------
*/
diff --git a/resources/assets/vue/app.js b/resources/assets/vue/app.js
index 1d98d12d..abb7a17e 100644
--- a/resources/assets/vue/app.js
+++ b/resources/assets/vue/app.js
@@ -24,6 +24,8 @@ import crudHandler from './general/mixins/crudHandler';
/** Directives */
import closable from './general/directives/closable';
+import { debounce } from 'vue-debounce'
+
import Avatar from 'vue-avatar';
/** Application Injections */
@@ -37,7 +39,7 @@ Vue.mixin({
route: route
},
mixins: [request, crudHandler]
- });
+});
/** Components Registrations */
Vue.component(Avatar);
@@ -48,11 +50,77 @@ files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(
const app = new Vue({
el: '#app',
store,
+ data(){
+ return {
+ captchaId: '',
+ reCaptchaToken: null
+ }
+ },
created(){
+ this.init();
this.routesGuard();
+ if (!this.$store.getters.isAdmin && !('company_marking' in this.$store.getters.getDecodedAccessToken.user) && this.isProtectedRoute() && !this.isWithTokenRoute()) {
+ this.$store.dispatch('crudRequest', {endpoint: this.route('api.account.authentication.refresh'), method: 'get'}).then(response => {
+ let success = response.ok;
+ response.json().then(response => {
+
+ if(!success){return;}
+
+ this.$store.dispatch('userAuthentication', {access_token: response.payload.refresh_token, redirect_url: window.location.href});
+
+ });
+ })
+
+ }
},
methods: {
- route: route
+ route: route,
+ init() {
+ if (!document.getElementById('gRecaptchaScript')) {
+
+ window.gRecaptchaOnLoadCallbacks = [this.render];
+ window.gRecaptchaOnLoad = function () {
+ for (let i = 0; i < window.gRecaptchaOnLoadCallbacks.length; i++) {
+ window.gRecaptchaOnLoadCallbacks[i]();
+ }
+ delete window.gRecaptchaOnLoadCallbacks;
+ delete window.gRecaptchaOnLoad;
+ };
+
+ let recaptchaScript = document.createElement('script');
+ recaptchaScript.setAttribute('src', 'https://www.google.com/recaptcha/api.js?render=explicit&onload=gRecaptchaOnLoad');
+ recaptchaScript.setAttribute('id', 'gRecaptchaScript');
+ recaptchaScript.async = true;
+ recaptchaScript.defer = true;
+ document.head.appendChild(recaptchaScript);
+
+ } else if (!window.grecaptcha || !window.grecaptcha.render) {
+ window.gRecaptchaOnLoadCallbacks.push(this.render);
+ } else {
+ this.render();
+ }
+ },
+ render() {
+ this.captchaId = window.grecaptcha.render('grecaptcha_container', {
+ sitekey: '6Lft5mkhAAAAAAFgQ0gWlwte1h-o6UPRpMNHP1xz',
+ badge: '',
+ size: 'invisible',
+ 'expired-callback': this.execute
+ });
+ this.execute();
+ },
+ execute: debounce(function() {
+ this.updateCaptchaToken();
+ }, 3000),
+ updateCaptchaToken() {
+ if(this.$store.getters.getReCaptcha === this.reCaptchaToken){
+ window.grecaptcha.execute(this.captchaId).then((token) => {
+ this.reCaptchaToken = token;
+ this.$store.dispatch('reCaptcha', {token: token});
+ });
+ }
+
+ }
},
mixins: [guards]
});
diff --git a/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue b/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue
new file mode 100644
index 00000000..b3d50d7b
--- /dev/null
+++ b/resources/assets/vue/components/accounting/elements/EditSingleItemInListComponent.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+ Date: {{ item.posting_date }}
+
+
+ Transaction Description 1: {{ item.transaction_description_1 }}
+
+
+ Transaction Description 2: {{ item.transaction_description_2 }}
+
+
+ Transaction Description 3: {{ item.transaction_description_3 }}
+
+
+ Transaction Description 4: {{ item.transaction_description_4 }}
+
+
+ Transaction Description 5: {{ item.transaction_description_5 }}
+
+
+
+
+
+
+
+
+ Transaction Type
+
+
+
+
+
+
+
+
+
+
+ Transaction ID / Reference
+
+
+
+
+
+
+
{{ error }}
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue
new file mode 100644
index 00000000..db2fc252
--- /dev/null
+++ b/resources/assets/vue/components/accounting/elements/StatementTransactionComponent.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
{{ item.posting_date }}
+
{{ item.transaction_description_1 + ' - ' + item.transaction_description_2 }}
+
+
+
{{item.owners.pending_verification[0].system}}
+
{{ typeString(item.owners.pending_verification[0].type) }}
+
+
+
+
+
+
+
+
+ {{ owner.system }}
+
+
+
+
+
+
+ {{ typeString(owner.type) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ item.amount }}
+
{{ [6, 7, 8, 9, 10, 11, 12, 13, 14].include(item.owners.approved[0].type) ? 'Miscellaneous' : 'Approved' }}
+
Pending...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue
new file mode 100644
index 00000000..54db23df
--- /dev/null
+++ b/resources/assets/vue/components/accounting/forms/ImportStatementFormComponent.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Import Bank Statement CSV
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue b/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue
new file mode 100644
index 00000000..9a5e0058
--- /dev/null
+++ b/resources/assets/vue/components/accounting/sections/StatementTransactionsDetailsComponent.vue
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue
new file mode 100644
index 00000000..7bbf87aa
--- /dev/null
+++ b/resources/assets/vue/components/accounting/sections/TransactionsMappingComponent.vue
@@ -0,0 +1,272 @@
+
+
+
+
+
+
+
+
+
Receivable Mapping
+
Payable Mapping
+
+
+
+
+
+
+
+
{{ type === 1 ? 'Receivable Mapping' : 'Payable Mapping'}}
+
+
+
+
Mapping Approval
+
+
Mapping Review
+
+
Unknown
+
+
Pending Export
+
+
+
+
+
+
+
All
+
Sales
+
Wallet Top Up
+
Internal Bank Transfer In
+
Unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
Approve all the Mapping Below
+
+
+
+
+
+
+
+
+
Date
+
Description
+
+
+
System
+
Type
+
Reference
+
+
+
Amount
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Export Invoices To AutoCount
+
Nest Step
+
+
+
+
+
+
+
+
Import Invoices
+
Nest Step
+
+
+
+
+
+
+
Export Receipts To AutoCount
+
+
+
+
+
+
+
+
Import Receipts
+
Nest Step
+
+
+
+
+
+
+ All Done, Good Job 👏
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/accounts/forms/LoginFormComponent.vue b/resources/assets/vue/components/accounts/forms/LoginFormComponent.vue
index ed097c68..50c4236d 100644
--- a/resources/assets/vue/components/accounts/forms/LoginFormComponent.vue
+++ b/resources/assets/vue/components/accounts/forms/LoginFormComponent.vue
@@ -2,18 +2,24 @@
+
+
+
+
+
+
@@ -55,4 +61,4 @@
mixins: [loginFormValidation]
}
-
\ No newline at end of file
+
diff --git a/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue b/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue
index e436960a..9f49275e 100644
--- a/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue
+++ b/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue
@@ -33,7 +33,7 @@
viewBox="0 0 172 172"
style=" fill:#000000;">
-
Company
+
Company
@@ -43,7 +43,7 @@
width="45" height="45"
viewBox="0 0 172 172"
style=" fill:#000000;">
- Personal
+ Personal
@@ -62,7 +62,7 @@
Company Name
-
+
@@ -70,13 +70,13 @@
Full Name
-
+
Phone
-
+
@@ -95,7 +95,7 @@
Email
-
+
@@ -103,13 +103,13 @@
Password
-
+
Password Confirmation
-
+
@@ -121,19 +121,21 @@
- Complete Registration
+ Complete Registration
diff --git a/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue b/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue
index 3bf7fa65..9c89c89d 100644
--- a/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue
+++ b/resources/assets/vue/components/banks/forms/BankAccountFormComponent.vue
@@ -14,56 +14,108 @@
{{error}}
-
+
-
-
-
- Account Holder Name
-
-
-
-
-
-
-
- {{ serviceType ? serviceType.id === 4 ? 'Alipay recipient Email / Phone' : 'Account No.' : 'Account No.'}}
-
-
-
-
-
-
-
+
-
+
+
+
+ {{ serviceType ? serviceType.id === 4 ? 'Alipay recipient Email / Phone' : 'Account No.' : 'Account No.'}}
+
+
+
+
+
+
+
+
+
+
+ Bank Branch / 所在地
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Account Holder Name
+
+
+
+
+
+
+
+ Account Holder Address
+
+
+
+
+
+
+
+
+
+
+
+ Bank Address
+
+
+
+
+
+
+
Swift
-
+
Snap
@@ -122,6 +174,11 @@
type: Object,
required: false,
default: null
+ },
+ currency: {
+ type: String,
+ required: false,
+ default: 'RMB'
}
},
data(){
@@ -181,4 +238,4 @@
mixins: [FormHandler]
}
-
\ No newline at end of file
+
diff --git a/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue b/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue
index dfec0ddb..76401d2b 100644
--- a/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue
+++ b/resources/assets/vue/components/banks/forms/PhoneAccountFormComponent.vue
@@ -14,18 +14,10 @@
{{error}}
-
- {{ serviceType.id === 4 ? 'Alipay recipient Email / Phone' : 'Account No.' }}
+ {{ serviceType.id === 4 ? '1688 Login Id/Email/Phone' : 'Account No.' }}
@@ -33,20 +25,17 @@
-
+
-
Warning: We do not encourage to transfer to non-chinese recipient Alipay account ! Proceed Anyway.
-
-
-
Foreign name alipay may exceed Monthly / Yearly Limit , and may be unable to withdraw your funds out.
-
-
-
The risk is too high. I changed my mind.
+
+ AliPay 6-digit Payment Pin
+
+
@@ -54,7 +43,7 @@
{{disabled ? 'Change Recipient Account' : 'Cancel'}}
- Add Account
+ Add Account
@@ -102,7 +91,7 @@
bank_name: '-',
holder_name: '',
account_no: '',
- bank_branch: '-',
+ bank_branch: '',
country_id: this.country_id,
},
englishTextWarning: false,
@@ -117,15 +106,15 @@
account_no: {
required
},
- reference: {
- required: requiredIf(function () { return this.parameters.type === 2 })
+ bank_branch: {
+ required
}
}
},
methods: {
submitForm(){
- this.parameters.account_type = 3,
- this.parameters.bank_name = '-',
+ this.parameters.account_type = 3;
+ this.parameters.bank_name = '-';
this.submit(route('api.bank.create'), 'post', this.section, true, false);
},
successHandler(response){
diff --git a/resources/assets/vue/components/bookings/elements/AvailableVouchersComponent.vue b/resources/assets/vue/components/bookings/elements/AvailableVouchersComponent.vue
new file mode 100644
index 00000000..45722aaf
--- /dev/null
+++ b/resources/assets/vue/components/bookings/elements/AvailableVouchersComponent.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
{{ item.voucher.code }}
+
RM{{ item.voucher.value/100 }} Discount
+
{{ item.voucher.value }}% Discount
+
+
+ Valid till {{ item.voucher.end_date }}
+
+
+ No expiry date
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/elements/BillingComponent.vue b/resources/assets/vue/components/bookings/elements/BillingComponent.vue
index 70f511ed..f24078ed 100644
--- a/resources/assets/vue/components/bookings/elements/BillingComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/BillingComponent.vue
@@ -84,7 +84,14 @@ export default {
type: 'INVOICE',
supplier: null
},
- documents: ['INVOICE', 'PURCHASE_ORDER', 'DELIVER_ORDER', 'SUPPLIER_DELIVER_ORDER'],
+ documents: [
+ 'INVOICE',
+ 'PURCHASE_ORDER',
+ 'DELIVER_ORDER',
+ 'SUPPLIER_DELIVER_ORDER',
+ 'INVOICE + PO + DO',
+ 'INVOICE + PO + DO + SDO'
+ ],
selectedDocumentStatus: false
}
},
diff --git a/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue b/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue
index f08ea597..68db519b 100644
--- a/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/BookingConfirmationComponent.vue
@@ -1,131 +1,135 @@
-
+
-
+
-
-
-
+
-
{{this.data.serviceType.name}}
+
Transfer Summary
-
-
-
Service Type
+
+
+
+
+
{{this.data.serviceType.name}}
+
+
+
-
-
-
-
-
-
{{this.data.serviceType.selectedCurrency.short_code}}
+
+
+
+
{{this.data.serviceType.selectedCurrency.short_code}}
+
+
+
-
-
-
-
-
-
-
{{(Math.round((parseFloat(this.data.amount.replace(",", "")) + Number.EPSILON) * 100) / 100).toFixed(2)}} {{this.parameters.type === 0 ? 'MYR': this.data.serviceType.selectedCurrency.short_code}}
-
-
-
-
-
Transfer Total
+
+
+
{{(Math.round((parseFloat(this.data.amount.replace(",", "")) + Number.EPSILON) * 100) / 100).toFixed(2)}} {{this.parameters.type === 0 ? 'MYR': this.data.serviceType.selectedCurrency.short_code}}
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
Recipient Details
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
Coming Soon
-
Pay-On-Behalf
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ {{ data.serviceType.id === 4 ? '1688 Login Id/Email/Phone' : 'Account No.' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
-
{{bank.reference ? bank.reference + ' - ':''}}{{bank.holder_name}}
-
-
-
-
-
{{bank.account_no}} {{bank.bank_name}}
+
+
+
{{bank.reference ? bank.reference + ' - ':''}}{{bank.holder_name}}
+
+
+
+
+
{{bank.account_no}} {{bank.bank_name}}
+
+
@@ -137,45 +141,45 @@
+
+
+
+ Create New
+
+
+
+
-
-
-
- Create New
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -183,95 +187,122 @@
-
-
+
+
+
+
+
+
+
Transfer Amount and Charges
+
** These are the rate as of {{this.parameters.calculation.date}} , all charges and rates are subject to change when making payment
+
+
+
+
+
+
+
Amount you are Transferring
+
+
+
{{this.parameters.type === 0 ? 'MYR': parameters.serviceType.selectedCurrency.short_code}}
+
+
+
{{(Math.round((parseFloat(this.parameters.amount.replace(",", ""))+ Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
+
+
+
+
{{(Math.round((this.parameters.calculation.rate + Number.EPSILON) * 100000) / 100000).toFixed(5)}}
+
+
+
+
+
+
+
{{(Math.round((this.parameters.calculation.service_charge + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
+
+
+
+
{{(Math.round((this.parameters.calculation.sub_total + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
+
+
+
{{Math.round((this.parameters.calculation.tax + Number.EPSILON) * 100) / 100}}%
+
+
+
{{(Math.round((this.parameters.calculation.tax_total + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
+
+
Amount you are Paying
+
+
+
+
{{(Math.round((this.parameters.calculation.total + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
+
+
+
+
+
-
+
-
-
-
Transfer Amount and Charges
-
** These are the rate as of {{this.parameters.calculation.date}} , all charges and rates are subject to change when making payment
-
-
-
-
-
-
-
Amount you are Transferring
-
-
-
{{this.parameters.type === 0 ? 'MYR': parameters.serviceType.selectedCurrency.short_code}}
-
-
-
{{(Math.round((parseFloat(this.parameters.amount.replace(",", ""))+ Number.EPSILON) * 100) / 100).toFixed(2)}}
-
-
-
-
-
-
-
{{(Math.round((this.parameters.calculation.rate + Number.EPSILON) * 100000) / 100000).toFixed(5)}}
-
-
-
-
-
-
-
{{(Math.round((this.parameters.calculation.service_charge + Number.EPSILON) * 100) / 100).toFixed(2)}}
-
-
-
-
-
-
-
{{(Math.round((this.parameters.calculation.sub_total + Number.EPSILON) * 100) / 100).toFixed(2)}}
-
-
-
-
-
-
{{Math.round((this.parameters.calculation.tax + Number.EPSILON) * 100) / 100}}%
-
-
-
{{(Math.round((this.parameters.calculation.tax_total + Number.EPSILON) * 100) / 100).toFixed(2)}}
-
-
-
-
-
Amount you are Paying
-
-
-
-
{{(Math.round((this.parameters.calculation.total + Number.EPSILON) * 100) / 100).toFixed(2)}}
-
-
-
-
+
1. Alipay account must be a real-name verified (实名认证), linked (已绑定), and activated (已激活) to the 1688 account.
+
2. Choose "secure transaction" (担保交易) and avoid using Angpao during 1688 orders submission.
+
3. For first-time 1688 login, our staff will only use our company number +6011-56489252 to request the OTP (验证码).
+
4. Purchase invoice will be available 1-2 days after payment.
+
I Agree
-
@@ -287,6 +318,9 @@
recipientBanks: [],
dropdownStatus: false,
account_no: '',
+ step:1,
+ promoCode:""
+
}
},
computed: {
@@ -317,7 +351,52 @@
this.account_no = '';
this.parameters.bankAccount = {};
}
+ /*
+ applyPromocoe(){
+ let promoRequestObj = {
+ category : 'New cat 4',
+ discount: {
+ "percent_off": 10.0,
+ "type": "PERCENT"
+ },
+ redemption: {
+ quantity: 10
+ },
+ start_date: "2016-01-01T00:00:00Z",
+ expiration_date: "2016-12-31T23:59:59Z",
+ order: {
+ amount: 10,
+ currency: "USD",
+ id: "order_1234523"
+ }
+ };
+ this.isLoading = true;
+
+ let url = `https://as1.api.voucherify.io/v1/vouchers/${this.promoCode}/redemption`;
+
+ return fetch(url, {
+ method: 'POST',
+ responseType: 'json',
+ body: JSON.stringify(promoRequestObj),
+ headers: {
+ 'content-type': 'application/json',
+ 'X-App-Token': 'b839bfa1-d8c1-4846-a367-05d466f48cfd',
+ 'X-App-Id': 'a180e3cb-ee34-469b-876f-d4a1139b82c6'
+ }
+ }).then(response => {
+
+ console.log(response);
+
+ if(response.status === 401 && window.location.href !== route('login')){
+ dispatch('userAuthentication', {access_token: '', redirect_url: '/'});
+ }
+
+ return response;
+
+ })
+ }
+ */
},
mixins: [FormHandler]
}
-
\ No newline at end of file
+
diff --git a/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue b/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue
index ad4c3e42..a88fd5a9 100644
--- a/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/CurrencyOrderComponent.vue
@@ -40,7 +40,7 @@
Currency Order Placed
-
Are you sure you that the currency order has been placed with the supplier?
+
Are you sure that the currency order has been placed with the supplier?
diff --git a/resources/assets/vue/components/bookings/elements/CustomerTransactionHistorySectionComponent.vue b/resources/assets/vue/components/bookings/elements/CustomerTransactionHistorySectionComponent.vue
new file mode 100644
index 00000000..026a76c5
--- /dev/null
+++ b/resources/assets/vue/components/bookings/elements/CustomerTransactionHistorySectionComponent.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
Transaction History
+
+
+
+
+
+
Date
+
Description
+
Marking
+
Incoming
+
Outgoing
+
Balance
+
+
+
+
{{item.created_at}}
+
{{ convertTransactionType(item.type) }} {{ item.payment_reference ? ' - ' + item.payment_reference : '' }}
+
+
{{[5, 9].includes(parseFloat(item.type)) ? (Math.round((parseFloat(item.amount) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : ''}}
+
{{(Math.round(((parseFloat(item.amount) / parseFloat(item.currency_rate) + parseFloat(item.service_charge)) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
{{[1, 11].includes(parseFloat(item.type)) ? '- ' + (Math.round((parseFloat(item.amount) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : ''}}
+
{{remainingBalance(index)}}
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/elements/DownloadBillingWithDatesComponent.vue b/resources/assets/vue/components/bookings/elements/DownloadBillingWithDatesComponent.vue
new file mode 100644
index 00000000..99be4562
--- /dev/null
+++ b/resources/assets/vue/components/bookings/elements/DownloadBillingWithDatesComponent.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+ Start Date
+
+
+
+
+
+ End Date
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue b/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue
new file mode 100644
index 00000000..a236bf8a
--- /dev/null
+++ b/resources/assets/vue/components/bookings/elements/FilterBookingComponent.vue
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Status
+
+
+
+
+
+
+ Search
+
+
+ Reset
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/assets/vue/components/bookings/elements/ListVouchersComponent.vue b/resources/assets/vue/components/bookings/elements/ListVouchersComponent.vue
new file mode 100644
index 00000000..e55e5151
--- /dev/null
+++ b/resources/assets/vue/components/bookings/elements/ListVouchersComponent.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
{{ item.voucher.code }}
+
RM{{ item.voucher.value/100 }} Discount
+
{{ item.voucher.value }}% Discount
+
+
+ Valid till {{ item.voucher.end_date }}
+
+
+ No expiry date
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue
index 51352679..ce4ba5a7 100644
--- a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue
@@ -33,6 +33,11 @@
{{ item.status === 2 ? 'Received' : item.status === 4 ? 'Rejected' : 'Submitted'}} On: {{item.updated_at}}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -86,6 +91,11 @@
{{ item.transaction_bill.status === 1 ? 'Paid On: ' + item.updated_at : 'Transferred On:' + item.transaction_bill.updated_at }}
+
+
+
Bill Number: {{ item.bill_no }}
+
+
@@ -123,7 +133,7 @@
Requested Refund Amount
-
{{item.original_currency.short_code}} {{(Math.round((totalRequestedRefund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
{{item.currency.short_code}} {{(Math.round((totalRequestedRefund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
@@ -150,6 +160,17 @@
MYR {{(Math.round((item.service_charge + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
+
- MYR {{(Math.round((item.redemption.value + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
-
-
-
Request Refund
+
+
+
+
Request Refund
@@ -249,12 +301,25 @@
},
computed: {
totalRequestedRefund() {
+ let vm = this;
var TotalRequestedRefund = 0;
this.data.transaction_refunds.forEach(function(refunds) {
TotalRequestedRefund += refunds.status === 1 ? refunds.original_amount : 0;
});
+
return TotalRequestedRefund;
},
+ totalRequestedConvertRefund() {
+ let vm = this;
+ var TotalRequestedRefund = 0;
+ this.data.transaction_refunds.forEach(function(refunds) {
+ TotalRequestedRefund += refunds.status === 1 ? refunds.original_amount : 0;
+ });
+ if (vm.data.booking.fixed_currency.id != 1 && this.data.transaction_refunds[0]) {
+ TotalRequestedRefund = (TotalRequestedRefund * this.data.transaction_refunds[0].currency_rate);
+ }
+ return ((Math.round((TotalRequestedRefund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
+ },
totalRefunds() {
var TotalRequestedRefund = 0;
this.data.transaction_refunds.forEach(function(refunds) {
diff --git a/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue
index 5af5548c..ff8452a5 100644
--- a/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue b/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue
index e78a14f6..42d5682e 100644
--- a/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/RefundVerificationComponent.vue
@@ -28,7 +28,7 @@
Amount
- {{(Math.round((data.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+ {{(Math.round((data.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
diff --git a/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue b/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue
index 80eb3932..e4877e96 100644
--- a/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/SupplierPendingOrderComponent.vue
@@ -20,11 +20,20 @@
-
timer
-
- {{item.interval.value}}
- {{item.interval.duration}}
- days
+
+
+
timer
+
+ {{item.interval.value}}
+ {{item.interval.duration}}
+ days
+
+
+
+
@@ -90,6 +99,13 @@
+
+
+
{{item.recipient_bank_account.holder_name}}
+
{{item.recipient_bank_account.bank_name}}({{item.recipient_bank_account.bank_branch}})
+
{{item.recipient_bank_account.type === 3 ? item.recipient_bank_account.account_no : item.recipient_bank_account.account_no.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim()}}
+
+
diff --git a/resources/assets/vue/components/bookings/elements/TransactionGroupComponent.vue b/resources/assets/vue/components/bookings/elements/TransactionGroupComponent.vue
new file mode 100644
index 00000000..680a9550
--- /dev/null
+++ b/resources/assets/vue/components/bookings/elements/TransactionGroupComponent.vue
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
Date
+
+ {{item.created_at}}
+
+
+
+
Supplier
+
+ {{item.issuer_name}}
+
+
+
+
+
+
Currency Rate
+
+ {{item.currency_rate}}
+
+
+
+
Currency Amount
+
+ {{item.original_currency.short_code}} {{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
+
+
+
Amount
+
+ {{item.currency.short_code}} {{((Math.round(( item.amount + Number.EPSILON) * 100) / 100)).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
+
+
+
+
+
reference
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{item.complete_transactions.length}} /{{item.transactions.length}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Delete Transaction Group
+
Are you sure that you want to delete this transaction group?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/elements/UploadDebtorExcelComponent.vue b/resources/assets/vue/components/bookings/elements/UploadDebtorExcelComponent.vue
index 946bafe1..a956a07c 100644
--- a/resources/assets/vue/components/bookings/elements/UploadDebtorExcelComponent.vue
+++ b/resources/assets/vue/components/bookings/elements/UploadDebtorExcelComponent.vue
@@ -1,31 +1,44 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
-
Upload & Update
+
Upload Debtors
+
+
+
+
+
+
+
+
+
+
+
+ Download New Debtors Report
@@ -57,7 +70,10 @@
};
this.submit(this.route('api.debtor.import'), 'post', this.section, true, false)
- }
+ },
+ downloadReport(){
+ window.open(route('newDebtor.export'), '_blank');
+ },
},
mixins: [ModalFromHandler]
diff --git a/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue b/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue
index 57cbe49e..d9c009b5 100644
--- a/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue
+++ b/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue
@@ -128,12 +128,12 @@
-
Transfer Now
+
Transfer Now
@@ -152,7 +152,7 @@
-
+
diff --git a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue
index 6f5cb632..9020fbce 100644
--- a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue
+++ b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationComponent.vue
@@ -40,6 +40,17 @@
+
+
+
+
+
+ {{ !item.purchase_order ? 'Please fill up purchase order to enjoy the cashback' : 'You have entitled to earn cashbback' }}
+
+
+
+
+
@@ -49,7 +60,7 @@
-
+
-
{{item.bank.bank_name}} ({{item.bank.bank_branch}})
+
{{item.bank.bank_name}} ({{item.bank.bank_branch}}) ({{item.bank.swift}})
@@ -81,7 +92,7 @@
Transfer Total:
-
{{this.data.fixed_currency.short_code}} {{(Math.round((this.data.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
+
{{this.data.fixed_currency.short_code}} {{(Math.round((this.data.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
@@ -113,7 +124,7 @@
- Make Payment
+ Make Payment
@@ -213,7 +224,50 @@
-
+
+
+
+
+
+ Enter Voucher
+
+
+
+
+
+
+
+
+
+
+
Apply a voucher
+
+
+ {{ voucherCodeFailedReason }}
+ Voucher applied
+
+
+
@@ -222,7 +276,7 @@
-
Online Banking
+
Online Banking
@@ -255,7 +309,7 @@
-
{{$store.getters.isShowing('otherPaymentMethods') ? 'Hide' : 'Show'}} Alternative Methods
+
{{$store.getters.isShowing('otherPaymentMethods') ? 'Hide' : 'Show'}} Alternative Methods
@@ -269,7 +323,7 @@
viewBox="0 0 172 172"
style=" fill:#000000;">
-
Manual Transfer
+
Manual Transfer
@@ -395,7 +449,7 @@
Cancel
- Create Booking
+ Create Booking
@@ -457,6 +511,17 @@
MYR {{(Math.round((calculation.sub_total + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
+
- MYR {{(Math.round((calculation.voucher_discount_amount + Number.EPSILON) * 100) / 100).toFixed(2)}}
+
+
+
Tax {{(Math.round((calculation.tax + Number.EPSILON) * 10) / 10).toFixed(1)}}%
@@ -495,9 +560,9 @@
Cancel
- Lock Booking
+ Lock Booking
-
+
@@ -529,12 +594,17 @@
id: '',
status: false
},
- onlinePayment: {
+ onlinePayment: {
id: '',
status: false
},
amount: (Math.round((this.data.outstanding_amount + Number.EPSILON) * 100) / 100).toFixed(2),
- calculation: null
+ calculation: null,
+ voucherCode: '',
+ voucherCodeFailedReason: '',
+ voucherValidated: false,
+ voucherIsChecking: false,
+ showApplyVoucher: false,
}
},
validations () {
@@ -569,6 +639,7 @@
submitForm(){
this.parameters = {
payment_method: this.paymentMethod.id,
+ voucherCode: this.voucherCode,
amount: this.amount
};
@@ -576,7 +647,22 @@
this.calculation = null;
},
successHandler(response){
- this.calculation = response.payload.data;
+ if(!response.payload.data){
+ this.voucherValidated = false;
+ this.voucherIsChecking = false;
+ this.voucherCodeFailedReason = 'Something went wrong. Please contact customer service.'
+ }
+ else if(response.payload.data.valid !== undefined && response.payload.data.code){ //applyVoucherCode()
+ this.voucherValidated = true;
+ if(response.payload.data.reason){
+ this.voucherCodeFailedReason = response.payload.data.reason;
+ this.voucherValidated = false;
+ }
+ this.voucherIsChecking = false;
+ }
+ else { //submitForm()
+ this.calculation = response.payload.data;
+ }
},
errorHandler(error) {
this.error = error.message;
@@ -589,9 +675,42 @@
cancelQuotation(){
this.calculation = null;
this.expandPayment = false;
+ },
+ updatedBankDetails(bank){
+ this.item.bank = bank;
+ },
+ removeVoucher(){
+ this.voucherCodeFailedReason = '';
+ this.voucherCode = '';
+ this.showApplyVoucher = false;
+ },
+ applyVoucherCode(){
+ this.voucherIsChecking = true;
+ this.voucherCodeFailedReason = '';
+ this.voucherValidated = false;
+ this.parameters = {
+ voucherCode: this.voucherCode,
+ amount: this.amount,
+ itemId: this.item.id
+ };
+ if(this.voucherCode.trim() !== ''){
+ this.submit(route('api.voucher.validate'), 'post', '', false, false);
+ }
+ else{
+ this.voucherValidated = true;
+ this.voucherIsChecking = false;
+ }
+ },
+ updateVoucherFlag(event){
+ this.voucherCodeFailedReason = '';
+ this.voucherValidated = false;
+ this.voucherCode = event.target.value;
+ },
+ handleSelectedVoucher(value){
+ this.voucherCode = value;
}
},
mixins: [componentHandler],
directives: {money: VMoney}
}
-
\ No newline at end of file
+
diff --git a/resources/assets/vue/components/bookings/forms/ChangeBookingOwnerFormComponent.vue b/resources/assets/vue/components/bookings/forms/ChangeBookingOwnerFormComponent.vue
new file mode 100644
index 00000000..8034fcdc
--- /dev/null
+++ b/resources/assets/vue/components/bookings/forms/ChangeBookingOwnerFormComponent.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
Change Booking Owner
+
+
+
+
+
+
+ Customer Marking
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue b/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue
new file mode 100644
index 00000000..0fc1cd49
--- /dev/null
+++ b/resources/assets/vue/components/bookings/forms/ChooseCurrencyComponent.vue
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+ {{ selectedCurrency.short_code }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ currency.short_code }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/forms/ChooseServiceComponent.vue b/resources/assets/vue/components/bookings/forms/ChooseServiceComponent.vue
new file mode 100644
index 00000000..43d8791e
--- /dev/null
+++ b/resources/assets/vue/components/bookings/forms/ChooseServiceComponent.vue
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+ {{ selectedCurrency.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ currency.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue b/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue
index f8db5603..975c4592 100644
--- a/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue
+++ b/resources/assets/vue/components/bookings/forms/ConfirmQuotationFormComponent.vue
@@ -71,7 +71,7 @@
Cancel
- Confirm Booking
+ Confirm Booking
@@ -120,6 +120,8 @@
payment_method: this.payment_method,
amount: this.amount,
bank_code: this.bank_code,
+ voucher_code: this.calculation.voucher_code,
+ voucher_discount_amount: this.calculation.voucher_discount_amount
}
}
},
@@ -143,4 +145,4 @@
\ No newline at end of file
+
diff --git a/resources/assets/vue/components/bookings/forms/CurrencyConverterComponent.vue b/resources/assets/vue/components/bookings/forms/CurrencyConverterComponent.vue
index a9b0ba80..c8c581ee 100644
--- a/resources/assets/vue/components/bookings/forms/CurrencyConverterComponent.vue
+++ b/resources/assets/vue/components/bookings/forms/CurrencyConverterComponent.vue
@@ -17,7 +17,7 @@
@@ -110,8 +110,8 @@
@@ -178,6 +178,10 @@
required: true,
type: Object
},
+ companySegment: {
+ required: true,
+ type: Array
+ },
id: {
required: true,
type: Number
@@ -211,6 +215,20 @@
created(){
this.serviceType = this.data;
},
+ computed: {
+ currentSegmentNames(){
+ return this.companySegment.map(function (value){
+ return value.name;
+ })
+ },
+ disabledMyr(){
+ if (!this.currentSegmentNames.includes('enable enter MYR rate')) {
+ return true;
+ }
+
+ return false;
+ },
+ },
watch: {
data: {
handler: function (val) {
diff --git a/resources/assets/vue/components/bookings/forms/DeletePoFormComponent.vue b/resources/assets/vue/components/bookings/forms/DeletePoFormComponent.vue
new file mode 100644
index 00000000..b8701f85
--- /dev/null
+++ b/resources/assets/vue/components/bookings/forms/DeletePoFormComponent.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
Are you Sure?
+
Are you sure you want to delete the Purchase Order PDF?
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/assets/vue/components/bookings/forms/EditTransactionGroupFormComponent.vue b/resources/assets/vue/components/bookings/forms/EditTransactionGroupFormComponent.vue
new file mode 100644
index 00000000..4d8dd262
--- /dev/null
+++ b/resources/assets/vue/components/bookings/forms/EditTransactionGroupFormComponent.vue
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
Edit Transaction Group
+
+
+
+
+
+
+
+ Purchase Rate
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/forms/ExportBookingTransactionFormComponent.vue b/resources/assets/vue/components/bookings/forms/ExportBookingTransactionFormComponent.vue
new file mode 100644
index 00000000..889ea3e7
--- /dev/null
+++ b/resources/assets/vue/components/bookings/forms/ExportBookingTransactionFormComponent.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+ Start Date
+
+
+
+
+
+ End Date
+
+
+
+
+ Download
+
+
+
+
+
+
+
+
+
diff --git a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue
index cde4d84d..7b7ec3d4 100644
--- a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue
+++ b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue
@@ -7,7 +7,14 @@
Purchase Order
-
In order for us to process your order, you will need to provide us with your purchase order information.
+
In order for us to process your order, you will need to provide us with your purchase order information.
+
+
+
+
+
+
Any Purchase Orders that aren't submitted within 60 days will be closed for editing.
+
@@ -37,13 +44,13 @@