Merge remote-tracking branch 'origin/master'

This commit is contained in:
Omair Saleh
2024-04-30 17:46:42 +08:00
101 changed files with 4563 additions and 97 deletions
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class CurrencyRateIsNotEqual implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('currency_rate', '!=', $value);
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Illuminate\Database\Eloquent\Builder;
class HasPendingVerifyTransaction implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('transactions', function ($q) {
$q->where('status', ApprovalStatus::PENDING_VERIFICATION);
});
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class IsPartialRefund implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owner', function ($q) use ($value) {
if ($value) {
$q->where('original_amount', '!=', DB::raw('transactions.original_amount'));
} else {
$q->where('original_amount', DB::raw('transactions.original_amount'));
}
});
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class OwnerDoesNotHaveTransactionType implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDoesntHave('owner', function($query) use($value) {
return $query->whereHas('transactions', function($query) use($value) {
return $query->where('transactions.type', $value);
});
});
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class OwnerHasTransactionType implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owner', function($query) use($value) {
return $query->whereHas('transactions', function($query) use($value) {
return $query->where('transactions.type', $value);
});
});
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class ReceiverIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereIn('receiver', $value);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use Illuminate\Database\Eloquent\Builder;
class WithoutBillGroup implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDoesntHave('billGroup');
}
}
@@ -14,6 +14,7 @@ use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation;
use App\Classes\Modules\Transactions\ControllersLogic\UpdateRefundTransactionStatusLogic;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
@@ -48,6 +49,9 @@ class CreateBookingRefundLogic extends AbstractControllerLogic
/** @var CreatesTransaction */
private $createsTransaction;
/** @var UpdateRefundTransactionStatusLogic */
private $updateRefundTransactionStatusLogic;
/**
* CreateBookingPaymentLogic constructor.
* @param FetchesBookingQuotation $fetchBookingQuotation
@@ -55,14 +59,16 @@ class CreateBookingRefundLogic extends AbstractControllerLogic
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatesTransaction $createsTransaction
* @param UpdateRefundTransactionStatusLogic $updateRefundTransactionStatusLogic
*/
public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction)
public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, UpdateRefundTransactionStatusLogic $updateRefundTransactionStatusLogic)
{
$this->fetchBookingQuotation = $fetchBookingQuotation;
$this->fetchesTransaction = $fetchesTransaction;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createsTransaction = $createsTransaction;
$this->updateRefundTransactionStatusLogic = $updateRefundTransactionStatusLogic;
}
/**
@@ -75,12 +81,14 @@ class CreateBookingRefundLogic extends AbstractControllerLogic
$transaction = $this->fetchesTransaction->execute(['id' => $request->route('payment_id')]);
if ($transaction->transactions()->bills()->first()) {
throw new MalformedRequestException('Booking under white form cannot request for refund');
}
$booking = $transaction->owner;
$invoice = $booking->transactions()->where('type', TransactionType::INVOICE)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first();
if(auth()->user()->type === 3) {
throw new MalformedRequestException('You do not have the permission to refund the order.');
}
$billNumber = $this->generatesTransactionBillNumber->execute('RFD-');
$refund = $transaction->transactions()->refunds()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->sum('original_amount');
@@ -92,7 +100,8 @@ class CreateBookingRefundLogic extends AbstractControllerLogic
$refundAmount = bcdiv($request->input('amount'), $transaction->currency_rate, 7);
// refund service charges if is fully refund
$refundTotal = ($refund + $request->input('amount')) == $transaction->original_amount ? $refundAmount + $transaction->service_charge + $transaction->tax : $refundAmount;
$isFullyRefund = ($refund + $request->input('amount')) == $transaction->original_amount;
$refundTotal = $isFullyRefund ? $refundAmount + $transaction->service_charge + $transaction->tax : $refundAmount;
$object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id,
1, PaymentMethodType::CASH,
@@ -100,9 +109,30 @@ class CreateBookingRefundLogic extends AbstractControllerLogic
$transaction->original_currency_id, $transaction->currency_rate,
0, 0, null, ApprovalStatus::PENDING_VERIFICATION, [], $transaction->bill_no);
$refund_transaction = $this->createsTransaction->execute($transaction, $object);
$bookingInWhiteForm = $transaction->transactions()->bills()->first();
// create supplier refund
if ($bookingInWhiteForm) {
$billNumber = $this->generatesTransactionBillNumber->execute('SRFD-');
$supplierRefundTotal = bcdiv($request->input('amount'), $bookingInWhiteForm->currency_rate, 7);
$object = new TransactionObject($billNumber, TransactionType::SUPPLIER_REFUND, 1, $bookingInWhiteForm->issuer,
1, PaymentMethodType::CASH,
$supplierRefundTotal, $request->input('amount'), 1,
$transaction->original_currency_id, $bookingInWhiteForm->currency_rate,
0, 0, null, ApprovalStatus::PENDING_VERIFICATION, [], $transaction->bill_no);
$transaction = $this->createsTransaction->execute($transaction, $object);
} else {
$request->route()->setParameter('id', $refund_transaction->id);
$request->route()->setParameter('status', ApprovalStatus::APPROVED);
$this->updateRefundTransactionStatusLogic->execute($request);
return $this->resourceResponse(new TransactionResource($transaction));
}
return $this->resourceResponse(new TransactionResource($refund_transaction));
}
@@ -81,7 +81,7 @@ class UpdateBookingAmountLogic extends AbstractControllerLogic
$minimum_amount = $booking->fix_amount - $this->calculatesBookingOutstanding->execute($booking);
if ((float)$input_amount < $minimum_amount) {
if (((float)$input_amount + 0.01) < (float)$minimum_amount) {
throw new MalformedRequestException('Booking Amount cannot be less than '. $minimum_amount .'.');
}
@@ -24,10 +24,22 @@ class CalculatesBookingCurrencyAverageRate
public function execute(Booking $booking, $type){
$transaction = $booking->transactions()
->where('type', TransactionType::PAYMENT)
->latest()->get()[0];
$voucherRedemption = $transaction->voucherRedemption;
$discount = 0;
if ($voucherRedemption) {
$discount = $voucherRedemption->value;
}
if ($type == TransactionType::PAYMENT) {
$totalPayment = $booking->fix_currency_id === 1 ? $booking->transactions()->payments()->complete()->sum('original_amount') :
$booking->transactions()->payments()->complete()->selectRaw('sum(amount - service_charge - tax) as sub_total')->get()->sum('sub_total');
return $this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id) / $totalPayment;
return $this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id) / ($totalPayment + $discount);
}
else if ($type == TransactionType::BILL) {
@@ -13,20 +13,25 @@ class CalculatesBookingOutstanding
/** @var CalculatesBookingFloatingAmount */
private $calculatesBookingFloatingAmount;
/** @var CalculatesBookingRefundAmount */
private $calculatesBookingRefundAmount;
/**
* CalculatesBookingOutstanding constructor.
* @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount
* @param CalculatesBookingFloatingAmount $calculatesBookingFloatingAmount
* @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount
*/
public function __construct(CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingFloatingAmount $calculatesBookingFloatingAmount)
public function __construct(CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingFloatingAmount $calculatesBookingFloatingAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount)
{
$this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount;
$this->calculatesBookingFloatingAmount = $calculatesBookingFloatingAmount;
$this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount;
}
public function execute(Booking $booking){
return $booking->fix_amount - $this->calculatesBookingFloatingAmount->execute($booking, $booking->fix_currency_id) - $this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id);
return $booking->fix_amount - $this->calculatesBookingFloatingAmount->execute($booking, $booking->fix_currency_id) - $this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id) + $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id);
}
}
@@ -9,7 +9,7 @@ class CalculatesBookingRefundAmount
{
public function execute(Booking $booking, int $type, ?string $payment_reference = null): float
{
$refundAmounts = $booking->transactions()->payments()->get()->map(function ($payment) use ($type) {
$refundAmounts = $booking->transactions()->payments()->complete()->get()->map(function ($payment) use ($type) {
return $this->calculateRefundAmount($payment, $type);
});
@@ -13,7 +13,7 @@ class CalculatesBookingTransferredAmount
public function execute(Booking $booking){
return $booking->transactions()->payments()->complete()->whereHas('transactions', function($query){
return $query->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
return $query->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->where('type', TransactionType::BILL);
})->sum('original_amount');
}
@@ -0,0 +1,62 @@
<?php
namespace App\Classes\Modules\Remarks\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
use App\Classes\Modules\Remarks\Processors\CreateRemarkProcessor;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Classes\Exceptions\MalformedRequestException;
use App\Http\Resources\RemarkResource;
class CreateRemarkLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Created Remark',
'message' => 'You have successfully created a new Remark'
];
}
/** @var CreateRemarkProcessor */
private $createRemarkProcessor;
/**
* CreateRemarkLogic constructor.
* @param CreateRemarkProcessor $createRemarkProcessor
*/
public function __construct(CreateRemarkProcessor $createRemarkProcessor)
{
$this->createRemarkProcessor = $createRemarkProcessor;
}
/**
* @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
{
$classs = '\\App\\Models\\' . Str::studly($request->input('model_type'));
if (!class_exists($classs)) {
throw new MalformedRequestException('Unable to process this entity');
}
$remarkOwner = $classs::find($request->route('id'));
$remarkObject = new RemarkObject($request->input('content'), auth()->user()->id);
$remmark = $this->createRemarkProcessor->execute($remarkOwner, $remarkObject);
return $this->resourceResponse(new RemarkResource($remmark));
}
}
@@ -0,0 +1,66 @@
<?php
namespace App\Classes\Modules\Remarks\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Remarks\Services\DeletesRemark;
use App\Classes\Modules\Remarks\Services\FetchesRemark;
use App\Classes\Modules\Remarks\Standards\Rules\CanDeleteRemark;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DeleteRemarkLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Deleted Remark',
'message' => 'You have successfully deleted a Remark'
];
}
/** @var CanDeleteRemark */
private $canDeleteRemark;
/** @var DeletesRemark */
private $deletesRemark;
/** @var FetchesRemark */
private $fetchesRemark;
/**
* DeleteRemarkControllersLogic constructor.
* @param CanDeleteRemark $canDeleteRemark
* @param DeletesRemark $deletesRemark
* @param FetchesRemark $fetchesRemark
*/
public function __construct(CanDeleteRemark $canDeleteRemark, DeletesRemark $deletesRemark, FetchesRemark $fetchesRemark)
{
$this->canDeleteRemark = $canDeleteRemark;
$this->deletesRemark = $deletesRemark;
$this->fetchesRemark = $fetchesRemark;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canDeleteRemark->passes();
$query = $this->fetchesRemark->execute(['id' => $request->route('id')]);
$this->deletesRemark->execute($query);
return $this->response([]);
}
}
@@ -0,0 +1,59 @@
<?php
namespace App\Classes\Modules\Remarks\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Remarks\Services\FetchesRemark;
use App\Classes\Modules\Remarks\Standards\Rules\CanFetchRemark;
use App\Http\Resources\RemarkResource;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchRemarkLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Remark',
'message' => 'You have successfully retrieved a Remark'
];
}
/** @var CanFetchRemark */
private $canFetchRemark;
/** @var FetchesRemark */
private $fetchesRemark;
/**
* FetchRemarkControllersLogic constructor.
* @param CanFetchRemark $canFetchRemark
* @param FetchesRemark $fetchesRemark
*/
public function __construct(CanFetchRemark $canFetchRemark, FetchesRemark $fetchesRemark)
{
$this->canFetchRemark = $canFetchRemark;
$this->fetchesRemark = $fetchesRemark;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canFetchRemark->passes();
$query = $this->fetchesRemark->execute(['id' => $request->route('id')]);
return $this->resourceResponse(new RemarkResource($query));
}
}
@@ -0,0 +1,59 @@
<?php
namespace App\Classes\Modules\Remarks\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Remarks\Services\ListsRemarks;
use App\Classes\Modules\Remarks\Standards\Rules\CanListRemarks;
use App\Http\Resources\RemarkResource;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListRemarksLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Remarks',
'message' => 'You have successfully retrieved a list of Remarks'
];
}
/** @var CanListRemarks */
private $canListRemarks;
/** @var ListsRemarks */
private $listsRemarks;
/**
* ListRemarksLogic constructor.
* @param CanListRemarks $canListRemarks
* @param ListsRemarks $listsRemarks
*/
public function __construct(CanListRemarks $canListRemarks, ListsRemarks $listsRemarks)
{
$this->canListRemarks = $canListRemarks;
$this->listsRemarks = $listsRemarks;
}
/**
* @param Request $request
* @return JsonResponse
* @throws ErrorException
*/
public function logic(Request $request) : JsonResponse
{
$this->canListRemarks->passes();
$query = $this->listsRemarks->execute($this->listsRemarks->deserializeFilters($request->input('filters')));
return $this->collectionResponse(RemarkResource::collection($query));
}
}
@@ -0,0 +1,71 @@
<?php
namespace App\Classes\Modules\Remarks\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Remarks\Services\UpdatesRemark;
use App\Classes\Modules\Remarks\Services\FetchesRemark;
use App\Classes\Modules\Remarks\Standards\Rules\CanUpdateRemark;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
use App\Http\Resources\RemarkResource;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UpdateRemarkLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Updated Remark',
'message' => 'You have successfully updated the Remark'
];
}
/** @var CanUpdateRemark */
private $canUpdateRemark;
/** @var UpdatesRemark */
private $updatesRemark;
/** @var FetchesRemark */
private $fetchesRemark;
/**
* UpdateRemarkLogic constructor.
* @param CanUpdateRemark $canUpdateRemark
* @param UpdatesRemark $updatesRemark
* @param FetchesRemark $fetchesRemark
*/
public function __construct(CanUpdateRemark $canUpdateRemark, UpdatesRemark $updatesRemark, FetchesRemark $fetchesRemark)
{
$this->canUpdateRemark = $canUpdateRemark;
$this->updatesRemark = $updatesRemark;
$this->fetchesRemark = $fetchesRemark;
}
/**
* @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
{
$object = new RemarkObject($request->input('content'), auth()->user()->id);
$this->canUpdateRemark->passes($object);
$query = $this->fetchesRemark->execute(['id' => $request->route('id')]);
$query = $this->updatesRemark->execute($query, $object);
return $this->resourceResponse(new RemarkResource($query));
}
}
@@ -0,0 +1,38 @@
<?php
namespace App\Classes\Modules\Remarks\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class RemarkObject implements DataTransferObject
{
/** @var int */
private $commenterID;
/** @var string*/
private $content;
public function __construct(string $content, int $commenterID)
{
$this->commenterID = $commenterID;
$this->content = $content;
}
/**
* @return int
*/
public function getCommenterId(): string
{
return $this->commenterID;
}
/**
* @return string
*/
public function getContent(): ?string
{
return $this->content;
}
}
@@ -0,0 +1,47 @@
<?php
namespace App\Classes\Modules\Remarks\Processors;
use App\Classes\General\Interfaces\Remarkable;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
use App\Classes\Modules\Remarks\Services\CreatesRemark;
use App\Classes\Modules\Remarks\Standards\Rules\CanCreateRemark;
class CreateRemarkProcessor
{
/** @var CreatesRemark */
private $createsRemark;
/** @var CanCreateRemark */
private $canCreateRemark;
/**
* CreateRemarkProcessor constructor.
* @param CreatesRemark $createsRemark
* @param CanCreateRemark $canCreateRemark
*/
public function __construct(CreatesRemark $createsRemark, CanCreateRemark $canCreateRemark)
{
$this->createsRemark = $createsRemark;
$this->canCreateRemark = $canCreateRemark;
}
/**
* @param Remarkable $remarkable
* @param RemarkObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function execute(Remarkable $remarkable, RemarkObject $object){
$this->canCreateRemark->passes($object);
return $this->createsRemark->execute($remarkable, $object);
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Classes\Modules\Remarks\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\General\Eloquent\AbstractUpdateRelationshipRecord;
use App\Classes\General\Interfaces\Remarkable;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
use App\Models\Container;
use App\Models\Remark;
use App\Models\User;
class CreatesRemark extends AbstractUpdateRelationshipRecord
{
/**
* @param Remarkable $remarkable
* @param RemarkObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Remarkable $remarkable, RemarkObject $object) {
$model = new Remark();
$model->commenter_id = $object->getCommenterId();
$model->content = $object->getContent();
return $this->handler($remarkable->remarks(), $model);
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Classes\Modules\Remarks\Services;
use App\Classes\General\Eloquent\AbstractDeleteRecord;
use App\Models\Remark;
class DeletesRemark extends AbstractDeleteRecord
{
/**
* @param Remark $model
* @return mixed
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Remark $model) {
return $this->handler($model);
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Remarks\Services;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\Remark;
class FetchesRemark extends AbstractFetchRecord
{
/** @var Remark */
private $repository;
/**
* FetchesRemark constructor.
* @param Remark $repository
*/
public function __construct(Remark $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Remarks\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\Remark;
class ListsRemarks extends AbstractListRecord
{
/** @var Remark */
private $repository;
/**
* ListsRemarks constructor.
* @param Remark $repository
*/
public function __construct(Remark $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Classes\Modules\Remarks\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
use App\Models\Remark;
class UpdatesRemark extends AbstractUpdateRecord
{
/**
* @param Remark $model
* @param RemarkObject $object
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Remark $model, RemarkObject $object) {
$model->commenter_id = $object->getCommenterId();
$model->content = $object->getContent();
return $this->handler($model);
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\Classes\Modules\Remarks\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
use App\Classes\Modules\Remarks\Standards\Validators\RemarkValidation;
class CanCreateRemark extends AbstractRule
{
/** @var RemarkValidation */
private $RemarkValidation;
/**
* CanCreateRemark constructor.
* @param RemarkValidation $RemarkValidation
*/
public function __construct(RemarkValidation $RemarkValidation)
{
$this->RemarkValidation = $RemarkValidation;
}
/**
* @return bool
*/
protected function authorized($object): bool
{
// TODO Set Authorization rules
return true;
}
/**
* @param RemarkObject $object
* @return bool
* @throws \App\Classes\Exceptions\RequestValidationException
*/
protected function validators($object): bool
{
return $this->RemarkValidation->validate($object);
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,43 @@
<?php
namespace App\Classes\Modules\Remarks\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
class CanDeleteRemark extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
// TODO Set Authorization rules
return true;
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function validators($object): bool
{
return true;
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,43 @@
<?php
namespace App\Classes\Modules\Remarks\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
class CanFetchRemark extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
// TODO Set Authorization rules
return true;
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function validators($object): bool
{
return true;
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,43 @@
<?php
namespace App\Classes\Modules\Remarks\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
class CanListRemarks extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
// TODO Set Authorization rules
return true;
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function validators($object): bool
{
return true;
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\Classes\Modules\Remarks\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
use App\Classes\Modules\Remarks\Standards\Validators\RemarkValidation;
class CanUpdateRemark extends AbstractRule
{
/** @var RemarkValidation */
private $RemarkValidation;
/**
* CanUpdateRemark constructor.
* @param RemarkValidation $RemarkValidation
*/
public function __construct(RemarkValidation $RemarkValidation)
{
$this->RemarkValidation = $RemarkValidation;
}
/**
* @return bool
*/
protected function authorized($object): bool
{
// TODO Set Authorization rules
return true;
}
/**
* @param RemarkObject $object
* @return bool
* @throws \App\Classes\Exceptions\RequestValidationException
*/
protected function validators($object): bool
{
return $this->RemarkValidation->validate($object);
}
/**
* @param RemarkObject $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,41 @@
<?php
namespace App\Classes\Modules\Remarks\Standards\Validators;
use App\Classes\General\Abstracts\AbstractValidation;
use App\Classes\Modules\Remarks\DataTransferObjects\RemarkObject;
class RemarkValidation extends AbstractValidation
{
/**
* @param RemarkObject $object
* @return array
*/
protected function data($object): array {
return [
'commenter_id' => $object->getCommenterId(),
'content' => $object->getContent(),
];
}
/**
* @return array
*/
protected function rules(): array {
return [
'commenter_id' => 'required',
'content' => 'required',
];
}
/**
* @return array
*/
protected function messages(): array {
return [];
}
}
@@ -0,0 +1,93 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Documents\Services\ApprovesDocument;
use App\Classes\Modules\Documents\Services\RejectsDocument;
use App\Classes\Modules\Transactions\Services\CalculatesBillGroupPaymentAmount;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ApproveBillGroupPaymentVerificationLogic extends AbstractControllerLogic
{
/**
* ApproveBillGroupPaymentVerificationLogic constructor.
* @param FetchesTransaction $fetchesTransaction
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param ApprovesDocument $approvesDocument
* @param RejectsDocument $rejectsDocument
* @param CalculatesBillGroupPaymentAmount $calculatesBillGroupPaymentAmount
*/
public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, ApprovesDocument $approvesDocument, RejectsDocument $rejectsDocument, CalculatesBillGroupPaymentAmount $calculatesBillGroupPaymentAmount)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->approvesDocument = $approvesDocument;
$this->rejectsDocument = $rejectsDocument;
$this->calculatesBillGroupPaymentAmount = $calculatesBillGroupPaymentAmount;
}
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Payment Status',
'message' => 'You have successfully updated the payment status'
];
}
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var ApprovesDocument */
private $approvesDocument;
/** @var RejectsDocument */
private $rejectsDocument;
/** @var CalculatesBillGroupPaymentAmount */
private $calculatesBillGroupPaymentAmount;
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request) : JsonResponse
{
$status = $request->route('status');
$transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]);
$status === 'approve' ? $this->approvesDocument->execute($transaction->documents()->first()) : $this->rejectsDocument->execute($transaction->documents()->first());
$this->updatesTransactionStatus->execute($transaction, $status === 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED);
$billGroup = $transaction->owner;
$billGroupPayment = $this->calculatesBillGroupPaymentAmount->execute($billGroup);
if ($status === 'reject') {
$billGroup->status = ApprovalStatus::PENDING_SUBMISSION;
$billGroup->save();
} else {
if ($billGroupPayment['outstanding_amount'] <= 0 && $billGroupPayment['floating_amount'] <= 0) {
$billGroup->status = ApprovalStatus::APPROVED;
$billGroup->save();
}
}
return $this->response([]);
}
}
@@ -0,0 +1,94 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Transactions\Services\CalculatesBillGroupPaymentAmount;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Document;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CreateBillGroupPaymentProofDocumentLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Payment Proof Document',
'message' => 'You have successfully submitted your payment proof document'
];
}
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFile;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CalculatesBillGroupPaymentAmount */
private $calculatesBillGroupPaymentAmount;
/**
* CreateBillGroupPaymentProofDocumentLogic constructor.
* @param FetchesTransaction $fetchesTransaction
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CalculatesBillGroupPaymentAmount $calculatesBillGroupPaymentAmount
*/
public function __construct(FetchesTransaction $fetchesTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, UpdatesTransactionStatus $updatesTransactionStatus, CalculatesBillGroupPaymentAmount $calculatesBillGroupPaymentAmount)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->calculatesBillGroupPaymentAmount = $calculatesBillGroupPaymentAmount;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request) : JsonResponse
{
$transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]);
$object = new DocumentObject(DocumentType::BILL_GROUP_PAYMENT_PROOF, $request->input('files'), '', ApprovalStatus::PENDING_VERIFICATION, 'bill_group_payments');
/** @var Document $document */
$document = $this->createsDocument->execute($transaction, $object);
$this->createsFile->execute($document, $object);
$this->updatesTransactionStatus->execute($transaction, ApprovalStatus::PENDING_VERIFICATION);
$billGroup = $transaction->owner;
$billGroupPayment = $this->calculatesBillGroupPaymentAmount->execute($billGroup);
if ($billGroupPayment['outstanding_amount'] <= 0 && $billGroup->transactions()->where('status', ApprovalStatus::PENDING_SUBMISSION)->count() === 0) {
$billGroup->status = ApprovalStatus::PENDING_VERIFICATION;
$billGroup->save();
}
return $this->response([]);
}
}
@@ -0,0 +1,95 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\CalculatesBillGroupPaymentAmount;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\FetchesBillGroup;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\BillGroupResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CreateBillGroupPaymentTransactionLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Create Bill Group Payment Transaction',
'message' => 'You have successfully created payment for this Bill Group'
];
}
/** @var FetchesBillGroup */
private $fetchesBillGroup;
/** @var GeneratesTransactionBillNumber */
private $generatesTransactionBillNumber;
/** @var CreatesTransaction */
private $createsTransaction;
/** @var CalculatesBillGroupPaymentAmount */
private $calculatesBillGroupPaymentAmount;
/**
* CreateBillGroupPaymentTransactionLogic constructor.
* @param FetchesBillGroup $fetchesBillGroup
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatesTransaction $createsTransaction
* @param CalculatesBillGroupPaymentAmount $calculatesBillGroupPaymentAmount
*/
public function __construct(FetchesBillGroup $fetchesBillGroup, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBillGroupPaymentAmount $calculatesBillGroupPaymentAmount)
{
$this->fetchesBillGroup = $fetchesBillGroup;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createsTransaction = $createsTransaction;
$this->calculatesBillGroupPaymentAmount = $calculatesBillGroupPaymentAmount;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request) : JsonResponse
{
$billGroup = $this->fetchesBillGroup->execute(['id' => $request->route('id')]);
$billGroupPayment = $this->calculatesBillGroupPaymentAmount->execute($billGroup);
$outstanding_amount = $billGroupPayment['outstanding_amount'];
if ($billGroupPayment['outstanding_amount'] <= 0) {
if ($billGroup->transactions()->whereIn('status', [ApprovalStatus::PENDING_SUBMISSION, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->count() !== 0) {
throw new MalformedRequestException('Invalid bill group, payment transaction already exist.');
}
}
$payAmount = floatval(str_replace(',', '', $request->input('payAmount')));
if($payAmount > round($outstanding_amount, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding_amount .'.');
if ($billGroupPayment['outstanding_amount'] == 0 && $payAmount == 0) {
$billGroup->status = ApprovalStatus::APPROVED;
$billGroup->save();
} else {
$billNumber = $this->generatesTransactionBillNumber->execute('SPLR-PYMT-');
$transaction_object = new TransactionObject($billNumber, TransactionType::SUPPLIER_PAYMENT, $billGroup->issuer,
$billGroup->receiver, $billGroup->issuerCompany->banks()->where('default', true)->first()->id, PaymentMethodType::CASH,
$payAmount, $payAmount, 1, 1, 1,
0, 0, null, ApprovalStatus::PENDING_SUBMISSION, [], '');
$this->createsTransaction->execute($billGroup, $transaction_object);
}
return $this->resourceResponse(new BillGroupResource($billGroup));
}
}
@@ -0,0 +1,195 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\Exceptions\MalformedRequestException;
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 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 App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\BillGroup;
use App\Models\Transaction;
class CreateSupplierBillGroupLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Create Supplier White Form Order',
'message' => 'You have successfully created currency supplier white form order'
];
}
/** @var FetchesCompany */
private $fetchesCompany;
/** @var CreatesTransaction */
private $createsTransaction;
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFile;
/** @var GeneratesTransactionBillNumber */
private $generatesTransactionBillNumber;
/** @var UpdateGroupLogic */
private $updateGroupLogic;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/**
* CreateSupplierBillGroupLogic constructor.
* @param FetchesCompany $fetchesCompany
* @param CreatesTransaction $createsTransaction
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFile
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param UpdateGroupLogic $updateGroupLogic
* @param UpdatesTransactionStatus $updatesTransactionStatus
*/
public function __construct(FetchesCompany $fetchesCompany, CreatesTransaction $createsTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber, UpdateGroupLogic $updateGroupLogic, UpdatesTransactionStatus $updatesTransactionStatus)
{
$this->fetchesCompany = $fetchesCompany;
$this->createsTransaction = $createsTransaction;
$this->createsDocument = $createsDocument;
$this->createsFile = $createsFile;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->updateGroupLogic = $updateGroupLogic;
$this->updatesTransactionStatus = $updatesTransactionStatus;
}
public function logic(Request $request) : JsonResponse
{
$supplier = $this->fetchesCompany->execute(['id' => $request->route('id')]);
$payments = $request->input('payments');
$supplierRefunds = $request->input('supplierRefunds');
foreach ($supplierRefunds as $supplierRefund) {
$refund = Transaction::find($supplierRefund['id']);
if ($refund->owner->transactions()->where('type', TransactionType::BILL)->first()->issuer !== $supplier->id) {
throw new MalformedRequestException('The supplier refund and bill group does not belongs to same supplier.');
}
if ($refund->type !== TransactionType::SUPPLIER_REFUND) {
throw new MalformedRequestException('Only transaction type supplier refund can be used for bill refund.');
}
if ($refund->currency_rate == 1) {
throw new MalformedRequestException('Supplier refund with currecy rate 1 cannot be used for bill refund.');
}
}
$amount = 0;
$original_amount = 0;
foreach ($payments as $payment) {
$amount += round($payment['amount'], 2);
$original_amount += round($payment['original_amount'], 2);
}
$service_charges = 0;
if ($supplier->id === 4548 || $supplier->id === 2729) {
$amount = round(floatval(str_replace(',', '', $request->input('payment_total'))), 2);
} else {
$service_charges = round(floatval(str_replace(',', '', $request->input('service_charges'))), 2);
}
$rate = $original_amount / $amount;
if ($supplier->id === 4548 || $supplier->id === 2729) {
$request['rate'] = $rate;
$request['supplier_id'] = $supplier->id;
foreach ($payments as $payment) {
$request->route()->setParameter('id', $payment['id']);
$this->updateGroupLogic->execute($request);
}
}
$billGroup = new BillGroup();
$billGroup->issuer = $supplier->id;
$billGroup->receiver = 1;
$billGroup->reference = $this->generatesTransactionBillNumber->execute('BSPO-');
$billGroup->amount = $amount;
$billGroup->original_amount = $original_amount;
$billGroup->currency_id = 1;
$billGroup->original_currency_id = $payments[0]['original_currency']['id'];
$billGroup->currency_rate = $rate;
$billGroup->tax = 0;
$billGroup->service_charge = $service_charges;
$billGroup->status = ApprovalStatus::PENDING_SUBMISSION;
$billGroup->save();
foreach ($payments as $payment) {
$billGroup->groups()->sync($payment['id'], false);
}
//create bill refund
$amount += $service_charges;
foreach ($supplierRefunds as $supplierRefund) {
$refund = Transaction::find($supplierRefund['id']);
$deductedRefunds = $refund->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->get();
$refundDeductableAmount = round(($refund->amount - $deductedRefunds->sum('amount')), 2);
$refundDeductableOriginalAmount = round(($refund->original_amount - $deductedRefunds->sum('original_amount')), 2);
$amount -= $refundDeductableAmount;
$original_amount -= $refundDeductableOriginalAmount;
if ($amount > 0) {
$deductedRefundAmount = $refundDeductableAmount;
$deductedRefundOriginalAmount = $refundDeductableOriginalAmount;
$this->updatesTransactionStatus->execute($refund, ApprovalStatus::COMPLETED);
}
if ($amount < 0) {
$deductedRefundAmount = $refundDeductableAmount + $amount;
$deductedRefundOriginalAmount = $refundDeductableOriginalAmount + $original_amount;
}
$billNumber = $this->generatesTransactionBillNumber->execute('BRFD-');
$object = new TransactionObject($billNumber, TransactionType::BILL_REFUND, $supplier->id, 1,
1, PaymentMethodType::CASH,
$deductedRefundAmount, $deductedRefundOriginalAmount, $refund->currency_id,
$refund->original_currency_id, $deductedRefundOriginalAmount / $deductedRefundAmount,
0, 0, null, ApprovalStatus::APPROVED, []);
$transaction = $this->createsTransaction->execute($refund, $object);
$billGroup->billRefunds()->sync($transaction->id, false);
}
return $this->response([]);
}
}
@@ -129,6 +129,10 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic
$service_charge += $row->service_charge;
}
$transferFee = (float)$this->createSupplierTransactionProcessor->getTransferTransactions()->sum('service_charge');
$original_amount += $transferFee;
$amount += $transferFee / $currency_rate;
$group->issuer = $issuer;
$group->receiver = $receiver;
$group->reference = $this->generatesTransactionBillNumber->execute('SPO-');
@@ -0,0 +1,83 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\Services\FetchesBillGroup;
use App\Classes\Modules\Transactions\Services\DeletesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Http\Resources\BillGroupResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DeleteBillGroupLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Delete Bill Group Transaction',
'message' => 'You have successfully deleted this Bill Group Transaction'
];
}
/** @var FetchesBillGroup */
private $fetchesBillGroup;
/** @var DeletesTransaction */
private $deletesTransaction;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/**
* DeleteBillGroupLogic constructor.
* @param FetchesBillGroup $fetchesBillGroup
* @param DeletesTransaction $deletesTransaction
* @param UpdatesTransactionStatus $updatesTransactionStatus
*/
public function __construct(FetchesBillGroup $fetchesBillGroup, DeletesTransaction $deletesTransaction, UpdatesTransactionStatus $updatesTransactionStatus)
{
$this->fetchesBillGroup = $fetchesBillGroup;
$this->deletesTransaction = $deletesTransaction;
$this->updatesTransactionStatus = $updatesTransactionStatus;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request) : JsonResponse
{
$billGroup = $this->fetchesBillGroup->execute(['id' => $request->route('id')]);
$transactions = $billGroup->transactions()->get();
foreach($transactions as $transaction) {
$this->deletesTransaction->execute($transaction);
}
$groups = $billGroup->groups()->get();
foreach($groups as $group) {
$billGroup->groups()->detach($group->id);
}
$billRefunds = $billGroup->billRefunds()->get();
foreach($billRefunds as $billRefund) {
$billGroup->billRefunds()->detach($billRefund->id);
$this->deletesTransaction->execute($billRefund);
$this->updatesTransactionStatus->execute($billRefund->owner, ApprovalStatus::APPROVED);
}
$billGroup->delete();
return $this->resourceResponse(new BillGroupResource($billGroup));
}
}
@@ -0,0 +1,42 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\Services\ListsBillGroups;
use App\Http\Resources\BillGroupResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListBillGroupsLogic extends AbstractControllerLogic
{
/**
* ListTransactionsLogic constructor.
* @param ListsBillGroups $listsBillGroups
*/
public function __construct(ListsBillGroups $listsBillGroups)
{
$this->listsBillGroups = $listsBillGroups;
}
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Bill Groups',
'message' => 'You have successfully retrieved a list of bill groups'
];
}
/** @var ListsBillGroups */
private $listsBillGroups;
public function logic(Request $request) : JsonResponse
{
$query = $this->listsBillGroups->execute($this->listsBillGroups->deserializeFilters($request->input('filters')));
return $this->collectionResponse(BillGroupResource::collection($query));
}
}
@@ -125,6 +125,18 @@ class UpdateGroupLogic extends AbstractControllerLogic
$billTransaction = $this->updatesTransaction->execute($transaction, $object);
$supplierRefundTransactions = $transaction->owner->transactions()->supplierRefunds()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->get();
foreach ($supplierRefundTransactions as $supplierRefundTransaction) {
$claimBefore = $supplierRefundTransaction->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->exists();
if (!$claimBefore) {
$supplierRefundTransaction->currency_rate = $rate;
$supplierRefundTransaction->amount = $supplierRefundTransaction->original_amount / $rate;
$supplierRefundTransaction->save();
}
}
$transferTransaction = $transaction->transactions()->where('type', TransactionType::TRANSFER_FEE)->first();
$transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant);
@@ -2,8 +2,9 @@
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\ControllersLogic\UpdateBookingAmountLogic;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
@@ -15,7 +16,8 @@ use Illuminate\Http\Request;
use App\Classes\Modules\Wallets\Processors\CreditWalletProcessor;
use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\ValueObjects\Constants\TransactionType;
use Illuminate\Support\Facades\Auth;
class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic
{
@@ -51,6 +53,9 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic
/** @var CalculatesBookingRefundAmount */
private $calculatesBookingRefundAmount;
/** @var UpdateBookingAmountLogic */
private $updateBookingAmountLogic;
/**
* CreatePaymentVerificationDocumentLogic constructor.
* @param FetchesCompany $fetchesCompany
@@ -60,8 +65,9 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic
* @param CreditWalletProcessor $creditWalletProcessor
* @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount
* @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount
* @param UpdateBookingAmountLogic $updateBookingAmountLogic
*/
public function __construct(FetchesCompany $fetchesCompany, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, DeletesDocument $deletesDocument, CreditWalletProcessor $creditWalletProcessor, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount)
public function __construct(FetchesCompany $fetchesCompany, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, DeletesDocument $deletesDocument, CreditWalletProcessor $creditWalletProcessor, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, UpdateBookingAmountLogic $updateBookingAmountLogic)
{
$this->fetchesCompany = $fetchesCompany;
$this->fetchesTransaction = $fetchesTransaction;
@@ -70,6 +76,7 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic
$this->creditWalletProcessor = $creditWalletProcessor;
$this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount;
$this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount;
$this->updateBookingAmountLogic = $updateBookingAmountLogic;
}
/**
@@ -79,21 +86,43 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic
*/
public function logic(Request $request) : JsonResponse
{
if(auth()->user()->type === 3) {
throw new MalformedRequestException('You do not have the permission to refund the order.');
}
$refundTransaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]);
$refundTransaction = $this->updatesTransactionStatus->execute($refundTransaction, $request->route('status'));
$paymentTransaction = $refundTransaction->owner;
$supplierRefundTransaction = $paymentTransaction->transactions()->supplierRefunds()->where('status', [ApprovalStatus::PENDING_VERIFICATION])->first();
$booking = $paymentTransaction->owner;
$reference = $refundTransaction->amount == $paymentTransaction->amount ? 'Fully Refund for Ref. ' . $booking->marking : 'Partially Refund for Ref. ' . $booking->marking;
$refundAmount = $this->calculatesBookingRefundAmount->calculateRefundAmount($paymentTransaction, $booking->fix_currency_id);
$paidAmount = $paymentTransaction->original_amount - $refundAmount;
if ($refundTransaction->status == ApprovalStatus::APPROVED) {
$this->creditWalletProcessor->execute($booking->company, $refundTransaction->type, $refundTransaction->amount, $reference);
$po_transaction = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first();
if ($po_transaction) {
$this->updatesTransactionStatus->execute($po_transaction, (float) number_format($po_transaction->amount, 2, '.', '') === (float) number_format((float)$booking->fix_amount - $refundAmount, 2, '.', '') ? ApprovalStatus::PENDING_VERIFICATION : ApprovalStatus::PENDING_SUBMISSION);
}
$request['fix_amount'] = $booking->fix_amount - $refundAmount;
$request->route()->setParameter('id', $booking->id);
$this->updateBookingAmountLogic->execute($request);
}
if ($supplierRefundTransaction) {
$this->updatesTransactionStatus->execute($supplierRefundTransaction, $request->route('status'));
}
$paidAmount = $paymentTransaction->original_amount - $this->calculatesBookingRefundAmount->calculateRefundAmount($paymentTransaction, $booking->fix_currency_id);
if (!$paidAmount > 0) {
$this->updatesTransactionStatus->execute($paymentTransaction, ApprovalStatus::REFUNDED);
}
@@ -0,0 +1,25 @@
<?php
namespace App\Classes\Modules\Transactions\Services;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\BillGroup;
class CalculatesBillGroupPaymentAmount
{
public function execute(BillGroup $billGroup)
{
$bill_refund_amount = round(floatval($billGroup->billRefunds->sum('amount')), 7);
$floating_amount = round(floatval($billGroup->transactions()->whereIn('status', [ApprovalStatus::PENDING_SUBMISSION, ApprovalStatus::PENDING_VERIFICATION])->sum('amount')), 7);
$paid_amount = round(floatval($billGroup->transactions()->where('status', ApprovalStatus::APPROVED)->sum('amount')), 7);
$outstanding_amount = $billGroup->amount - $bill_refund_amount - $paid_amount - $floating_amount + $billGroup->service_charge;
$outstanding_amount = round($outstanding_amount, 7);
return [
'bill_refund_amount' => $bill_refund_amount,
'floating_amount' => $floating_amount,
'paid_amount' => $paid_amount,
'outstanding_amount' => $outstanding_amount,
];
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Classes\Modules\Transactions\Services;
use Illuminate\Database\Eloquent\Builder;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use App\Models\BillGroup;
class FetchesBillGroup extends AbstractFetchRecord
{
/** @var BillGroup */
private $repository;
/**
* FetchesBillGroup constructor.
* @param BillGroup $repository
*/
public function __construct(BillGroup $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,31 @@
<?php
namespace App\Classes\Modules\Transactions\Services;
use Illuminate\Database\Eloquent\Builder;
use App\Classes\General\Eloquent\AbstractListRecord;
use App\Models\BillGroup;
class ListsBillGroups extends AbstractListRecord
{
/** @var BillGroup */
private $repository;
/**
* ListsBookings constructor.
* @param Group $repository
*/
public function __construct(BillGroup $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -25,4 +25,6 @@ final class DocumentType {
public const INVOICE = 'INVOICE';
public const SUPPLIER_DELIVER_ORDER = 'SUPPLIER_DELIVER_ORDER';
public const BULK_PURCHASE_ORDER = 'BULK_PURCHASE_ORDER';
public const BILL_GROUP_PAYMENT_PROOF = 'BILL_GROUP_PAYMENT_PROOF';
}
@@ -0,0 +1,13 @@
<?php
namespace App\Classes\ValueObjects\Constants;
final class RemarkTypes
{
public const INTERNAL = 0;
public const EXTERNAL = 1;
}
@@ -31,4 +31,11 @@ final class TransactionType {
public const TRANSFER_FEE = 12;
public const CASH_BACK = 13;
public const SUPPLIER_PAYMENT = 14;
public const SUPPLIER_REFUND = 15;
public const BILL_REFUND = 16;
}
@@ -95,7 +95,7 @@ class ExpiredRefundedBookingCommand extends Command
if (!$bookingPayment) {
$bookingPaymentCount = $booking->transactions()->payments()->count();
if ($bookingPaymentCount > 1) {
Log::info("Credit note transaction id: {$transaction->id}, there are {$bookingPaymentCount} payment for the booking.");
$this->info("Credit note transaction id: {$transaction->id}, there are {$bookingPaymentCount} payment for the booking.");
foreach ($booking->transactions()->payments()->get() as $bp) {
if ($transaction->amount - $bp->amount < 0.01) {
$bookingPayment = $bp;
@@ -107,25 +107,30 @@ class ExpiredRefundedBookingCommand extends Command
if (!$bookingPayment) {
$bookingPayment = $booking->transactions()->payments()->whereIn('status', [ApprovalStatus::SUSPENDED, ApprovalStatus::EXPIRED, ApprovalStatus::REJECTED])->orderBy('id', 'DESC')->first();
}
$status = ApprovalStatus::APPROVAL_STATUS_ID[$bookingPayment->status];
Log::info("Credit note transaction id: {$transaction->id}, the payment for the booking is in status {$status}");
}
if ($bookingPayment) {
$status = ApprovalStatus::APPROVAL_STATUS_ID[$bookingPayment->status];
$this->info("Credit note transaction id: {$transaction->id}, the payment for the booking is in status {$status}");
$bookingPaymentAmount = $bookingPayment->amount;
// check if the booking is fully refund
$amountDifference = bcsub($transaction->amount, $bookingPaymentAmount, 7);
$isFullyRefund = false;
if (abs($amountDifference) < 0.01) {
// rejecting booking payment transaction
// $bookingPayment->status = ApprovalStatus::REJECTED;
// $bookingPayment->save();
$isFullyRefund = true;
// update fully refunded booking payment transaction
$bookingPayment->status = ApprovalStatus::REFUNDED;
$bookingPayment->save();
//expired booking
// $this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED);
Log::info("Credit note transaction id: {$transaction->id} is fully refunded, the refunded amount was {$transaction->amount} the payment reference is: {$transaction->payment_reference}");
// Log::info("Credit note transaction id: {$transaction->id}, Rejected Booking Transaction Payment id: {$bookingPayment->id}, the payment amount was {$bookingPayment->amount}");
// Log::info("Credit note transaction id: {$transaction->id}, Expired Booking id: {$booking->id}");
$this->info("Credit note transaction id: {$transaction->id} is fully refunded, the refunded amount was {$transaction->amount} the payment reference is: {$transaction->payment_reference}");
// $this->info("Credit note transaction id: {$transaction->id}, Rejected Booking Transaction Payment id: {$bookingPayment->id}, the payment amount was {$bookingPayment->amount}");
// $this->info("Credit note transaction id: {$transaction->id}, Expired Booking id: {$booking->id}");
} else {
Log::info("Credit note transaction id: {$transaction->id} is not fully refunded, the refunded amount was {$transaction->amount}, the payment amount was {$bookingPayment->amount}, the payment reference is: {$transaction->payment_reference}");
$this->info("Credit note transaction id: {$transaction->id} is not fully refunded, the refunded amount was {$transaction->amount}, the payment amount was {$bookingPayment->amount}, the payment reference is: {$transaction->payment_reference}");
}
$refund = $bookingPayment->transactions()->refunds()->where('amount', $transaction->amount)->where('status', ApprovalStatus::APPROVED)->first();
@@ -133,29 +138,59 @@ class ExpiredRefundedBookingCommand extends Command
$bookingInWhiteForm = $bookingPayment->transactions()->bills()->first();
if ($refund) {
Log::info("Credit note transaction id: {$transaction->id}, already created same amount of refund transaction for same booking payment transaction");
$this->info("Credit note transaction id: {$transaction->id}, already created same amount of refund transaction for same booking payment transaction");
}
if ($bookingInWhiteForm) {
Log::info("Credit note transaction id: {$transaction->id}, booking is in white form");
}
if (!$refund && !$bookingInWhiteForm) {
if (!$refund) {
$billNumber = $this->generatesTransactionBillNumber->execute('RFD-');
$object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id,
1, PaymentMethodType::CASH,
$transaction->amount, $transaction->amount * $bookingPayment->currency_rate, 1,
$transaction->amount, $isFullyRefund ? $bookingPayment->original_amount : $transaction->amount * $bookingPayment->currency_rate, 1,
$bookingPayment->original_currency_id, $bookingPayment->currency_rate,
0, 0, null, ApprovalStatus::APPROVED, [], $bookingPayment->bill_no);
$transaction = $this->createsTransaction->execute($bookingPayment, $object);
}
} else {
Log::info("Credit note transaction id: {$transaction->id}, booking marking not found, the payment reference is: {$transaction->payment_reference}");
if ($bookingInWhiteForm) {
$original_amount = $isFullyRefund ? $bookingPayment->original_amount : bcmul($transaction->amount, $bookingPayment->currency_rate, 7);
$supplier_refund_amount = bcdiv($original_amount, $bookingInWhiteForm->currency_rate, 7);
$this->info("Credit note transaction id: {$transaction->id}, booking is in white form, white form currency rate is {$bookingInWhiteForm->currency_rate}");
// if ($isFullyRefund && $bookingInWhiteForm->currency_rate == 1) {
// dd ($bookingInWhiteForm->owner_id);
// }
$refund = $bookingPayment->transactions()->supplierRefunds()->where('original_amount', $original_amount)->first();
if (!$refund) {
$billNumber = $this->generatesTransactionBillNumber->execute('SRFD-');
$object = new TransactionObject($billNumber, TransactionType::SUPPLIER_REFUND, 1, $bookingInWhiteForm->issuer,
1, PaymentMethodType::CASH,
$supplier_refund_amount, $original_amount, 1,
$bookingPayment->original_currency_id, $bookingInWhiteForm->currency_rate,
0, 0, null, ApprovalStatus::APPROVED, [], $bookingPayment->bill_no);
$transaction = $this->createsTransaction->execute($bookingPayment, $object);
}
}
} else {
Log::info("Credit note transaction id: {$transaction->id} does not have booking marking, the payment reference is: {$transaction->payment_reference}");
// $bookingPayment = $booking->transactions()->payments()->where('status', ApprovalStatus::REFUNDED)->orderBy('id', 'DESC')->first();
// if ($bookingPayment) {
// $this->info("Credit note transaction id: {$transaction->id}, booking payment refunded");
// } else {
$this->info("Credit note transaction id: {$transaction->id}, booking payment not found, the payment reference is: {$transaction->payment_reference}");
// }
}
} else {
$this->info("Credit note transaction id: {$transaction->id}, booking marking not found, the payment reference is: {$transaction->payment_reference}");
}
} else {
$this->info("Credit note transaction id: {$transaction->id} does not have booking marking, the payment reference is: {$transaction->payment_reference}");
}
}
}
@@ -0,0 +1,68 @@
<?php
namespace App\Console\Commands;
use App\Models\SeasonalSegment;
use Illuminate\Console\Command;
use Carbon\Carbon;
use App\Classes\Modules\Companies\Services\RemovesCompanyFromSegment;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Group;
use App\Models\Transaction;
use App\Models\Wallet;
use Illuminate\Support\Facades\Log;
class UpdateGroupWithTransferFee extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'updateGroupWithTransferFee';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update group with transfer fee';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
ini_set('max_execution_time', 0);
set_time_limit(0);
$groups = Group::whereDate('updated_at', '<', Carbon::now())->get();
foreach ($groups as $group) {
$originalTransferFees = (float)Transaction::where('type', TransactionType::TRANSFER_FEE)->whereIn('owner_id', $group->transactions->pluck('id'))->sum('service_charge');
$correctOriginalAmount = $group->transactions()->sum('original_amount');
$correctOriginalAmount += $originalTransferFees;
$correctAmount = $group->transactions()->sum('amount');
$transferFees = $originalTransferFees / $group->currency_rate;
$correctAmount += $transferFees;
if ($group->original_amount != $correctOriginalAmount) {
$group->original_amount = $correctOriginalAmount;
$group->amount = $correctAmount;
$group->save();
}
}
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Remarks;
use App\Classes\Modules\Remarks\ControllersLogic\CreateRemarkLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CreateRemarkController
{
/**
* @param Request $request
* @param CreateRemarkLogic $logic
* @return JsonResponse
*/
public function create(Request $request, CreateRemarkLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Remarks;
use App\Classes\Modules\Remarks\ControllersLogic\DeleteRemarkLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DeleteRemarkController
{
/**
* @param Request $request
* @param DeleteRemarkLogic $logic
* @return JsonResponse
*/
public function delete(Request $request, DeleteRemarkLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Remarks;
use App\Classes\Modules\Remarks\ControllersLogic\FetchRemarkLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchRemarkController
{
/**
* @param Request $request
* @param FetchRemarkLogic $logic
* @return JsonResponse
*/
public function fetch(Request $request, FetchRemarkLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Remarks;
use App\Classes\Modules\Remarks\ControllersLogic\ListRemarksLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListRemarksController
{
/**
* @param Request $request
* @param ListRemarksLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListRemarksLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Remarks;
use App\Classes\Modules\Remarks\ControllersLogic\UpdateRemarkLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UpdateRemarkController
{
/**
* @param Request $request
* @param UpdateRemarkLogic $logic
* @return JsonResponse
*/
public function update(Request $request, UpdateRemarkLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Transactions;
use App\Classes\Modules\Transactions\ControllersLogic\ApproveBillGroupPaymentVerificationLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ApproveBillGroupPaymentVerificationController
{
/**
* @param Request $request
* @param ApproveBillGroupPaymentVerificationLogic $logic
* @return JsonResponse
*/
public function approve(Request $request, ApproveBillGroupPaymentVerificationLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\CreateBillGroupPaymentProofDocumentLogic;
class CreateBillGroupPaymentProofDocumentController
{
/**
* @param Request $request
* @param CreateBillGroupPaymentProofDocumentLogic $logic
* @return JsonResponse
*/
public function create(Request $request, CreateBillGroupPaymentProofDocumentLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\CreateBillGroupPaymentTransactionLogic;
class CreateBillGroupPaymentTransactionController
{
/**
* @param Request $request
* @param DeleteGroupTransactionLogic $logic
* @return JsonResponse
*/
public function pay(Request $request, CreateBillGroupPaymentTransactionLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\CreateSupplierBillGroupLogic;
class CreateSupplierBillGroupController
{
/**
* @param Request $request
* @param CreateSupplierBillGroupLogic $logic
* @return JsonResponse
*/
public function create(Request $request, CreateSupplierBillGroupLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\DeleteBillGroupLogic;
class DeleteBillGroupController
{
/**
* @param Request $request
* @param DeleteGroupTransactionLogic $logic
* @return JsonResponse
*/
public function delete(Request $request, DeleteBillGroupLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\ListBillGroupsLogic;
class ListBillGroupsController
{
/**
* @param Request $request
* @param ListBillGroupsLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListBillGroupsLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace App\Http\Resources;
use App\Classes\Modules\Transactions\Services\CalculatesBillGroupPaymentAmount;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
class BillGroupResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$billGroupPayment = (App()->make(CalculatesBillGroupPaymentAmount::class))->execute($this->resource);
$bill_refund_amount = $billGroupPayment['bill_refund_amount'];
$floating_amount = $billGroupPayment['floating_amount'];
$paid_amount = $billGroupPayment['paid_amount'];
$outstanding_amount = $billGroupPayment['outstanding_amount'];
return [
'id' => $this->id,
'reference' => $this->reference,
'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->service_charge,
'currency' => new CurrencyResource($this->currency),
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A'),
'updated_at' => Carbon::parse($this->updated_at)->format('d-m-Y h:i:s A'),
'currency_rate' => (float) $this->currency_rate,
'status' => $this->status,
'groups' => GroupResource::collection($this->groups),
'bill_refund_amount' => $bill_refund_amount,
'floating_amount' => $floating_amount,
'paid_amount' => $paid_amount,
'outstanding_amount' => $outstanding_amount,
'payment_history' => $this->transactions->map(function ($transaction) {
return [
'id' => $transaction->id,
'type' => (int) $transaction->type,
'bill_no' => $transaction->bill_no,
'payment_method' => (float) $transaction->payment_method,
'amount' => (double) $transaction->amount,
'original_amount' => (double) $transaction->original_amount,
'currency' => new CurrencyResource($transaction->currency),
'original_currency' => new CurrencyResource($transaction->original_currency),
'service_charge' => (double) $transaction->service_charge,
'tax' => (double) $transaction->tax,
'status' => (int) $transaction->status,
'statusText' => ApprovalStatus::APPROVAL_STATUS_ID[(int) $transaction->status],
'documents' => $transaction->documents()->first() ? new DocumentResource($transaction->documents()->first()) : null,
'updated_at' => Carbon::parse($transaction->updated_at)->format('d-m-Y h:i:s A'),
];
}),
'bill_refunds' => $this->billRefunds->map(function ($transaction) {
return [
'id' => $transaction->id,
'type' => (int) $transaction->type,
'bill_no' => $transaction->bill_no,
'payment_method' => (float) $transaction->payment_method,
'amount' => (double) $transaction->amount,
'original_amount' => (double) $transaction->original_amount,
'currency' => new CurrencyResource($transaction->currency),
'original_currency' => new CurrencyResource($transaction->original_currency),
'service_charge' => (double) $transaction->service_charge,
'tax' => (double) $transaction->tax,
'status' => (int) $transaction->status,
'statusText' => ApprovalStatus::APPROVAL_STATUS_ID[(int) $transaction->status],
'updated_at' => Carbon::parse($transaction->updated_at)->format('d-m-Y h:i:s A'),
];
}),
];
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class RemarkResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'commenter' => new UserResource($this->commenter),
'owner_id' => $this->owner_id,
'content' => $this->content,
'created_at' => $this->created_at->format('d-m-Y H:i'),
'long_ago' => $this->created_at->diffForHumans()
];
}
}
+4 -3
View File
@@ -3,6 +3,7 @@
namespace App\Http\Resources;
use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
use Carbon\Carbon;
@@ -19,7 +20,7 @@ class TransactionResource extends JsonResource
public function toArray($request)
{
$booking = in_array((int)$this->type, [TransactionType::BILL, TransactionType::REFUND])? $this->owner->owner : $this->owner;
$booking = in_array((int)$this->type, [TransactionType::BILL, TransactionType::REFUND, TransactionType::SUPPLIER_REFUND])? $this->owner->owner : $this->owner;
$days = $this->created_at->endOfDay()->addWeekdays($booking->service_id === 3 ? 3 : 1);
return [
@@ -32,8 +33,8 @@ class TransactionResource extends JsonResource
'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,
'amount' => (double) ($this->type === TransactionType::SUPPLIER_REFUND ? $this->amount - $this->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->sum('amount') : $this->amount),
'original_amount' => (double) ($this->type === TransactionType::SUPPLIER_REFUND ? $this->original_amount - $this->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->sum('original_amount') : $this->original_amount),
'currency' => new CurrencyResource($this->currency),
'original_currency' => new CurrencyResource($this->original_currency),
'service_charge' => (double) $this->service_charge,
+76
View File
@@ -0,0 +1,76 @@
<?php
namespace App\Models;
use App\Classes\General\Traits\LogData;
use Illuminate\Database\Eloquent\Model;
use App\Classes\General\Interfaces\Documentable;
use App\Classes\General\Interfaces\Transactionable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Staudenmeir\EloquentHasManyDeep\HasManyDeep;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
class BillGroup extends Model implements Documentable, Transactionable
{
use SoftDeletes;
protected $table = 'bill_groups';
protected $dates = ['deleted_at'];
/**
* @return MorphMany
*/
public function transactions(): MorphMany
{
return $this->MorphMany(Transaction::class, 'owner');
}
use HasRelationships;
use \Staudenmeir\EloquentHasManyDeep\HasTableAlias;
public function billRefunds()
{
return $this->belongsToMany(Transaction::class, BillGroupRefund::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 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');
}
public function groups()
{
return $this->belongsToMany(Group::class, BillGroupPayment::class, 'bill_group_id', 'group_id');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BillGroupPayment extends Model
{
protected $table = 'bill_group_payments';
/**
* @return BelongsTo
*/
public function billGroup(): BelongsTo
{
return $this->BelongsTo(BillGroup::class, 'bill_group_id', 'id');
}
/**
* @return BelongsTo
*/
public function group(): BelongsTo
{
return $this->BelongsTo(Group::class, 'group_id', 'id');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BillGroupRefund extends Model
{
protected $table = 'bill_group_refunds';
/**
* @return BelongsTo
*/
public function billGroup(): BelongsTo
{
return $this->BelongsTo(BillGroup::class, 'bill_group_id', 'id');
}
/**
* @return BelongsTo
*/
public function transaction(): BelongsTo
{
return $this->BelongsTo(Transaction::class, 'transaction_id', 'id');
}
}
+9
View File
@@ -6,6 +6,7 @@ use App\Classes\General\Traits\LogData;
use Illuminate\Database\Eloquent\Model;
use App\Classes\General\Interfaces\Documentable;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Staudenmeir\EloquentHasManyDeep\HasManyDeep;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
@@ -60,4 +61,12 @@ class Group extends Model implements Documentable
{
return $this->BelongsTo(Currency::class, 'original_currency_id', 'id');
}
/**
* @return BelongsToMany
*/
public function billGroup(): BelongsToMany
{
return $this->belongsToMany(BillGroup::class, BillGroupPayment::class, 'group_id', 'bill_group_id');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Remark extends AbstractModel
{
use SoftDeletes;
protected $table = 'remarks';
public function owner(): morphTo
{
return $this->morphTo();
}
/**
* @return BelongsTo
*/
public function commenter(): BelongsTo
{
return $this->BelongsTo(User::class, 'commenter_id', 'id');
}
}
+22
View File
@@ -186,6 +186,20 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
return $query->where('type', TransactionType::REFUND);
}
/**
* @param Builder $query
* @param string $payment_reference
* @return Builder
*/
public function scopeSupplierRefunds(Builder $query, ?string $payment_reference = NULL)
{
if($payment_reference){
$query->where('payment_reference', $payment_reference);
}
return $query->where('type', TransactionType::SUPPLIER_REFUND);
}
/**
* @param Builder $query
@@ -245,4 +259,12 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
return $this->morphMany(VoucherEntityMapping::class, 'owner');
}
/**
* @return MorphMany
*/
public function remarks(): morphMany
{
return $this->morphMany(Remark::class, 'owner');
}
}
@@ -0,0 +1,44 @@
<?php
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBillGroupsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('bill_groups', function (Blueprint $table) {
$table->id();
$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();
$table->foreignId('original_currency_id')->unsigned();
$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->integer('status')->default(ApprovalStatus::PENDING_VERIFICATION);
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('bill_groups');
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBillGroupPaymentsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('bill_group_payments', function (Blueprint $table) {
$table->id();
$table->foreignId('bill_group_id')->constrained('bill_groups');
$table->foreignId('group_id')->constrained('groups');
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('bill_group_payments');
}
}
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBillGroupRefundsTable extends Migration
{
public function up()
{
Schema::create('bill_group_refunds', function (Blueprint $table) {
$table->id();
$table->foreignId('bill_group_id')->unsigned()->on('bill_groups');
$table->foreignId('transaction_id')->unsigned()->on('transactions');
});
}
public function down()
{
Schema::dropIfExists('bill_group_refunds');
}
}
@@ -0,0 +1,41 @@
<?php
use App\Classes\ValueObjects\Constants\RemarkTypes;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateRemarksTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('remarks', function (Blueprint $table) {
$table->id();
$table->morphs('owner');
$table->bigInteger('commenter_id')->unsigned()->index();
$table->string('content',200);
$table->integer('type')->default(RemarkTypes::INTERNAL);
$table->softDeletes();
$table->timestamps();
});
Schema::table('remarks', function (Blueprint $table) {
$table->string('owner_type', 191)->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('remarks');
}
}
+1 -1
View File
@@ -48,6 +48,6 @@ class DatabaseSeeder extends Seeder
$this->call(DummyDataSeeder::class);
}
DB::commit();
// DB::commit();
}
}
@@ -0,0 +1,290 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div class="row parentContainer m-b-10">
<div class="col p-l-0">
<div class="row m-l-15 p-t-10 b-a align-items-center pointer shadow bg-white rounded" :class="{'b-white': !(this.selectedBillGroup.id === item.id), 'b-primary': this.selectedBillGroup.id === item.id}" @click="select()">
<div class="col-12">
<div class="row m-b-10">
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Date</div>
<div class="font-heading fs-12">
{{item.updated_at}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Reference</div>
<div class="font-heading fs-12">
{{item.reference}}
</div>
</div>
<div class="col-auto" v-if="$store.getters.isAdmin">
<div class="font-heading fs-12 muted all-caps">Supplier</div>
<div class="font-heading fs-12">
{{item.issuer_name}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Amount</div>
<div class="font-heading fs-12">
{{ item.currency.short_code }} {{formatAmount(item.amount)}}
</div>
</div>
<div class="col parentContainer position-static">
<div class="row align-items-center justify-content-end">
<div class="col-auto position-static no-padding requestModal" data-type="deleteBillGroup" v-if="section !== 'paidBillGroupList'">
<div class="btn bg-grey no-border">
<i class="fa fa-times text-danger"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="deleteBillGroup">
<general-confirmation-form-component
contentText="Are you sure you want to delete this Bill Group?"
modalType="delete"
class="text-center"
:apiRoute="route('api.transaction.group.bill.delete', item.id)"
apiMethod="delete"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div>
<div class="col-auto no-padding">
<div class="btn btn-sm btn-default b-rad-none no-border" @click="expanded = !expanded">
<i class="fa fa-fw" :class="[{'fa-angle-up': expanded}, {'fa-angle-down': !expanded}]"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded">
<div class="row m-b-10">
<div class="font-heading fs-12 all-caps text-underline">Bill Refunds</div>
</div>
<div class="row m-b-10" v-for="refund in item.bill_refunds">
<div class="col-3">
<div class="font-heading fs-12 muted all-caps">Date</div>
<div class="font-heading fs-12">
{{refund.updated_at}}
</div>
</div>
<div class="col-3">
<div class="font-heading fs-12 muted all-caps">Reference</div>
<div class="font-heading fs-12">
{{refund.bill_no}}
</div>
</div>
<div class="col-3" v-if="$store.getters.isAdmin">
<div class="font-heading fs-12 muted all-caps">Amount</div>
<div class="font-heading fs-12">
{{ refund.currency.short_code }} {{ formatAmount(refund.amount) }}
</div>
</div>
<div class="col-3" v-if="$store.getters.isAdmin">
<div class="font-heading fs-12 muted all-caps">Status</div>
<div class="font-heading fs-12" :class="[{'text-danger': refund.status === 0 || refund.status === 4}, {'text-success': refund.status === 2 || refund.status === 3}, {'text-warning': refund.status === 1}]">
{{ refund.statusText }}
</div>
</div>
</div>
<div class="row m-t-20 m-b-20" v-if="item.bill_refunds.length === 0">
<div class="col">
<div class="font-heading fs-12 text-center all-caps">No Selected Refunds</div>
</div>
</div>
</div>
<div class="col b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded">
<div class="row m-b-10">
<div class="font-heading fs-12 all-caps text-underline">Payment History</div>
</div>
<div class="row m-b-10" v-for="payment in item.payment_history">
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Date</div>
<div class="font-heading fs-12">
{{payment.updated_at}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Reference</div>
<div class="font-heading fs-12">
{{payment.bill_no}}
</div>
</div>
<div class="col-auto" v-if="$store.getters.isAdmin">
<div class="font-heading fs-12 muted all-caps">Amount</div>
<div class="font-heading fs-12">
{{ payment.currency.short_code }} {{ formatAmount(payment.amount) }}
</div>
</div>
<div class="col-auto" v-if="$store.getters.isAdmin">
<div class="font-heading fs-12 muted all-caps">Status</div>
<div class="font-heading fs-12" :class="[{'text-danger': payment.status === 0 || payment.status === 4}, {'text-success': payment.status === 2 || payment.status === 3}, {'text-warning': payment.status === 1}]">
{{ payment.statusText }}
</div>
</div>
<div class="col parentContainer position-static align-items-center">
<div class="row align-items-center justify-content-end">
<div v-if="!payment.documents && section === 'paymentInProgressBillGroupList'">
<div class="btn no-border muted btn-success requestModal" data-type="paymentProofModal" @click="selectedTransactionID(payment.id)">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="30" height="30" viewBox="0 0 172 172" style=" fill:#000000;"><defs><linearGradient x1="86" y1="70.76994" x2="86" y2="116.46013" gradientUnits="userSpaceOnUse" id="color-1_52139_gr1"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient><linearGradient x1="61.8125" y1="34.48869" x2="61.8125" y2="144.97181" gradientUnits="userSpaceOnUse" id="color-2_52139_gr2"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient><linearGradient x1="130.34375" y1="34.48869" x2="130.34375" y2="144.97181" gradientUnits="userSpaceOnUse" id="color-3_52139_gr3"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient><linearGradient x1="86" y1="32.25" x2="86" y2="148.71013" gradientUnits="userSpaceOnUse" id="color-4_52139_gr4"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient></defs><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z" fill="none"></path><g><path d="M102.45825,99.43213h-5.70825c-1.4835,0 -2.6875,1.16637 -2.6875,2.65256v8.10013c0,1.48081 -1.19862,2.68481 -2.67944,2.68481h-10.76612c-1.48081,0 -2.67944,-1.204 -2.67944,-2.68481v-8.10013c0,-1.48619 -1.204,-2.65256 -2.6875,-2.65256h-5.70825c-1.93769,0 -3.04225,-2.39188 -1.88125,-4.05275l14.577,-20.855c1.8275,-2.61494 5.69481,-2.61763 7.52231,-0.00538l14.577,20.86306c1.16369,1.66088 0.05644,4.05006 -1.87856,4.05006z" fill="url(#color-1_52139_gr1)"></path><path d="M51.0625,67.1875h5.375c0,-8.0625 7.23206,-16.12231 16.125,-16.12231v-5.375c-11.85456,0 -21.5,10.74731 -21.5,21.49731z" fill="url(#color-2_52139_gr2)"></path><path d="M139.75,80.625c0,-10.75 -8.44144,-18.80981 -18.8125,-18.80981v5.375c7.40944,0 13.4375,5.37231 13.4375,13.43481z" fill="url(#color-3_52139_gr3)"></path><path d="M148.09738,92.27263c1.59369,-3.68188 2.40263,-7.59219 2.40263,-11.64494c0,-16.29969 -13.26281,-29.5625 -29.5625,-29.5625c-6.5145,0 -12.68769,2.08819 -17.78588,5.96088c-4.30269,-13.03438 -16.52006,-22.08587 -30.58912,-22.08587c-17.78319,0 -32.25,14.46681 -32.25,32.25c0,4.14681 0.16662,8.05981 1.42437,10.74731h-1.42437c-13.33806,0 -24.1875,10.84944 -24.1875,24.1875c0,11.56431 8.16194,21.24737 19.0275,23.62044c1.02394,6.39894 6.53869,11.31706 13.2225,11.31706h56.4375h16.125h5.375c6.54944,0 12.00238,-4.71388 13.18488,-10.92469c9.2235,-1.20131 16.37762,-9.08913 16.37762,-18.63513c0,-6.09256 -2.924,-11.72019 -7.77762,-15.23006zM126.3125,131.6875h-5.375h-16.125h-56.4375c-3.49912,0 -6.45538,-2.6875 -7.568,-5.375h93.0735c-1.11263,2.6875 -4.06888,5.375 -7.568,5.375zM137.0625,120.9375h-96.75c-10.37106,0 -18.8125,-8.44144 -18.8125,-18.8125c0,-10.37106 8.44144,-18.8125 18.8125,-18.8125h9.51375l-1.68506,-3.78131c-2.23063,-5.01488 -2.45369,-7.48737 -2.45369,-12.341c0,-14.81888 12.05613,-26.875 26.875,-26.875c13.01825,0 24.13106,9.29875 26.42619,22.11275l0.92719,5.17075l3.64962,-3.77325c4.60638,-4.76225 10.77687,-7.38525 17.372,-7.38525c13.33806,0 24.1875,10.84944 24.1875,24.1875c0,3.6765 -0.81431,7.21056 -2.37575,10.41944l-1.763,3.32713l2.37037,1.26044c4.40481,2.34081 7.14338,6.88806 7.14338,11.868c0,7.40944 -6.02806,13.43481 -13.4375,13.43481z" fill="url(#color-4_52139_gr4)"></path></g></g></svg>
</div>
<modal-component type="paymentProofModal">
<bill-group-payment-proof-form-component v-if="selected_transaction_id === payment.id" :section="section" :data="{item}" :id="payment.id"></bill-group-payment-proof-form-component>
</modal-component>
</div>
<div v-if="payment.documents">
<document-file-viewer-component :file="payment.documents.files[0]">
<template slot="button">
<div class="btn no-border muted btn-success">
<i class="fa fa-file-pdf-o"></i>
</div>
</template>
</document-file-viewer-component>
</div>
<div v-if="section === 'paymentVerificationBillGroupList' && payment.status === 1">
<div class="col-auto">
<div class="row">
<div class="col-6 col-md-auto text-right">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="rejectPayment">
<i class="fa fa-times fa-fw"></i>
</button>
<button class="btn btn-xs btn-success b-rad-none requestModal" data-type="approvePayment">
<i class="fa fa-check fa-fw"></i>
</button>
<modal-component small type="rejectPayment">
<general-confirmation-form-component
contentText="Are you sure you want to reject this payment?"
modalType="delete"
class="text-center"
:apiRoute="route('api.transaction.group.bill.payment_proof.approval', payment.id, 'reject')"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
<modal-component small type="approvePayment">
<general-confirmation-form-component
contentText="Are you sure you want to approve this payment?"
modalType="confirm"
class="text-center"
:apiRoute="route('api.transaction.group.bill.payment_proof.approval', payment.id, 'approve')"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row m-t-20 m-b-20" v-if="item.payment_history.length === 0">
<div class="col">
<div class="font-heading fs-12 text-center all-caps">No Payments</div>
</div>
</div>
</div>
<div class="col b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded">
<div class="row m-b-10">
<div class="font-heading fs-12 all-caps text-underline">Currency Orders</div>
</div>
<div class="row m-t-10 m-b-20" v-for="group in item.groups">
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Date</div>
<div class="font-heading fs-12">
{{group.created_at}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Currency Rate</div>
<div class="font-heading fs-12">
{{group.currency_rate}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">Original Amount</div>
<div class="font-heading fs-12">
{{ group.original_currency.short_code }} {{formatAmount(group.original_amount)}}
</div>
</div>
<div class="col-2">
<div class="font-heading fs-12 muted all-caps">Amount</div>
<div class="font-heading fs-12">
{{ group.currency.short_code }} {{formatAmount(group.amount)}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-12 muted all-caps">PO Completion</div>
<div class="col">
<p class="font-heading fs-12 bold text-success pointer"><span :class="[{'text-danger': group.complete_transactions.length !== group.transactions.length}]">{{group.complete_transactions.length}}</span>/{{group.transactions.length}}</p>
</div>
</div>
<div class="col parentContainer position-static align-items-center">
<div class="row align-items-center justify-content-end">
<div v-if="group.documents.currency_order">
<document-file-viewer-component :file="group.documents.currency_order.files[0]">
<template slot="button">
<div class="btn no-border muted btn-success">
<i class="fa fa-file-pdf-o"></i>
</div>
</template>
</document-file-viewer-component>
</div>
<div v-else>
<div class="btn bg-grey no-border muted invisible">
<i class="fa fa-file-pdf-o"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
selectedBillGroup: {
type: Object,
required: true
},
section: {
type: String,
required: true
}
},
data(){
return {
selected_id: '',
selected: false,
expanded: false,
selected_transaction_id: '',
}
},
methods: {
selectedTransactionID(id){
this.selected_transaction_id = id;
},
formatAmount(amount) {
return (Math.round((amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
},
select(){
this.$emit('input', this.item)
this.selected = this.selectedBillGroup.id === this.item.id;
},
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,170 @@
<template>
<div class="row mt-3 mt-md-0">
<div class="col">
<div class="row m-l-10 p-t-20 p-b-20 shadow bg-white rounded">
<div class="col">
<div class="row align-items-end m-b-10 text-complete">
<div class="col">
<div class="font-heading all-caps fs-12">Transfer Total:</div>
</div>
<div class="col-auto text-right" v-if="selectedBillGroup.id">
<div class="font-heading fs-12">{{selectedBillGroup.original_currency.short_code}} {{formatAmount(selectedBillGroup.original_amount)}}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading fs-12">CNY 0.00</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-complete">
<div class="col">
<div class="font-heading all-caps fs-12"></div>
</div>
<div class="col-auto text-right" v-if="selectedBillGroup.id">
<div class="font-heading fs-12">{{selectedBillGroup.currency.short_code}} {{formatAmount(selectedBillGroup.amount)}}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading fs-12">MYR 0.00</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-complete">
<div class="col">
<div class="font-heading all-caps fs-12">Service Charge:</div>
</div>
<div class="col-auto text-right" v-if="selectedBillGroup.id">
<div class="font-heading fs-12">{{selectedBillGroup.currency.short_code}} {{formatAmount(selectedBillGroup.service_charge)}}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading fs-12">MYR 0.00</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-danger">
<div class="col">
<div class="font-heading all-caps fs-12">Bill Refund Total:</div>
</div>
<div class="col-auto text-right" v-if="selectedBillGroup.id">
<div class="font-heading fs-12">- {{selectedBillGroup.currency.short_code}} {{formatAmount(selectedBillGroup.bill_refund_amount)}}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading fs-12">MYR 0.00</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-success">
<div class="col">
<div class="font-heading all-caps fs-12">Paid Total:</div>
</div>
<div class="col-auto text-right" v-if="selectedBillGroup.id">
<div class="font-heading fs-12">{{selectedBillGroup.currency.short_code}} {{ formatAmount(selectedBillGroup.paid_amount) }}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading fs-12">MYR 0.00</div>
</div>
</div>
<div class="row align-items-end m-b-10">
<div class="col">
<div class="font-heading all-caps fs-12">Floating Amount:</div>
</div>
<div class="col-auto text-right" v-if="selectedBillGroup.id">
<div class="font-heading fs-12">{{selectedBillGroup.currency.short_code}} {{ formatAmount(selectedBillGroup.floating_amount) }}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading fs-12">MYR 0.00</div>
</div>
</div>
<div class="row align-items-end bold text-danger">
<div class="col">
<div class="font-heading all-caps fs-12">OutStanding Total:</div>
</div>
<div class="col-auto text-right" v-if="selectedBillGroup.id">
<div class="font-heading fs-12">{{selectedBillGroup.currency.short_code}} {{ formatAmount(selectedBillGroup.outstanding_amount) }}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading fs-12">MYR 0.00</div>
</div>
</div>
<div v-if="selectedBillGroup.status === 0 && (selectedBillGroup.outstanding_amount > 0 || (selectedBillGroup.bill_refund_amount === (selectedBillGroup.amount + selectedBillGroup.service_charge) && !selectedBillGroup.payment_history.some((item)=> [0, 1, 2, 3].includes(item.status))))">
<div class="row p-r-15 m-t-20 m-b-10">
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.payAmount">
<label>Amount</label>
<input class="form-control" v-model.lazy="parameters.payAmount" v-money="{decimal: '.',thousands: ',', precision: 2}">
</validation-wrapper-component>
</div>
<div class="col-auto b-r b-t b-b b-grey">
<div class="row h-100 align-items-center">
<div class="col">
<div class="font-heading fs-10 muted">{{ selectedBillGroup.id ? selectedBillGroup.currency.short_code : "MYR" }}</div>
</div>
</div>
</div>
</div>
<div class="row m-t-20" v-if="selectedBillGroup.outstanding_amount > 0">
<div class="col">
<button id="payment-btn" class="btn btn-sm all-caps b-rad-none btn-success btn-block" @click="submitForm">Make Payment</button>
</div>
</div>
<div class="row m-t-20" v-if="(selectedBillGroup.bill_refund_amount === selectedBillGroup.amount + selectedBillGroup.service_charge)">
<div class="col">
<button id="payment-btn" class="btn btn-sm all-caps b-rad-none btn-success btn-block" @click="submitForm">Complete Order</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ModalComponent from "../../general/elements/ModalComponent";
import componentHandler from '../../../general/mixins/componentHandler';
import { required } from "vuelidate/lib/validators";
import {VMoney} from 'v-money'
export default {
props: {
selectedBillGroup: {
type: Object,
required: true
},
refreshList: {
type: Function,
required: true
},
},
watch: {
selectedBillGroup(){
this.parameters.payAmount = (Math.round((this.selectedBillGroup.outstanding_amount + Number.EPSILON) * 100) / 100).toFixed(2)
}
},
data(){
return {
parameters: {
payAmount: 0
}
}
},
validations: {
parameters: {
payAmount: {
required
}
}
},
methods: {
formatAmount(amount) {
return (Math.round((amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
},
submitForm(){
this.submit(route('api.transaction.group.bill.pay', this.selectedBillGroup.id), 'post', this.section, true, true)
},
successHandler(){
this.refreshList();
// this.$store.dispatch('toggleSection', {name: 'paymentInProgressBillGroupList', status: !this.$store.getters.isShowing('paymentInProgressBillGroupList')});
this.parameters = {
payments: [],
};
},
},
mixins: [componentHandler],
directives: {money: VMoney}
}
</script>
@@ -18,13 +18,13 @@
<div class="col-auto p-l-0">
<div class="font-heading fs-8 muted all-caps">Payment Amount</div>
<div class="font-heading fs-10 bold">
{{item.original_currency.short_code}} {{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
{{item.original_currency.short_code}} {{(Math.round((item.original_amount - item.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
<div class="col-auto p-l-0" v-if="totalRefunds !== 0">
<div class="font-heading fs-8 muted all-caps">Refunded Amount</div>
<div class="font-heading fs-10 bold text-danger">
{{item.currency.short_code}} {{(Math.round((totalConvertRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
{{item.original_currency.short_code}} {{(Math.round((item.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
</div>
@@ -68,15 +68,13 @@
<div class="col-auto p-l-0">
<div class="font-heading fs-8 muted all-caps">Payment Amount</div>
<div class="font-heading fs-10 bold">
{{item.original_currency.short_code}} {{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
{{item.original_currency.short_code}} {{(Math.round((item.original_amount - item.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
<div class="row align-items-end m-b-10 bold text-danger" v-if="totalRefunds !== 0">
<div class="col">
<div class="font-heading all-caps fs-10">Refunded Amount</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">{{item.original_currency.short_code}} {{(Math.round((totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
<div class="col-auto p-l-0 text-danger" v-if="totalRefunds !== 0">
<div class="font-heading fs-8 muted all-caps">Refunded Amount</div>
<div class="font-heading fs-10 bold">
{{item.original_currency.short_code}} {{(Math.round((totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
<div class="col-auto" v-if="$store.getters.isAdmin">
@@ -291,8 +289,8 @@
</div>
</div>
</div>
<div class="row m-t-10" v-show="[2, 3].includes(item.status) && (totalRequestedRefund + totalRefunds) < data.original_amount">
<div class="col" v-if="!item.transaction_bill && $store.getters.isAdmin">
<div class="row m-t-10" v-show="!hasRefundInProgress && [2, 3].includes(item.status) && (totalRequestedRefund + totalRefunds) < data.original_amount">
<div class="col" v-if="$store.getters.isAdmin">
<button class="btn btn-xs all-caps b-rad-none bg-master-lighter btn-block no-border requestModal" data-type="transferSummary">Request Refund</button>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="transferSummary" size="large">
<refund-confirmation-component :data="data" :section="section" :totalRefunds="totalRequestedRefund + totalRefunds"></refund-confirmation-component>
@@ -347,7 +345,7 @@
</div>
</div>
</div>
<div class="row m-b-15 text-right parentContainer" v-if="$store.getters.isAdmin && refund.status === 1">
<div class="row m-b-15 text-right parentContainer" v-if="$store.getters.isSuperAdmin && refund.status === 1">
<div class="col">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="rejectRefund">
<i class="fa fa-times fa-fw"></i>
@@ -436,6 +434,10 @@
TotalRequestedRefund += refunds.status === 2 ? refunds.amount : 0;
});
return TotalRequestedRefund;
},
hasRefundInProgress() {
var refundTransactionsStatus = this.data.transaction_refunds.length > 0 ? this.data.transaction_refunds.map(refund => refund.status) : [];
return refundTransactionsStatus.includes(0) || refundTransactionsStatus.includes(1)
}
},
methods: {
@@ -38,6 +38,7 @@
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="font-heading all-caps fs-10 m-b-5">Paid Amount: {{ paidAmount }}</div>
<div class="font-heading all-caps fs-10 m-b-5" v-if="this.data.refunded_amount > 0">Paid Amount: {{ (Math.round((this.data.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2) }}</div>
<div class="font-heading all-caps fs-10 m-b-5">Refund Amount: {{ refundAmount }}</div>
</div>
</div>
@@ -69,10 +70,11 @@ export default {
},
data() {
return {
refundAmount: (Math.round((this.data.original_amount - this.data.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2),
refundMethod: { name: 'Fully Refund', status: false },
refundMethods: [
{ name: 'Fully Refund', label: 'Full Refund' },
// { name: 'Partially Refund', label: 'Partial Refund' }
{ name: 'Partially Refund', label: 'Partial Refund' }
]
}
},
@@ -84,12 +86,8 @@ export default {
}
},
computed: {
refundAmount() {
// return (Math.round((this.data.booking.amount - this.totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2);
return (Math.round((this.data.original_amount - this.data.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2);
},
refundMaxValue() {
return this.refundAmount;
return (Math.round((this.data.original_amount - this.data.refunded_amount + Number.EPSILON) * 100) / 100).toFixed(2);
},
paidAmount() {
return this.data.original_amount;
@@ -34,7 +34,7 @@
</div>
<div class="row m-b-10">
<div class="col">
<div class="text-right">
<div class="text-right" v-if="$store.getters.isSuperAdmin">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="rejectRefund">
<i class="fa fa-times fa-fw"></i>
</button>
@@ -56,7 +56,7 @@
<span class="flag-icon" :class="'flag-icon-'+item.original_currency.country.short_code.toLowerCase()"></span> {{item.original_currency.short_code}}
</div>
</div>
<div class="col text-right" v-if="totalRefunds !== 0">
<!-- <div class="col text-right" v-if="totalRefunds !== 0">
<div class="row">
<div class="col">
<div class="font-heading fs-10 muted all-caps">Refunded Amount</div>
@@ -65,7 +65,7 @@
</div>
</div>
</div>
</div>
</div> -->
</div>
<div class="row">
<div class="col-auto">
@@ -0,0 +1,98 @@
<template>
<div class="row m-b-10 parentContainer">
<div class="col">
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div class="row align-items-center p-b-5 b-b b-grey" :class="[{'pointer': clickable}]" v-show="!isLoading" @click="clickable ? activate() : undefined">
<div class="col-auto p-r-0">
<div class="b-grey b-a fs-10 btn-rounded icon-thumbnail icon-25 m-r-0" :class="[{'bg-primary': active}, {'bg-transparent': !active}]" v-if="clickable">
<i class="fa fa-check text-white fs-12 fa-fw"></i>
</div>
<div style="width: 25px;" v-else></div>
</div>
<div class="col">
<div class="row m-b-10">
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Date</div>
<div class="font-heading fs-10">
{{item.created_at}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Reference</div>
<div class="font-heading fs-10">
<a :href="route('booking.details', item.booking.marking)">{{item.booking.marking}}</a>
</div>
</div>
<div class="col text-right">
<div class="font-heading fs-10 muted all-caps">Amount</div>
<div class="font-heading fs-14 text-success bold">
{{item.currency.short_code}} {{((Math.round(( item.amount + Number.EPSILON) * 100) / 100)).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
import staticFormHandler from '../../../general/mixins/staticFormHandler'
export default {
props: {
section:{
type: String,
required: true
},
is1688Supplier:{
type: Boolean,
required: true
},
payments:{
type: Array,
required: true
},
paymentTotal: {
type: Number,
required: true
},
inputPaymentTotal: {
type: Number,
required: true
},
refundTotal: {
type: Number,
required: true
},
supplierRefunds:{
type: Array,
required: true
}
},
computed: {
clickable(){
if (this.is1688Supplier) {
return this.refundTotal < this.inputPaymentTotal || this.supplierRefunds.some((i) => this.item.id === i.id );
} else {
return this.refundTotal < this.paymentTotal || this.supplierRefunds.some((i) => this.item.id === i.id );
}
}
},
data(){
return {
active: this.item ? this.supplierRefunds.some(supplierRefund => supplierRefund.id === this.item.id) : false,
}
},
created(){
this.active = this.supplierRefunds.some(supplierRefund => supplierRefund.id === this.item.id);
},
methods: {
activate(){
this.active = !this.active;
this.$emit('input', this.item)
},
},
mixins: [componentHandler, staticFormHandler]
}
</script>
@@ -0,0 +1,132 @@
<template>
<div class="row m-b-10 parentContainer">
<div class="col">
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div class="row align-items-center pointer p-b-5 b-b b-grey" v-show="!isLoading" @click="activate()">
<div class="col-auto p-r-0">
<div class="b-grey b-a fs-10 btn-rounded icon-thumbnail icon-25 m-r-0" :class="[{'bg-primary': active}, {'bg-transparent': !active}]">
<i class="fa fa-check text-white fs-12 fa-fw"></i>
</div>
</div>
<div class="col">
<div class="row m-b-10">
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Date</div>
<div class="font-heading fs-10">
{{item.created_at}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Supplier</div>
<div class="font-heading fs-10">
{{item.issuer_name}}
</div>
</div>
</div>
<div class="row">
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Currency Rate</div>
<div class="font-heading fs-10">
{{item.currency_rate}}
</div>
</div>
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">Currency Amount</div>
<div class="font-heading fs-10">
{{item.original_currency.short_code}} {{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
<div class="col text-right">
<div class="font-heading fs-10 muted all-caps">Amount</div>
<div class="font-heading fs-14 text-success bold">
{{item.currency.short_code}} {{((Math.round(( item.amount + Number.EPSILON) * 100) / 100)).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
</div>
<div class="row">
<div class="col-auto">
<div class="font-heading fs-10 muted all-caps">reference</div>
<document-file-viewer-component v-if="item.documents.currency_order" :file="item.documents.currency_order.files[0]">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-eye"></i>
</button>
</template>
</document-file-viewer-component>
</div>
<div class="col">
<div class="row">
<div class="col">
<div class="font-heading fs-10 muted all-caps">PO Completion</div>
</div>
</div>
<div class="row">
<div class="col-auto p-r-0" v-if="item.documents.purchase_order" >
<document-file-viewer-component :file="item.documents.purchase_order.files[0]">
<template slot="button">
<button class="btn btn-xs btn-default bg-master-lightest b-rad-none no-border">
<i class="fa fa-file-pdf-o"></i>
</button>
</template>
</document-file-viewer-component>
</div>
<div class="col">
<p class="font-heading fs-12 bold text-success pointer" @click="expand($event)"><span :class="[{'text-danger': item.complete_transactions.length !== item.transactions.length}]">{{item.complete_transactions.length}}</span>/{{item.transactions.length}}</p>
</div>
</div>
</div>
</div>
<div class="row" v-if="expanded">
<div class="col">
<div class="row" v-for="transaction in item.transactions">
<div class="col">
<a :href="route('booking.details', transaction)" target="_blank">
<span :class="[{'text-success': item.complete_transactions.includes(transaction)}, {'text-danger': !item.complete_transactions.includes(transaction)}]">{{ transaction }}</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
import staticFormHandler from '../../../general/mixins/staticFormHandler'
export default {
props: {
section:{
type: String,
required: true
},
payments:{
type: Array,
required: true
}
},
data(){
return {
expanded: false,
active: this.item ? this.payments.some(payment => payment.id === this.item.id) : false,
}
},
created(){
this.active = this.payments.some(payment => payment.id === this.item.id);
},
methods: {
activate(){
this.active = !this.active;
this.$emit('input', this.item)
},
expand(event){
this.expanded = !this.expanded;
event.stopPropagation();
}
},
mixins: [componentHandler, staticFormHandler]
}
</script>
@@ -0,0 +1,80 @@
<template>
<div class="row" @keyup.enter="submitForm">
<div class="col">
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
<div class="row" v-show="!$store.getters.isLoading(section)">
<div class="col">
<div class="row m-b-10">
<div class="col">
<div class="font-heading fs-16 all-caps bold m-b-15">Payment Proof</div>
</div>
</div>
<error-message-component class="m-b-20" :error="error"></error-message-component>
<div class="row">
<div class="col">
<file-input-component :validator="$v.files" v-model="files">
<template slot="label">
<div class="font-heading fs-11 text-primary all-caps">Payment Proof</div>
</template>
</file-input-component>
</div>
</div>
<div class="row m-t-20">
<div class="col">
<div class="row">
<div class="col-auto">
<button type="button" class="btn btn-sm bg-master-lighter p-t-10 p-b-10 p-r-35 p-l-35 btn-default b-rad-none" data-dismiss="modal">Cancel</button>
</div>
<div class="col text-right">
<button type="button" class="btn btn-sm p-t-10 p-b-10 p-r-35 p-l-35 btn-success b-rad-none" @click="submitForm">Save and continue</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import ModalFromHandler from '../../../general/mixins/modalFormHandler'
import { required } from "vuelidate/lib/validators";
export default {
props: {
id: {
required: true,
type: Number
}
},
data(){
return {
files: [],
parameters: {}
}
},
validations: {
files: {
required
}
},
methods: {
submitForm(){
this.parameters = {
files: this.files
};
this.submit(this.route('api.transaction.group.bill.payment_proof.create', this.id), 'post', this.section, true, true)
},
successHandler(){
this.closeModal();
this.$store.dispatch('toggleSection', {name: 'paymentInProgressBillGroupList', status: !this.$store.getters.isShowing('paymentInProgressBillGroupList')});
this.$store.dispatch('toggleSection', {name: 'paymentVerificationBillGroupList', status: !this.$store.getters.isShowing('paymentVerificationBillGroupList')});
this.files = []
},
},
mixins: [ModalFromHandler]
}
</script>
@@ -242,6 +242,7 @@
'data': function () {
if (this.data && this.data.purchase_order && this.data.purchase_order.details) {
this.products = this.data.purchase_order.details;
this.submitted = this.data.purchase_order ? this.data.purchase_order.status === 1 || this.data.purchase_order.status === 2: false;
} else {
this.products = [];
}
@@ -0,0 +1,246 @@
<template>
<div class="row mt-3 mt-md-0">
<div class="col">
<div class="row">
<div class="col">
<div class="row align-items-end m-b-10">
<div class="col">
<div class="font-heading all-caps fs-10">Supplier:</div>
</div>
<div class="col-auto">
<div class="font-heading fs-10">{{this.supplier.name}}</div>
</div>
</div>
<div class="row align-items-end m-b-5 text-info">
<div class="col">
<div class="font-heading all-caps fs-10">Order Total:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-11">{{ this.payments.length > 0 ? this.payments[0].original_currency.short_code : "CNY"}} {{ formatNumber(this.originalTotal) }}</div>
</div>
</div>
<div class="row align-items-end m-b-5 text-info" v-if="!is1688Supplier">
<div class="col">
<div class="font-heading all-caps fs-10"></div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-11">MYR {{ formatNumber(this.total) }}</div>
</div>
</div>
<div class="row align-items-end m-b-5 text-info" v-if="!is1688Supplier">
<div class="col">
<div class="font-heading all-caps fs-10">Service Charges:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-11">MYR {{parameters.service_charges}}</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-primary" v-if="is1688Supplier">
<div class="col">
<div class="font-heading all-caps fs-10">Currency Rate:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading bold">{{ billGroupCurrencyRate }}</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-danger" v-if="refundTotal > 0">
<div class="col">
<div class="font-heading all-caps fs-10">Bill Refund Total:</div>
</div>
<div class="col-auto text-right" v-if="is1688Supplier">
<div class="font-heading bold">- {{ this.supplierRefunds.length > 0 ? this.supplierRefunds[0].currency.short_code : 'MYR' }} {{ this.inputPaymentTotal > 0 ? refundTotal > this.inputPaymentTotal ? this.inputPaymentTotal : formatNumber(refundTotal) : '0.00' }}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading bold">- {{ this.supplierRefunds[0].currency.short_code }} {{ formatNumber(refundTotal > paymentTotal ? paymentTotal : refundTotal) }}</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-success">
<div class="col">
<div class="font-heading all-caps fs-10">Payment Total:</div>
</div>
<div class="col-auto text-right" v-if="is1688Supplier">
<div class="font-heading bold">MYR {{ formatNumber(this.inputPaymentTotal) }}</div>
</div>
<div class="col-auto text-right" v-else>
<div class="font-heading bold">MYR {{ formatNumber(paymentTotal - refundTotal > 0 ? (paymentTotal - refundTotal) : 0) }}</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-primary" v-if="refundTotal > 0 && is1688Supplier && this.inputPaymentTotal > 0">
<div class="col">
<div class="font-heading all-caps fs-10">Payment Total After Refund:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading bold">{{ this.supplierRefunds.length > 0 ? this.supplierRefunds[0].currency.short_code : 'MYR' }} {{ refundTotal > this.inputPaymentTotal ? '0.00' : formatNumber(this.inputPaymentTotal - refundTotal) }}</div>
</div>
</div>
</div>
</div>
<div class="row" v-if="is1688Supplier">
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.payment_total">
<label class="all-caps">Payment Total</label>
<input type="text" class="form-control" v-model="parameters.payment_total" v-money="productPrice">
</validation-wrapper-component>
</div>
<div class="col-auto b-r b-t b-b b-grey m-r-15">
<div class="row h-100 align-items-center">
<div class="col">
<div class="font-heading fs-10 muted">MYR</div>
</div>
</div>
</div>
</div>
<div class="row" v-else>
<div class="col">
<validation-wrapper-component :validator="$v.parameters.service_charges">
<label class="all-caps">Service Charges</label>
<input type="text" class="form-control" v-model="parameters.service_charges" v-money="productPrice">
</validation-wrapper-component>
</div>
</div>
<div class="row" v-if="paymentTotal > 0">
<div class="col">
<button class="btn btn-xs btn-success b-rad-none p-t-5 p-b-5 all-caps fs-10 btn-block" @click="submitForm()">Create Bill Group</button>
</div>
</div>
<div class="row" v-if="this.supplier.id !== ''">
<div class="col mx-3 m-t-20">
<div class="m-b-20">
<small class="all-caps muted fs-15">Supplier Refund</small>
</div>
<list-component :key="supplierRefundListKey" section="supplierRefundListSection" :options="{'per_page': 20, 'type': 15, 'currency_rate_is_not_equal': 1, 'status': 2, 'receiver_in': [this.supplier.id]}" :endpoint="route('api.transaction.list')">
<template slot="list" slot-scope="{data}">
<supplier-refund-component section="supplierRefundListSection" :data="data" :is1688Supplier="is1688Supplier" :payments="payments" :supplierRefunds="supplierRefunds" :refundTotal="refundTotal" :paymentTotal="paymentTotal" :inputPaymentTotal="inputPaymentTotal" v-on:input="refundOrder($event)"></supplier-refund-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
</template>
<script>
import FormHandler from '../../../general/mixins/formHandler';
import { required, minValue, requiredIf} from "vuelidate/lib/validators";
import ModalComponent from "../../general/elements/ModalComponent";
export default {
components: {ModalComponent},
props: {
payments: {
type: Array,
required: true
},
supplier: {
type: Object,
required: true
},
refreshList: {
type: Function,
required: true
},
},
watch: {
'supplier': function() {
this.supplierRefundListKey ++;
this.supplierRefunds = []
},
},
data(){
return {
supplierRefunds: [],
supplierRefundListKey: 1,
parameters: {
service_charges: '0',
payment_total: '0',
}
}
},
validations: {
parameters: {
service_charges: {
required: requiredIf(function () {
return !this.is1688Supplier;
})
},
payment_total: {
required: requiredIf(function () {
return this.is1688Supplier;
}),
minValue: function(value) {
const amount = parseFloat(value.replaceAll(',', ''))
return this.is1688Supplier ? amount >= 1 : true;
}
}
}
},
computed: {
inputPaymentTotal(){
return parseFloat(this.parameters.payment_total.replaceAll(',', ''));
},
originalTotal(){
return this.payments.reduce(function (total, currentValue) {
return total + currentValue.original_amount;
}, 0);
},
total(){
return this.payments.reduce(function (total, currentValue) {
return total + currentValue.amount;
}, 0);
},
paymentTotal(){
return this.total ? this.total + parseFloat(this.parameters.service_charges.replaceAll(',', '')) : 0;
},
refundOriginalTotal(){
return this.supplierRefunds.reduce(function (total, currentValue) {
return total + currentValue.original_amount;
}, 0);
},
refundTotal(){
return this.supplierRefunds.reduce(function (total, currentValue) {
return total + currentValue.amount;
}, 0);
},
billGroupCurrencyRate(){
const paymentTotal = this.inputPaymentTotal;
return this.originalTotal && paymentTotal > 0 ? (this.originalTotal / paymentTotal).toFixed(5) : 1;
},
is1688Supplier(){
return (this.supplier.id === 4548 || this.supplier.id === 2729)
}
},
methods: {
submitForm(){
this.parameters = {
payments: this.payments,
supplierRefunds: this.supplierRefunds.sort((a, b) => {
return a.amount - b.amount;
}),
service_charges: this.is1688Supplier ? '0' : this.parameters.service_charges,
payment_total: this.is1688Supplier ? this.parameters.payment_total : '0',
};
this.submit(route('api.transaction.supplier.bill_group.create', this.supplier.id), 'post', 'transactionGroupsListPaymentSection', true, true)
},
successHandler(){
this.refreshList();
this.supplierRefunds = []
this.supplierRefundListKey ++;
this.$store.dispatch('toggleSection', {name: 'paymentInProgressBillGroupList', status: !this.$store.getters.isShowing('paymentInProgressBillGroupList')});
this.parameters = {
payments: [],
service_charges: '0',
payment_total: '0',
};
},
refundOrder(supplierRefund){
this.supplierRefunds.some(item => item.id === supplierRefund.id) ? this.supplierRefunds = this.supplierRefunds.filter(item => item.id !== supplierRefund.id) : this.supplierRefunds.push(supplierRefund);
},
formatNumber(value) {
return (Math.round((value + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
},
mixins: [FormHandler]
}
</script>
@@ -0,0 +1,136 @@
<template>
<div class="row p-r-10">
<div class="col p-r-5">
<div class="row m-b-20">
<div class="col-7">
<div class="row m-b-15">
<div class="col-12">
<div class="row m-b-5">
<div class="col p-r-0">
<div class="btn btn-xs btn-outline-primary btn-block text-left b-rad-none p-t-0 p-b-0 p-l-15 p-r-15" @click="selectedSupplier.status = !selectedSupplier.status">
<div class="row">
<div class="col p-t-5 p-b-5 fs-9">
{{selectedSupplier.name}} - {{selectedSupplier.reference}}
</div>
<div class="col-auto b-l b-success">
<div class="row h-100 align-items-center">
<div class="col">
<i class="fa" :class="[{'fa-angle-down': !selectedSupplier.status}, {'fa-angle-up': selectedSupplier.status}]"></i>
</div>
</div>
</div>
</div>
</div>
<div class="relative w-100">
<div class="absolute w-100 b-l b-b b-r b-primary" :class="[{'hide': !selectedSupplier.status}]" style="top: 100%; right: 0; z-index: 1;">
<div class="row text-left no-margin bg-white">
<div class="col no-padding">
<div class="row no-margin" v-for="supplier in suppliers" v-bind:key="supplier.id" :data="supplier">
<div class="col b-b b-grey p-t-10 p-b-10 pointer hover-t-10 p-b-10" :class="[{'bg-primary-light': selectedSupplier.id === supplier.id}, {'text-white': selectedSupplier.id === supplier.id}, {'hover-primary': selectedSupplier.id !== supplier.id}, {'pointer': selectedSupplier.id !== supplier.id}]" @click="updateSupplier(supplier)">
<div class="row align-items-center justify-content-center">
<div class="col">
<div class="font-heading fs-10">{{supplier.name}} - {{supplier.reference}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col" v-if="section === 'paymentInProgressBillGroupList'">
<list-component :key="key" section="paymentInProgressBillGroupListSection" :endpoint="route('api.transaction.group.bill.list')" :options="{per_page: 20, status: 0, ...this.selectedSupplier.id && {issuer_in: [this.selectedSupplier.id]}, order_by: {column: 'id', DESC: true}}">
<template slot="list" slot-scope="{data}">
<bill-group-component :data="data" :section="section" :selectedBillGroup="selectedBillGroup" v-on:input="selectBillGroup($event)"></bill-group-component>
</template>
</list-component>
</div>
<div class="col" v-if="section === 'paymentVerificationBillGroupList'">
<list-component :key="key" section="paymentVerificationBillGroupListSection" :endpoint="route('api.transaction.group.bill.list')" :options="{per_page: 20, has_pending_verify_transaction: true, ...this.selectedSupplier.id && {issuer_in: [this.selectedSupplier.id]}, order_by: {column: 'id', DESC: true}}">
<template slot="list" slot-scope="{data}">
<bill-group-component :data="data" :section="section" :selectedBillGroup="selectedBillGroup" v-on:input="selectBillGroup($event)"></bill-group-component>
</template>
</list-component>
</div>
<div class="col" v-if="section === 'paidBillGroupList'">
<list-component :key="key" section="paidBillGroupListSection" :endpoint="route('api.transaction.group.bill.list')" :options="{per_page: 20, status_in: [2, 3], ...this.selectedSupplier.id && {issuer_in: [this.selectedSupplier.id]}, order_by: {column: 'id', DESC: true}}">
<template slot="list" slot-scope="{data}">
<bill-group-component :data="data" :section="section" :selectedBillGroup="selectedBillGroup" v-on:input="selectBillGroup($event)"></bill-group-component>
</template>
</list-component>
</div>
</div>
</div>
<div class="col-12 col-md" v-if="section === 'paymentInProgressBillGroupList'">
<bill-group-payment-summary-component :selectedBillGroup="selectedBillGroup" :refreshList="updateList" section="paymentInProgressBillGroupList"></bill-group-payment-summary-component>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
section:{
type: String,
required: true
},
},
data(){
return {
suppliers: [],
selectedSupplier: {
id: '',
name: '',
status: false
},
selectedBillGroup: {
id: ''
},
key: 1
}
},
computed: {
pendingQueue() {
return this.$store.getters.isShowing(this.section);
}
},
watch: {
pendingQueue(){
this.updateList();
},
},
created(){
this.submit(route('api.company.list') + '?filters=' + JSON.stringify({'business_type': 3, 'status_in': [1, 2, 0]}), 'get', 'transactionGroupsListPaymentSection', false, false)
},
methods: {
successHandler(response){
this.suppliers = response.payload.data;
},
updateSupplier(supplier){
if(supplier !== this.selectedSupplier){
this.selectedSupplier = supplier;
this.updateList();
}
},
updateList(){
this.selectedSupplier.status = false;
this.selectedBillGroup = {
id: ''
};
this.key++;
},
selectBillGroup(billGroup){
this.selectedBillGroup = billGroup;
}
}
}
</script>
@@ -0,0 +1,113 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col p-r-5">
<div class="row m-b-20">
<div class="col-12 col-md-7">
<div class="row m-b-15">
<div class="col">
<div class="row m-b-5">
<div class="col p-r-0">
<div class="btn btn-xs btn-outline-primary btn-block text-left b-rad-none p-t-0 p-b-0 p-l-15 p-r-15" @click="selectedSupplier.status = !selectedSupplier.status">
<div class="row">
<div class="col p-t-5 p-b-5 fs-9">
{{selectedSupplier.name}} - {{selectedSupplier.reference}}
</div>
<div class="col-auto b-l b-success">
<div class="row h-100 align-items-center">
<div class="col">
<i class="fa" :class="[{'fa-angle-down': !selectedSupplier.status}, {'fa-angle-up': selectedSupplier.status}]"></i>
</div>
</div>
</div>
</div>
</div>
<div class="relative w-100">
<div class="absolute w-100 b-l b-b b-r b-primary" :class="[{'hide': !selectedSupplier.status}]" style="top: 100%; right: 0; z-index: 1;">
<div class="row text-left no-margin bg-white">
<div class="col no-padding">
<div class="row no-margin" v-for="supplier in suppliers" v-bind:key="supplier.id" :data="supplier">
<div class="col b-b b-grey p-t-10 p-b-10 pointer hover-t-10 p-b-10" :class="[{'bg-primary-light': selectedSupplier.id === supplier.id}, {'text-white': selectedSupplier.id === supplier.id}, {'hover-primary': selectedSupplier.id !== supplier.id}, {'pointer': selectedSupplier.id !== supplier.id}]" @click="updateSupplier(supplier)">
<div class="row align-items-center justify-content-center">
<div class="col">
<div class="font-heading fs-10">{{supplier.name}} - {{supplier.reference}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" v-if="this.selectedSupplier.id !== ''">
<div class="col">
<list-component :key="currencyOrderKey" section="transactionGroupsListPaymentSection" :options="{'per_page': 20, 'without_bill_group': true, 'issuer_in': [this.selectedSupplier.id]}" :endpoint="route('api.transaction.group.list')">
<template slot="list" slot-scope="{data}">
<transaction-group-payment-component section="transactionGroupsListPaymentSection" :data="data" :payments="payments" v-on:input="updateOrder($event)"></transaction-group-payment-component>
</template>
</list-component>
</div>
</div>
<div class="row" v-else>
<div class="col">
<loading-component style="height: 200px; top: 0;" key="1" color="success"></loading-component>
</div>
</div>
</div>
<div class="col-12 col-md">
<supplier-white-form-place-order-form-component :payments="payments" :refreshList="updateList" :supplier="selectedSupplier" section="transactionGroupsListPaymentSection"></supplier-white-form-place-order-form-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
suppliers: [],
selectedSupplier: {
id: '',
name: '',
status: false
},
payments: [],
currencyOrderKey: 1,
}
},
created(){
this.submit(route('api.company.list') + '?filters=' + JSON.stringify({'business_type': 3, 'status_in': [1, 2, 0]}), 'get', 'transactionGroupsListPaymentSection', false, false)
},
methods: {
successHandler(response){
this.suppliers = response.payload.data;
this.selectedSupplier= this.suppliers[0];
this.updateList();
},
updateSupplier(supplier){
if(supplier !== this.selectedSupplier){
this.selectedSupplier= supplier;
this.updateList();
}
},
updateList(){
this.currencyOrderKey ++;
this.selectedSupplier.status = false;
this.payments = [];
},
updateOrder(payment){
this.payments.some(item => item.id === payment.id) ? this.payments = this.payments.filter(item => item.id !== payment.id) : this.payments.push(payment);
}
}
}
</script>
@@ -0,0 +1,32 @@
<template>
<div class="row w-100">
<div class="col">
<loading-component style="height: 300px; top: 0;" key="1" color="success" v-show="isLoading" ></loading-component>
<!-- <div class="row justify-content-center padding-30" v-show="!isLoading"> -->
<div class="row justify-content-center" v-show="!isLoading">
<div class="col">
<div class="row" v-for="remark in data.remarks">
<div class="col">
<remark-list-component :section="section" :data="remark"></remark-list-component>
</div>
</div>
<remark-comment-form-component :section="section" :module_type="module_type" :id="data.id"></remark-comment-form-component>
</div>
</div>
</div>
</div>
</template>
<script>
import ModalFormHandler from '../../../general/mixins/modalFormHandler';
export default {
props: {
module_type: {
type: String,
required: true
}
},
mixins: [ModalFormHandler]
}
</script>
@@ -0,0 +1,71 @@
<template>
<!-- <div class="row m-b-20 parentContainer"> -->
<div class="row m-b-20 parentContainer bg-white padding-10 rounded" style="margin: 10px">
<div class="col">
<div class="row justify-content-center align-items-center">
<div class="hide col-auto btn-rounded padding-10 d-flex justify-content-center align-items-center bg-master-lighter" style="width: 30px;height: 30px; box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.14);">
<span class="bold">{{item.commenter.name.substr(0, 1)}}</span>
</div>
<div class="col">
<div class="row">
<div class="col-auto">
<p class="no-margin muted bold fs-14">{{item.commenter.name}}</p>
</div>
<div class="col-auto">
<span class="fs-12 muted normal">{{item.long_ago}}</span>
</div>
<div class="col d-flex justify-content-end">
<div class="row">
<div class="col p-t-5 p-b-5 b-a b-grey">
<div class="row" v-if="!isEdit && $store.getters.isAdmin">
<div class="col">
<i class="fa fa-edit pointer" @click="isEdit = !isEdit"></i>
</div>
<div class="col">
<div class="requestModal pointer" data-type="deleteComment">
<i class="fa fa-trash"></i>
</div>
</div>
</div>
<div class="row" v-if="isEdit">
<div class="col">
<i class="fa fa-times pointer" @click="isEdit = !isEdit"></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<p v-if="!isEdit">{{item.content}}</p>
<container-comment-form-component :section="section" :data="item" :id="item.owner_id" v-on:submit="isEdit=false" v-if="isEdit"></container-comment-form-component>
</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteComment">
<delete-container-comment-form-component :section="section" :data="item"></delete-container-comment-form-component>
</modal-component>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
section: {
type: String,
required: true,
}
},
data(){
return {
isEdit: false,
}
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,32 @@
<template>
<div class="row">
<div class="col">
<loading-component style="height: 300px; top: 0;" key="1" color="success" v-show="isLoading" ></loading-component>
<div class="row justify-content-center" v-show="!isLoading">
<div class="col">
<div class="row m-b-20">
<div class="col text-center">
<h3 class="all-caps">Are you Sure?</h3>
<div class="fs-11">Are you sure you want to delete this remark?</div>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div class="btn btn-sm btn-success btn-block b-rad-none" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-danger btn-block b-rad-none" @click="submit(route('api.remark.delete', item.id), 'delete', section, true, true)">Delete</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
import ModalFormHandler from '../../../general/mixins/modalFormHandler';
export default {
mixins: [componentHandler, ModalFormHandler]
}
</script>
@@ -57,8 +57,17 @@
submitForm() {
if (this.params) this.parameters = this.params;
return this.submit(this.apiRoute, this.apiMethod, this.section, true, true);
},
successHandler(){
this.closeModal();
this.formHandler();
if (this.section && (this.section === 'paymentInProgressBillGroupList' || this.section === 'paymentVerificationBillGroupList')) {
this.$store.dispatch('toggleSection', {name: 'paymentInProgressBillGroupList', status: !this.$store.getters.isShowing('paymentInProgressBillGroupList')});
this.$store.dispatch('toggleSection', {name: 'paymentVerificationBillGroupList', status: !this.$store.getters.isShowing('paymentVerificationBillGroupList')});
this.$store.dispatch('toggleSection', {name: 'paidBillGroupList', status: !this.$store.getters.isShowing('paidBillGroupList')});
}
},
},
mixins: [componentHandler, ModalFormHandler]
}
@@ -0,0 +1,55 @@
<template>
<div class="row m-t-5">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.content">
<input type="text" class="form-control fs-12" placeholder="Write your comments..." v-model.trim="parameters.content">
</validation-wrapper-component>
</div>
<div class="col-auto b-a b-grey d-flex justify-content-center align-items-center rounded pointer">
<i class="fa fa-paper-plane" @click="submitForm"></i>
</div>
</div>
</template>
<script>
import modalFormHandler from '../../../general/mixins/modalFormHandler';
import { required } from "vuelidate/lib/validators";
export default {
props: {
id: {
type: Number,
required: true
},
module_type: {
type: String,
required: true
}
},
data() {
return {
parameters: {
// content: this.data.remarks.length > 0 ? this.data.remarks[0].content : '',
content: '',
model_type: this.module_type
}
};
},
validations: {
parameters: {
content: { required },
}
},
created() {
if (this.data) {
this.parameters.content = this.data.content;
}
},
methods: {
submitForm() {
this.submit(this.data ? this.route('api.remark.update', this.data.id) : this.route('api.remark.create', this.id), this.data ? 'put' : 'post', this.section, true, true);
this.$emit('submit')
}
},
mixins: [modalFormHandler]
}
</script>
@@ -0,0 +1,73 @@
<template>
<div class="row">
<div class="col">
<loading-component style="height: 50px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
<div class="row" v-show="!$store.getters.isLoading(section)">
<div class="col">
<div class="row m-b-20">
<div class="col">
<h6 class="all-caps m-b-5 bold no-margin">{{ data.remarks.length > 0 ? 'Edit' : 'Create' }} Remark</h6>
</div>
</div>
<div class="row m-b-5 animate__animated animate__fadeInUpBig animate__fast" v-if="error">
<div class="col">
<small class="bold fs-10 text-danger">{{error}}</small>
</div>
</div>
<div class="row m-b-10">
<div class="col">
<validation-wrapper-component :validator="$v.parameters.content">
<label>Remark</label>
<input type="text" class="form-control" v-model="parameters.content">
</validation-wrapper-component>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div class="btn btn-sm btn-default bg-master-lightest btn-block b-rad-none" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-primary btn-block b-rad-none" @click="submitForm()">{{ data.remarks.length > 0 ? 'Edit' : 'Create' }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
import ModalFormHandler from '../../../general/mixins/modalFormHandler';
export default {
props: {
data:{
type: Object,
required: true
},
module_type: {
type: String,
required: true
}
},
data(){
return {
parameters: {
content: this.data.remarks.length > 0 ? this.data.remarks[0].content : '',
model_type: this.module_type
}
}
},
validations: {
parameters: {
content: { required: required },
}
},
methods: {
submitForm() {
this.submit(this.data.remarks.length > 0 ? this.route('api.remark.update', this.data.remarks[0].id) : this.route('api.remark.create', this.data.id), this.data.remarks.length > 0 ? 'put' : 'post', this.section, true, true);
}
},
mixins: [ModalFormHandler]
}
</script>
@@ -1,10 +1,10 @@
<template>
<div v-if="validator.$error" class="text-danger fs-10">
<small class="bold" v-for="(object, param) in validator.$params">
<span class="btn-block" v-if="object.type === 'required'">{{errorMessages[param]}}</span>
<span class="btn-block" v-if="object.type === 'minLength'">{{errorMessages[param]}} {{object.min}} characters</span>
<span class="btn-block" v-if="object.type === 'sameAs'">{{errorMessages[param]}} {{object.eq}} field</span>
<span class="btn-block" v-if="object.type === 'maxValue'">{{errorMessages[param]}} {{object.max}}</span>
<span class="btn-block" v-if="object && object.type === 'required'">{{errorMessages[param]}}</span>
<span class="btn-block" v-if="object && object.type === 'minLength'">{{errorMessages[param]}} {{object.min}} characters</span>
<span class="btn-block" v-if="object && object.type === 'sameAs'">{{errorMessages[param]}} {{object.eq}} field</span>
<span class="btn-block" v-if="object && object.type === 'maxValue'">{{errorMessages[param]}} {{object.max}}</span>
</small>
</div>
</template>
@@ -27,7 +27,7 @@
</div>
<div class="col">
<div class="row fs-12 text-center">
<div class="col p-t-20 p-b-20 bg-master-lighter tabButton" tab-name="refunds">
<div class="col p-t-20 p-b-20 bg-master-lighter tabButton" tab-name="pre-refunds">
<div class="row justify-content-center m-b-5">
<div class="col-auto">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
@@ -38,7 +38,26 @@
</div>
<div class="row">
<div class="col">
<div class="fs-12 m-t-5 all-caps">Refunds</div>
<div class="fs-12 m-t-5 all-caps">Pre-Refunds</div>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="row fs-12 text-center">
<div class="col p-t-20 p-b-20 bg-master-lighter tabButton" tab-name="post-refunds">
<div class="row justify-content-center m-b-5">
<div class="col-auto">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
width="35" height="35"
viewBox="0 0 172 172"
style=" fill:#000000;"><defs><linearGradient x1="86" y1="45.01563" x2="86" y2="152.92681" gradientUnits="userSpaceOnUse" id="color-1_48314_gr1"><stop offset="0" stop-color="#009add"></stop><stop offset="1" stop-color="#00baa4"></stop></linearGradient><linearGradient x1="86" y1="45.01563" x2="86" y2="152.92681" gradientUnits="userSpaceOnUse" id="color-2_48314_gr2"><stop offset="0" stop-color="#009add"></stop><stop offset="1" stop-color="#00baa4"></stop></linearGradient><linearGradient x1="86" y1="16.79688" x2="86" y2="92.05225" gradientUnits="userSpaceOnUse" id="color-3_48314_gr3"><stop offset="0" stop-color="#4ec9ff"></stop><stop offset="1" stop-color="#2bffe6"></stop></linearGradient><linearGradient x1="86" y1="45.01563" x2="86" y2="152.92681" gradientUnits="userSpaceOnUse" id="color-4_48314_gr4"><stop offset="0" stop-color="#009add"></stop><stop offset="1" stop-color="#00baa4"></stop></linearGradient></defs><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z" fill="none"></path><g><path d="M147.8125,48.375h-32.25v5.375h32.25c1.50769,0 2.6875,1.12875 2.6875,2.56925v75.25c0,1.51844 -1.23087,2.80575 -2.6875,2.80575h-123.625c-1.45662,0 -2.6875,-1.28731 -2.6875,-2.80575v-75.25c0,-1.4405 1.17981,-2.56925 2.6875,-2.56925h32.25v-5.375h-32.25c-4.52038,0 -8.0625,3.49106 -8.0625,7.94425v75.25c0,3.55019 2.25481,6.54944 5.375,7.67819v3.07181c0,4.51231 3.61737,8.18075 8.0625,8.18075h112.875c4.44512,0 8.0625,-3.66844 8.0625,-8.18075v-3.07181c3.12019,-1.12875 5.375,-4.128 5.375,-7.67819v-75.25c0,-4.45319 -3.54212,-7.94425 -8.0625,-7.94425zM142.4375,145.125h-112.875c-1.45662,0 -2.6875,-1.28731 -2.6875,-2.80575v-2.56925h118.25v2.56925c0,1.51844 -1.23087,2.80575 -2.6875,2.80575z" fill="url(#color-1_48314_gr1)"></path><path d="M126.3125,129h-83.3125v-2.6875c0,-7.51425 -6.02806,-13.4375 -13.4375,-13.4375h-2.6875v-37.625h2.6875c6.3425,0 13.4375,-5.79156 13.4375,-13.46706v-2.70363l13.4375,0.04569v5.375l-8.2775,-0.01613c-1.26044,7.95231 -7.94425,14.61731 -15.91,15.86969v27.219c8.25869,1.18788 14.80006,7.76956 15.94762,16.05244h75.6155c1.17444,-8.30438 7.70238,-14.85112 15.93688,-16.03631v-27.262c-7.96844,-1.247 -14.65494,-7.89587 -15.91269,-15.82669h-8.27481v-5.375h13.4375v2.6875c0,6.30219 5.74587,13.4375 13.4375,13.4375h2.6875v37.50675l-2.6875,0.01613c-7.40944,0 -13.4375,6.03344 -13.4375,13.45094v2.6875z" fill="url(#color-2_48314_gr2)"></path><path d="M104.8125,53.75h-8.0625c-1.4835,0 -2.6875,1.14487 -2.6875,2.63106v29.61356c0,1.48619 -1.204,2.69288 -2.6875,2.69288h-10.75c-1.4835,0 -2.6875,-1.20669 -2.6875,-2.69288v-29.61356c0,-1.48619 -1.204,-2.63106 -2.6875,-2.63106h-8.0625c-2.2145,0 -3.47763,-2.881 -2.15,-4.87781l16.65175,-25.04481c2.05056,-3.08256 6.57094,-3.08794 8.6215,-0.00806l16.65175,25.05556c1.32762,1.99681 0.0645,4.87512 -2.15,4.87512z" fill="url(#color-3_48314_gr3)"></path><path d="M86,112.875c-10.37375,0 -18.8125,-8.0625 -18.8125,-18.8125h5.375c0,8.0625 6.02806,13.4375 13.4375,13.4375c7.40944,0 13.4375,-5.375 13.4375,-13.4375h5.375c0,10.75 -8.43875,18.8125 -18.8125,18.8125z" fill="url(#color-4_48314_gr4)"></path></g></g></svg>
</div>
</div>
<div class="row">
<div class="col">
<div class="fs-12 m-t-5 all-caps">Post-Refunds</div>
</div>
</div>
</div>
@@ -79,13 +98,84 @@
</div>
</div>
</div>
<div class="row tabsContainer hide tabContent" tab-name="refunds">
<div class="row tabsContainer hide tabContent" tab-name="pre-refunds">
<div class="col">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Fully Refund</small>
</div>
<div class="col text-right">
<a :href="route('orders.refunds') + '?type=pre-full'" target="_blank">
<button class="btn btn-success btn-xs">Export Refunded Transactions</button>
</a>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="2" section="listRefundTransactionSection" :endpoint="route('api.transaction.list')" :options="{'type': 6, status: 1}">
<list-component key="2" section="listPreFullRefundTransactionSection" :endpoint="route('api.transaction.list')" :options="{'type': 6, status: 1, owner_does_not_have_transaction_type: 3, is_partial_refund: false}">
<template slot="list" slot-scope="{data}">
<refund-verification-component section="listRefundTransactionSection" :data="data"></refund-verification-component>
<refund-verification-component section="listPreFullRefundTransactionSection" :data="data"></refund-verification-component>
</template>
</list-component>
</div>
</div>
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Partial Refund</small>
</div>
<div class="col text-right">
<a :href="route('orders.refunds') + '?type=pre-partial'" target="_blank">
<button class="btn btn-success btn-xs">Export Refunded Transactions</button>
</a>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="3" section="listPrePartialRefundTransactionSection" :endpoint="route('api.transaction.list')" :options="{'type': 6, status: 1, owner_does_not_have_transaction_type: 3, is_partial_refund: true}">
<template slot="list" slot-scope="{data}">
<refund-verification-component section="listPrePartialRefundTransactionSection" :data="data"></refund-verification-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
<div class="row tabsContainer hide tabContent" tab-name="post-refunds">
<div class="col">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Fully Refund</small>
</div>
<div class="col text-right">
<a :href="route('orders.refunds') + '?type=post-full'" target="_blank">
<button class="btn btn-success btn-xs">Export Refunded Transactions</button>
</a>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="4" section="listPostFullRefundTransactionSection" :endpoint="route('api.transaction.list')" :options="{'type': 6, status: 1, owner_has_transaction_type: 3, is_partial_refund: false}">
<template slot="list" slot-scope="{data}">
<refund-verification-component section="listPostFullRefundTransactionSection" :data="data"></refund-verification-component>
</template>
</list-component>
</div>
</div>
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Partial Refund</small>
</div>
<div class="col text-right">
<a :href="route('orders.refunds') + '?type=post-partial'" target="_blank">
<button class="btn btn-success btn-xs">Export Refunded Transactions</button>
</a>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="5" section="listPostPartialRefundTransactionSection" :endpoint="route('api.transaction.list')" :options="{'type': 6, status: 1, owner_has_transaction_type: 3, is_partial_refund: true}">
<template slot="list" slot-scope="{data}">
<refund-verification-component section="listPostPartialRefundTransactionSection" :data="data"></refund-verification-component>
</template>
</list-component>
</div>
@@ -60,7 +60,7 @@
<tr class="voucher">
<td colspan="4"></td>
<td class="right middle">Voucher ({{ $voucher_redemption->voucher->code }})</td>
<td class="right middle">-{{ number_format($voucherDiscount, 2) }}</td>
<td class="right middle">{{ number_format($voucherDiscount, 2) }}</td>
</tr>
@endif
File diff suppressed because one or more lines are too long
@@ -38,6 +38,11 @@
<div class="text-white all-caps fs-12">Currency Orders</div>
</a>
</div>
<div class="col-auto p-r-20" v-if="$store.getters.isAdmin">
<a href="{{route('supplier.payments')}}">
<div class="text-white all-caps fs-12">Supplier Currency Orders</div>
</a>
</div>
<div class="col-auto p-r-20" v-if="$store.getters.isAdmin">
<a href="{{route('bookings')}}">
<div class="text-white all-caps fs-12">Transfers</div>
+2
View File
@@ -67,6 +67,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function
require __DIR__ . '/milestone.php';
require __DIR__.'/remark.php';
// require __DIR__ . '/accounting.php'; //cief todo: To check if this is needed
require __DIR__ . '/job.php';
+11
View File
@@ -0,0 +1,11 @@
<?php
use Illuminate\Support\Facades\Route;
Route::group(['namespace' => 'Remarks', 'as' => 'remark.', 'prefix' => 'remark'], function () {
Route::get('/{id}/show', 'FetchRemarkController@fetch')->name('show');
Route::get('/list', 'ListRemarksController@list')->name('list');
Route::post('/{id}/create', 'CreateRemarkController@create')->name('create');
Route::put('/update/{id}', 'UpdateRemarkController@update')->name('update');
Route::delete('/delete/{id}', 'DeleteRemarkController@delete')->name('delete');
});
+10
View File
@@ -8,6 +8,7 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' =>
Route::delete('/suspend/{id}', 'SuspendTransactionController@suspend')->name('suspend');
route::post('/supplier/{id}/bill/create', 'CreateSupplierTransactionController@create')->name('supplier.create');
route::post('/supplier/{id}/bill/group/create', 'CreateSupplierBillGroupController@create')->name('supplier.bill_group.create');
route::post('{id}/bill/verification', 'CreatePaymentProofDocumentController@verify')->name('bill.verification');
route::post('{id}/bill/pay', 'CreatePaymentProofDocumentController@pay')->name('bill.pay');
Route::put('/{id}/bill/{status}', 'UpdatePaymentTransactionStatusController@update')->where('status', 'pending|complete')->name('bill.status');
@@ -33,5 +34,14 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' =>
Route::put('/{id}/update', 'UpdateGroupController@update')->name('update');
Route::post('/{id}/approve', 'CreateBulkPurchaseOrderDocumentController@aprove')->name('approve');
Route::post('/bulk/po', 'CreateBulkPurchaseOrderDocumentController@create')->name('bulk.po');
Route::group(['prefix' => 'bills', 'as' => 'bill.'], function () {
Route::get('/list', 'ListBillGroupsController@list')->name('list');
Route::post('/transaction/{id}', 'CreateBillGroupPaymentProofDocumentController@create')->name('payment_proof.create');
Route::post('/transaction/{id}/approval/{status}', 'ApproveBillGroupPaymentVerificationController@approve')->where('status', 'approve|reject')->name('payment_proof.approval');
Route::delete('/{id}/delete', 'DeleteBillGroupController@delete')->name('delete');
Route::post('/{id}/pay', 'CreateBillGroupPaymentTransactionController@pay')->name('pay');
// Route::post('/bulk/po', 'CreateBulkPurchaseOrderDocumentController@create')->name('bulk.po');
});
});
});

Some files were not shown because too many files have changed in this diff Show More