Merge branch 'master' of gitlab.com:CIEFWorldwideSdnBhd/exchange-2.0 into refund-booking

This commit is contained in:
JiaSheng
2024-01-08 21:54:23 +08:00
104 changed files with 3777 additions and 781 deletions
@@ -16,6 +16,8 @@ class CreatedAfterOrEqual implements Filter
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('created_at', '>=', Carbon::parse($value));
$table = $builder->getModel()->getTable();
$startDate = Carbon::createFromFormat('d-m-Y', $value)->startOfDay();
return $builder->where("{$table}.created_at", '>=', $startDate);
}
}
@@ -15,6 +15,8 @@ class CreatedBeforeOrEqual implements Filter
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('created_at', '<=', Carbon::parse($value));
$table = $builder->getModel()->getTable();
$endDate = Carbon::createFromFormat('d-m-Y', $value)->endOfDay();
return $builder->where("{$table}.created_at", '<=', $endDate);
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class GroupByImportedDate implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->groupby('imported_date');
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class ImportedDateFrom implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDate('imported_date', '>=', date('Y-m-d',strtotime($value)));
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class ImportedDateTo implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDate('imported_date', '<=', date('Y-m-d',strtotime($value)));
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class IsMappedFalseOrMappedButStatusIn implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where(function($q) use ($value) {
$q->whereDoesntHave('owners');
$q->orwhereDoesntHave('owner_status');
});
}
}
@@ -14,7 +14,8 @@ class OwnerId implements Filter
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('owner_id', $value);
$table = $builder->getModel()->getTable();
return $builder->where("{$table}.owner_id", $value);
}
}
@@ -14,7 +14,8 @@ class OwnerType implements Filter
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('owner_type', $value);
$table = $builder->getModel()->getTable();
return $builder->where("{$table}.owner_type", $value);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionInvoiceReference implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) use ($value) {
return $query->where('Invoice_reference', $value);
});
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionOwnerInvoiceOrReceiptRefNotNull implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) {
return $query->where(function ($q) {
$q->orWhereNotNull('invoice_reference')->orWhereNotNull('receipt_reference');
});
});
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionOwnerReference implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) use ($value) {
return $query->where('owner_reference', $value);
});
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionPostingEnd implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDate('posting_date', '<=', date('Y-m-d',strtotime($value)));
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionPostingStart implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereDate('posting_date', '>=', date('Y-m-d',strtotime($value)));
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class StatementTransactionReceiptReference implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) use ($value) {
return $query->where('receipt_reference', $value);
});
}
}
@@ -14,7 +14,8 @@ class StatusIn implements Filter
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereIn('status', $value);
$table = $builder->getModel()->getTable();
return $builder->whereIn("{$table}.status", $value);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class WhereHasOwnersAndNotNull implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) use ($value) {
return $query->whereNotNull($value);
});
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class WhereHasOwnersAndNull implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('owners', function ($query) use ($value) {
return $query->whereNull($value);
});
}
}
@@ -0,0 +1,29 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class WithBookingMarkingLike implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->join('transactions as t2', 't2.payment_reference', '=', 'transactions.bill_no')
->join('bookings', function ($join) use ($value) {
$join->on('bookings.id', '=', 't2.owner_id')
->where('bookings.marking', 'LIKE', '%'.$value.'%');
})
->addSelect(['transactions.*','t2.owner_id as bookingId', 'bookings.marking as bookingMarking']);
}
}
@@ -13,9 +13,15 @@ class CreateBankStatementTransactionOwners implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $transactions;
public function __construct($transactions) {
$this->transactions = $transactions;
}
public function handle()
{
(App()->make(CreateBankStatementTransactionOwnersProcessor::class))->execute();
(App()->make(CreateBankStatementTransactionOwnersProcessor::class))->execute($this->transactions);
}
public function delay($delay)
@@ -135,7 +135,7 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
public function logic(Request $request): JsonResponse
{
// Determine the approval status
$status = $this->getApprovalStatus($request);
$status = $this->getConstantStatus($request->route('status'));
// Find the statement transaction owner
$owner = $this->getOwner($request);
@@ -143,21 +143,33 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
// Update owner status
$this->updateOwnerStatus($owner, $status);
// If the status is 'approved', handle the approval process
if ($status === ApprovalStatus::APPROVED) {
$this->handleApprovedStatus($owner);
}
// handle the siblings process
$this->handleSiblingsStatus($owner, $this->getSiblingsStatus($status));
// Check and approve remaining matches if any
$this->checkAndApproveRemainingMatches($owner);
$this->checkAndApproveRemainingMatches($owner, $status);
// Return an empty response
return $this->response([]);
}
private function getApprovalStatus(Request $request): int
private function getConstantStatus(String $statusName=null): int
{
return $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED;
switch ($statusName) {
case 'approve':
return ApprovalStatus::APPROVED;
case 'pending_verification':
return ApprovalStatus::PENDING_VERIFICATION;
default:
return ApprovalStatus::REJECTED;
}
}
private function getSiblingsStatus(int $status): int
{
return $status == ApprovalStatus::APPROVED ? ApprovalStatus::REJECTED : ApprovalStatus::PENDING_VERIFICATION;
}
private function getOwner(Request $request): StatementTransactionOwner
@@ -170,23 +182,24 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
$this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status);
}
private function handleApprovedStatus(StatementTransactionOwner $owner): void
private function handleSiblingsStatus(StatementTransactionOwner $owner, int $siblingStatus): void
{
// Reject all other owners with the same system, owner type, and owner ID
$this->rejectOtherOwners($owner);
// update all other owners with the same system, owner type, and owner ID
$this->updateOtherOwners($owner, $siblingStatus);
// Find all siblings and process them
$siblings = $this->getSiblings($owner);
$this->processSiblings($siblings);
foreach ($siblings as $sibling) {
$this->processSibling($sibling, $siblingStatus);
}
}
private function rejectOtherOwners(StatementTransactionOwner $owner): void
private function updateOtherOwners(StatementTransactionOwner $owner, int $status): void
{
StatementTransactionOwner::where('system', $owner->system)
->where('owner_type', $owner->owner_type)
->where('owner_id', $owner->owner_id)
StatementTransactionOwner::getSiblingsOwner()
->where('id', '!=', $owner->id)
->update(['status' => ApprovalStatus::REJECTED]);
->update(['status' => $status]);
}
private function getSiblings(StatementTransactionOwner $owner): Collection
@@ -196,86 +209,65 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
->get();
}
private function processSiblings(Collection $siblings): void
{
foreach ($siblings as $sibling) {
$this->processSibling($sibling);
}
}
private function processSibling(StatementTransactionOwner $sibling): void
private function processSibling(StatementTransactionOwner $sibling, int $siblingStatus): void
{
// Reject the sibling and save the changes
$sibling->status = ApprovalStatus::REJECTED;
$sibling->status = $siblingStatus;
$sibling->save();
// Find all twins and process them
$twins = $this->getTwins($sibling);
$this->processTwins($twins);
}
private function getTwins(StatementTransactionOwner $sibling): Collection
{
return StatementTransactionOwner::where('system', $sibling->system)
->where('owner_type', $sibling->owner_type)
->where('owner_id', $sibling->owner_id)
->where('id', '!=', $sibling->id)
->get();
}
private function processTwins(Collection $twins): void
{
foreach ($twins as $twin) {
$this->processTwin($twin);
}
}
private function getTwins(StatementTransactionOwner $sibling): Collection
{
return StatementTransactionOwner::getSiblingsOwner()
->where('id', '!=', $sibling->id)
->get();
}
private function processTwin(StatementTransactionOwner $twin): void
{
// Find all owners with the same statement transaction ID as the twin
$owners = $this->getOwners($twin);
$owners = $this->getSiblings($twin);
// If there is only one owner (the twin itself), approve it
if ($owners->count() === 1) {
$this->updateOwnerStatus($twin, ApprovalStatus::APPROVED);
$this->updateOwnerStatus($twin, $this->getConstantStatus(request()->route('status')));
}
}
private function getOwners(StatementTransactionOwner $transactionOwner): Collection
{
return StatementTransactionOwner::where('statement_transaction_id', $transactionOwner->statement_transaction_id)
->where('id', '!=', $transactionOwner->id)
->get();
}
private function checkAndApproveRemainingMatches(StatementTransactionOwner $owner): void
private function checkAndApproveRemainingMatches(StatementTransactionOwner $owner, int $status): void
{
// Find all remaining matching owners for the related transaction
$remainingMatches = $this->getRemainingMatches($owner);
$checkStatus = ($status == ApprovalStatus::APPROVED ? ApprovalStatus::PENDING_VERIFICATION : ApprovalStatus::APPROVED);
$remainingMatches = $this->getRemainingMatches($owner, $checkStatus);
// Process each remaining match
foreach ($remainingMatches as $match) {
$this->processRemainingMatch($match);
$this->processRemainingMatch($match, $status);
}
}
private function getRemainingMatches(StatementTransactionOwner $owner): Collection
private function getRemainingMatches(StatementTransactionOwner $owner, int $checkStatus): Collection
{
return StatementTransactionOwner::where('system', $owner->system)
->where('owner_type', $owner->owner_type)
->where('owner_id', $owner->owner_id)
->where('status', ApprovalStatus::PENDING_VERIFICATION)
return StatementTransactionOwner::getSiblingsOwner()
->where('status', $checkStatus)
->get();
}
private function processRemainingMatch(StatementTransactionOwner $match): void
private function processRemainingMatch(StatementTransactionOwner $match, int $status): void
{
// Find all owners with the same statement transaction ID as the match
$owners = $this->getOwners($match);
$owners = $this->getSiblings($match);
// If there is only one owner (the match itself), approve it
if ($owners->count() === 1) {
$this->updateOwnerStatus($match, ApprovalStatus::APPROVED);
$this->updateOwnerStatus($match, $status);
}
}
@@ -57,17 +57,12 @@ class GroupApproveStatementTransactionLogic extends AbstractControllerLogic
public function logic(Request $request): JsonResponse
{
$filters = [
"min_amount" => 0,
"is_mapped" => true,
"is_mapped_with_multiple" => false,
"statement_transaction_owner_type_in" => [1, 2],
"statement_transaction_owner_status_in" => [1]
];
$filters = $request->except(['per_page','order_by']);
$statementTransactions = $this->listsBankStatementTransactions->execute($filters);
foreach ($statementTransactions as $statementTransaction) {
$owners = $statementTransaction->owners;
$owners = $statementTransaction->owners()->where('status', ApprovalStatus::PENDING_VERIFICATION)->get();
if (count($owners)) {
$this->updatesBankStatementTransactionOwnerStatus->execute($owners->first(), ApprovalStatus::APPROVED);
@@ -0,0 +1,49 @@
<?php
namespace App\Classes\Modules\Accounting\ControllersLogic;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Http\Resources\TransactionMappingLogResource;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\ListTransactionMappingLogs;
class HistoryImportedTransactionMappedControllerLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification(): array
{
return [
'title' => 'Retrieved History Imported Invoices',
'message' => 'You have successfully retrieved history imported invoices'
];
}
/** @var ListTransactionMappingLogs */
private $listTransactionMappingLogs;
/**
* UpdateAnnouncementLogic constructor.
* @param ListTransactionMappingLogs $listTransactionMappingLogs
*/
public function __construct(
ListTransactionMappingLogs $listTransactionMappingLogs
) {
$this->listTransactionMappingLogs = $listTransactionMappingLogs;
}
/**
* @param Request $request
* @return JsonResponse
*/
public function logic(Request $request): JsonResponse
{
$query = $this->listTransactionMappingLogs->execute($this->listTransactionMappingLogs->deserializeFilters($request->input('filters')));
return $this->collectionResponse(TransactionMappingLogResource::collection($query));
}
}
@@ -19,6 +19,9 @@ use App\Models\Wallet;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\StatementTransactionOwner;
class UpdateBankStatementDetailLogic extends AbstractControllerLogic
{
@@ -40,14 +43,21 @@ class UpdateBankStatementDetailLogic extends AbstractControllerLogic
/** @var ChecksBillNumber */
private $checksBillNumber;
/** @var UpdatesBankStatementTransactionOwnerStatus */
private $updatesBankStatementTransactionOwnerStatus;
/**
* @param FetchesBankStatementTransaction $fetchesBankStatementTransaction
* @param ChecksBillNumber $checksBillNumber
*/
public function __construct(FetchesBankStatementTransaction $fetchesBankStatementTransaction, ChecksBillNumber $checksBillNumber)
public function __construct(
FetchesBankStatementTransaction $fetchesBankStatementTransaction,
ChecksBillNumber $checksBillNumber,
UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus)
{
$this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction;
$this->checksBillNumber = $checksBillNumber;
$this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus;
}
public function logic(Request $request): JsonResponse
@@ -129,7 +139,10 @@ class UpdateBankStatementDetailLogic extends AbstractControllerLogic
$system = $systemReference != null ? SystemType::SYSTEM_NAMES[$systemReference] : '';
$this->createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference);
$statementTransactionOwner = $this->createBankStatementTransactionOwner($bankStatementTransaction, $statementTransactionOwnerType, $system, $owner_type, $owner_id, $owner_reference);
// this for edit a transaction already mapped
if ($request->has('editMapped')) $this->editAccountMapped($statementTransactionOwner);
return $this->response([]);
}
@@ -144,9 +157,26 @@ class UpdateBankStatementDetailLogic extends AbstractControllerLogic
'owner_reference' => $owner_reference,
];
$bankStatementTransaction->owners()->firstOrCreate($ownerData);
return $bankStatementTransaction->owners()->where('status','<>',ApprovalStatus::REJECTED)->firstOrCreate($ownerData);
}
private function editAccountMapped(StatementTransactionOwner $owner){
$this->updateOwnerStatus($owner, ApprovalStatus::APPROVED);
// update all other owners with the same system, owner type, and owner ID
$this->updateOtherOwners($owner, ApprovalStatus::REJECTED);
}
private function updateOwnerStatus(StatementTransactionOwner $owner, int $status)
{
$this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status);
}
private function updateOtherOwners(StatementTransactionOwner $owner, int $status): void
{
StatementTransactionOwner::where('statement_transaction_id', $owner->statement_transaction_id)
->where('id', '!=', $owner->id)
->update(['status' => $status]);
}
}
@@ -2,16 +2,14 @@
namespace App\Classes\Modules\Accounting\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransaction;
use App\Http\Resources\BankStatementTransactionResource;
use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use Illuminate\Http\JsonResponse;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Http\Resources\BankStatementTransactionOwnerResource;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use App\Classes\Modules\Accounting\Services\FetchesBankStatementTransactionOwner;
use App\Classes\Modules\Accounting\Services\UpdatesBankStatementTransactionOwnerStatus;
class UpdateStatementTransactionStatusLogic extends AbstractControllerLogic
{
@@ -27,29 +25,23 @@ class UpdateStatementTransactionStatusLogic extends AbstractControllerLogic
];
}
/** @var FetchesBankStatementTransaction */
private $fetchesBankStatementTransaction;
/** @var FetchesBankStatementTransactionOwner */
private $fetchesBankStatementTransactionOwner;
/** @var UpdatesBankStatementTransactionOwnerStatus */
private $updatesBankStatementTransactionOwnerStatus;
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/**
* UpdateAnnouncementLogic constructor.
* @param FetchesBankStatementTransaction $fetchesBankStatementTransaction
* @param FetchesBankStatementTransactionOwner $fetchesBankStatementTransactionOwner
* @param UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus
* @param UpdatesTransactionStatus $updatesTransactionStatus
*/
public function __construct(
FetchesBankStatementTransaction $fetchesBankStatementTransaction,
UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus,
UpdatesTransactionStatus $updatesTransactionStatus
FetchesBankStatementTransactionOwner $fetchesBankStatementTransactionOwner,
UpdatesBankStatementTransactionOwnerStatus $updatesBankStatementTransactionOwnerStatus
) {
$this->fetchesBankStatementTransaction = $fetchesBankStatementTransaction;
$this->fetchesBankStatementTransactionOwner = $fetchesBankStatementTransactionOwner;
$this->updatesBankStatementTransactionOwnerStatus = $updatesBankStatementTransactionOwnerStatus;
$this->updatesTransactionStatus = $updatesTransactionStatus;
}
/**
@@ -61,21 +53,10 @@ class UpdateStatementTransactionStatusLogic extends AbstractControllerLogic
*/
public function logic(Request $request): JsonResponse
{
$statementTrasaction = $this->fetchesBankStatementTransaction->execute(['id' => $request->route('id')]);
$statementTrasactionOwner = $statementTrasaction->owners->first();
$statementTrasactionOwner = $this->fetchesBankStatementTransactionOwner->execute(['id' => $request->route('id')]);
$this->updatesBankStatementTransactionOwnerStatus->execute($statementTrasactionOwner, $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED);
// todo-new: approve payments status, need to check the owner(if system is shipping, need to api with shipping portal)
// if ($request->route('status') == 'approve') {
// if ($statementTrasactionOwner->transaction->type === StatementTransactionOwnerType::SALES) {
// if ($statementTrasactionOwner->owner->status === ApprovalStatus::PENDING_VERIFICATION) {
// $this->updatesTransactionStatus->execute($statementTrasactionOwner->owner, ApprovalStatus::APPROVED);
// }
// }
// }
return $this->resourceResponse(new BankStatementTransactionResource($statementTrasaction));
return $this->resourceResponse(new BankStatementTransactionOwnerResource($statementTrasactionOwner));
}
}
@@ -23,15 +23,12 @@ class CreateBankStatementTransactionOwnersProcessor
/**
* @return void
*/
public function execute() {
$transactions = StatementTransaction::whereDoesntHave('owners', function($query){
return $query->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
})->orderBy('posting_date')->get();
public function execute($transactions) {
// $transactions = StatementTransaction::whereDoesntHave('owners')->where('amount', '<', 0)->get();
foreach ($transactions as $transaction) {
$mapped = false;
$keywords = array_filter(explode(" ", $transaction->transaction_description . " " . $transaction->transaction_description_2));
if($transaction->amount > 0){
@@ -40,7 +37,7 @@ class CreateBankStatementTransactionOwnersProcessor
$creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::PAYMENT, Booking::class, PaymentMethodType::WALLET, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], $keywords);
foreach ($creditTransactions as $creditTransaction) {
$isArray = is_array($creditTransaction);
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::SALES,
'system' => 'EXCHANGE',
'owner_type' => Transaction::class,
@@ -49,12 +46,11 @@ class CreateBankStatementTransactionOwnersProcessor
]);
}
// Shipping Portal Sales
$creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), 2, PaymentMethodType::WALLET);
$creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), [2], PaymentMethodType::WALLET);
foreach ($creditTransactions as $creditTransaction) {
if($creditTransaction['owner_type'] === Wallet::class) continue;
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::SALES,
'system' => 'SHIPPING_PORTAL',
'owner_type' => $creditTransaction['owner_type'],
@@ -67,7 +63,7 @@ class CreateBankStatementTransactionOwnersProcessor
$creditTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::TOP_UP, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], $keywords);
foreach ($creditTransactions as $creditTransaction) {
$isArray = is_array($creditTransaction);
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::WALLET_TOP_UP,
'system' => 'EXCHANGE',
'owner_type' => Transaction::class,
@@ -76,9 +72,9 @@ class CreateBankStatementTransactionOwnersProcessor
]);
}
$creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), 5, null);
$creditTransactions = $this->getTransactionsFromShippingPortal($transaction->amount, $this->getDateRange($transaction->posting_date, 1), [5,15], null);
foreach ($creditTransactions as $creditTransaction) {
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::WALLET_TOP_UP,
'system' => 'SHIPPING_PORTAL',
'owner_type' => $creditTransaction['owner_type'],
@@ -89,7 +85,7 @@ class CreateBankStatementTransactionOwnersProcessor
// fpx charge refund
if($transaction->transaction_description === 'DUITNOW S/CHRG REFUND'){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::FPX_CHARGE_REFUND
]);
}
@@ -98,7 +94,7 @@ class CreateBankStatementTransactionOwnersProcessor
// INTERNAL_BANK_TRANSFER_IN
if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE')){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_IN
]);
}
@@ -123,7 +119,7 @@ class CreateBankStatementTransactionOwnersProcessor
->where('amount', '<=', (($transaction->amount * -1) + 0.01))->whereDate('created_at', '>=', $paymentDateStart)->whereDate('created_at', '<=', $paymentDateEnd)->get();
foreach ($debitTransactions as $debitTransaction) {
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::SUPPLIER_PAYMENT,
'system' => 'EXCHANGE',
'owner_type' => Group::class,
@@ -139,7 +135,7 @@ class CreateBankStatementTransactionOwnersProcessor
$debitTransactions = $this->getTransactions($transaction->posting_date, $transaction->amount, TransactionType::DEBIT_NOTE, Wallet::class, null, [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED], $keywords);
foreach ($debitTransactions as $debitTransaction) {
$isArray = is_array($creditTransaction);
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::WALLET_WITHDRAWAL,
'system' => 'EXCHANGE',
'owner_type' => Transaction::class,
@@ -152,50 +148,52 @@ class CreateBankStatementTransactionOwnersProcessor
// STATUTORY
if(str_contains($transaction->transaction_description_2, 'PEMBANGUNAN SUMBER') || str_contains($transaction->transaction_description_2, 'HASIL') || str_contains($transaction->transaction_description_2, 'PERTUBUHAN KESELAMAT') || str_contains($transaction->transaction_description_2, 'KUMPULAN WANG SIMPAN')){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::STATUTORY
]);
}
// FPX_CHARGE
if($transaction->transaction_description === 'DR DUITNOW S/CHRG' || str_contains($transaction->transaction_description, 'Manual FPX') || str_contains($transaction->transaction_description, 'CMS - DR FPX CHG')){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::FPX_CHARGE
]);
}
// BANK_CHARGE
if($transaction->transaction_description === 'CMS - DR CORP CHG' || $transaction->transaction_description === 'MONTHLY PROFIT DEBIT'){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::BANK_CHARGE
]);
}
// CREDIT_CARD_PAYMENT
if(str_contains($transaction->transaction_description_2, 'VISA CARD')){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::CREDIT_CARD_PAYMENT
]);
}
// INTERNAL_BANK_TRANSFER_OUT
if(str_contains($transaction->transaction_description_2, 'CIEF WORLDWIDE') || str_contains($transaction->transaction_description_2, 'CIEF WORLWIDE') || str_contains($transaction->transaction_description_2, 'IZYIM GLOBAL')){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::INTERNAL_BANK_TRANSFER_OUT
]);
}
// non-operational charges
if(str_contains($transaction->transaction_description_2, 'HIRE PURCHASE') || str_contains($transaction->transaction_description_2, 'TENAGA NASIONAL') || str_contains($transaction->transaction_description, 'CABLE CHARGE') || str_contains($transaction->transaction_description_2, 'CTOS DATA SYSTEMS') || str_contains($transaction->transaction_description_2, 'MAXIS')){
$transaction->owners()->firstOrCreate([
$data = $transaction->owners()->firstOrCreate([
'type' => StatementTransactionOwnerType::NON_OPERATIONAL
]);
}
}
if (isset($data) && $data->wasRecentlyCreated) $mapped = true;
$this->updateMappedRate($transaction, $mapped);
}
}
}
private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $keywords, $model = Transaction::class) {
private function getTransactions($date, $amount, $type, $ownerType, $paymentMethod, $statuses, $keywords, $model = Transaction::class) {
$dateRange = $this->getDateRange($date, 4);
if (App::environment(['production'])) {
$query = $model::whereIn('status', $statuses)
@@ -322,7 +320,7 @@ class CreateBankStatementTransactionOwnersProcessor
try{
$client = new \GuzzleHttp\Client(['verify' => false]);
$response = $client->request('GET', $url.'?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2]'.$paymentMethodFilter.',"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).',"type_in":['.$type.']}');
$response = $client->request('GET', $url.'?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters={"order_by":{"column":"id","DESC":true},"status_in":[2]'.$paymentMethodFilter.',"created_after":"'.$dateRange['start_date'].'","created_before":"'.$dateRange['end_date'].'","amount_exceed":'.($amount - 0.01).',"amount_short":'.($amount + 0.01).',"type_in":'.json_encode($type).'}');
$body = $response->getBody();
$data = json_decode($body, true);
$payload = $data['payload'];
@@ -349,4 +347,11 @@ class CreateBankStatementTransactionOwnersProcessor
'end_date' => $nextDay,
];
}
private function updateMappedRate($transaction, $mapped) {
$statement = $transaction->statement;
$statement->total_rows = StatementTransaction::where('account_statement_id',$transaction->account_statement_id)->count();
$statement->mapped_rows = $mapped ? $statement->mapped_rows+1 : $statement->mapped_rows;
$statement->save();
}
}
@@ -13,6 +13,7 @@ class ListShippingPortalTransactions
{
try {
$url = 'https://izyim.cief-malaysia.com/public/api/v1/transactions/mappable/query/with-details';
// $url = 'http://127.0.0.1:8001/public/api/v1/transactions/mappable/query/with-details';
$client = new \GuzzleHttp\Client(['verify' => false]);
$response = $client->request('GET', $url . '?api-key=510acd13d8d24375cf038ad626c282565451461a9c2399357e0b65365300787e&filters=' . json_encode($filters));
$body = $response->getBody();
@@ -0,0 +1,31 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Models\StatementTransactionOwner;
use Illuminate\Database\Eloquent\Builder;
use App\Classes\General\Eloquent\AbstractFetchRecord;
class FetchesBankStatementTransactionOwner extends AbstractFetchRecord
{
/** @var StatementTransactionOwner */
private $repository;
/**
* FetchesBankStatementDetails constructor.
* @param StatementTransactionOwner $repository
*/
public function __construct(StatementTransactionOwner $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Classes\Modules\Accounting\Services;
use App\Models\TransactionMappingLog;
use Illuminate\Database\Eloquent\Builder;
use App\Classes\General\Eloquent\AbstractListRecord;
class ListTransactionMappingLogs extends AbstractListRecord
{
/** @var TransactionMappingLog */
private $repository;
/**
* ListsBankStatementDetails constructor.
* @param TransactionMappingLog $repository
*/
public function __construct(TransactionMappingLog $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\Classes\Modules\Bookings\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ExpireBookingPaymentControllerLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Expire Payment',
'message' => 'You have successfully expire the Payment'
];
}
/** @var FetchesBooking */
private $fetchesBooking;
/**
* DeletePurchaseOrderPdfLogic constructor.
* @param FetchesBooking $fetchesBooking
*/
public function __construct(fetchesBooking $fetchesBooking)
{
$this->fetchesBooking = $fetchesBooking;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request) : JsonResponse
{
$booking = $this->fetchesBooking->execute(['id' => $request->route('id')]);
$payment = $booking->transactions()
->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->first();
$payment->status = ApprovalStatus::EXPIRED;
$payment->save();
return $this->response([]);
}
}
@@ -9,13 +9,16 @@ use App\Classes\Modules\Bookings\Services\UpdatesBookingStatus;
use App\Classes\Modules\Transactions\Services\DeletesTransaction;
use App\Classes\Modules\Documents\Services\DeletesDocument;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionWithInvoiceNoProcessor;
use Illuminate\Support\Str;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Http\Resources\BookingResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Transaction;
use Illuminate\Support\Carbon;
class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
{
@@ -23,7 +26,8 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
/**
* @return array
*/
protected function notification():array {
protected function notification(): array
{
return [
'title' => 'Regenerate Booking Invoice',
'message' => 'You have successfully regenerate booking invoice'
@@ -48,6 +52,9 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
/** @var CreateInvoiceTransactionProcessor */
private $createInvoiceTransactionProcessor;
/** @var CreateInvoiceTransactionWithInvoiceNoProcessor */
private $createInvoiceTransactionWithInvoiceNoProcessor;
/**
* FetchBookingLogic constructor.
* @param CanFetchBooking $canFetchBooking
@@ -56,6 +63,7 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
* @param UpdatesBookingStatus $updatesBookingStatus
* @param DeletesDocument $deletesDocument
* @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
* @param CreateInvoiceTransactionWithInvoiceNoProcessor $createInvoiceTransactionWithInvoiceNoProcessor
*/
public function __construct(
CanFetchBooking $canFetchBooking,
@@ -63,15 +71,16 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
DeletesTransaction $deletesTransaction,
UpdatesBookingStatus $updatesBookingStatus,
DeletesDocument $deletesDocument,
CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
)
{
CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor,
CreateInvoiceTransactionWithInvoiceNoProcessor $createInvoiceTransactionWithInvoiceNoProcessor
) {
$this->canFetchBooking = $canFetchBooking;
$this->fetchesBooking = $fetchesBooking;
$this->deletesTransaction = $deletesTransaction;
$this->updatesBookingStatus = $updatesBookingStatus;
$this->deletesDocument = $deletesDocument;
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
$this->createInvoiceTransactionWithInvoiceNoProcessor = $createInvoiceTransactionWithInvoiceNoProcessor;
}
@@ -82,18 +91,45 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request) : JsonResponse
public function logic(Request $request): JsonResponse
{
$this->canFetchBooking->passes();
$booking = $this->fetchesBooking->execute([
'id' => $request->route('id'),
'status' => ApprovalStatus::COMPLETED,
'with_transactions' => true]
$booking = $this->fetchesBooking->execute(
[
'id' => $request->route('id'),
'status' => ApprovalStatus::COMPLETED,
'with_transactions' => true
]
);
$this->updatesBookingStatus->execute($booking, ApprovalStatus::APPROVED);
$firstInvoice = $booking->transactions()
->whereIn('type', [TransactionType::INVOICE])
->withTrashed()
->orderBy('created_at', 'asc')
->first();
// get the first bill_no
$firstBillNo = $firstInvoice->bill_no;
if (strpos($firstBillNo, '-deleted') !== false) {
$firstBillNo = substr($firstBillNo, 0, strpos($firstBillNo, '-deleted'));
}
// update currentInvoice bill_no to '-deleted-'
$currentInvoice = $booking->transactions()->where('type', TransactionType::INVOICE)->first();
$currentInvoice->bill_no = $currentInvoice->bill_no ."-deleted-" . (string)(Carbon::now()->timestamp);
$currentInvoice->save();
$transactionWithSameBillNo = Transaction::where('bill_no', $firstBillNo)->withTrashed()->get();
if ($transactionWithSameBillNo) {
foreach ($transactionWithSameBillNo as $transaction) {
$transaction->bill_no = $transaction->bill_no . "-deleted-" . Str::random(10);
$transaction->save();
}
}
$transaction = $booking->transactions()->whereIn('type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->get();
foreach ($transaction as $key => $row) {
$this->deletesTransaction->execute($row);
@@ -104,9 +140,8 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
$this->deletesDocument->execute($row);
}
$this->createInvoiceTransactionProcessor->execute($booking);
$this->createInvoiceTransactionWithInvoiceNoProcessor->execute($booking, $firstBillNo);
return $this->resourceResponse(new BookingResource($booking));
}
}
@@ -0,0 +1,94 @@
<?php
namespace App\Classes\Modules\Companies\ControllersLogic;
use App\Classes\ValueObjects\Constants\TransactionType;
use ZipArchive;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Classes\Exceptions\MalformedRequestException;
use Illuminate\Support\Facades\File;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Company;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class BulkDownloadCustomerInvoicesLogic
{
/**
* @param Request $request
* @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function execute(Request $request)
{
try {
$company = Company::where('reference', $request->input('marking'))->first();
if (!$company) {
return response()->json([
'status' => 'Failed',
'message' => 'Customer not found.',
]);
}
$startDate = Carbon::createFromFormat('d-m-Y', $request->input('startDate'))->startOfDay();
$endDate = Carbon::createFromFormat('d-m-Y', $request->input('endDate'))->endOfDay();
$invoicebookings = $company->bookings()
->whereDate('created_at', '>=', $startDate)
->whereDate('created_at', '<=', $endDate)
->whereHas('transactions', function ($query) {
$query->where('transactions.type', TransactionType::INVOICE)
->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
})
->orderBy('created_at')
->get();
if ($invoicebookings->isEmpty()) {
return response()->json([
'status' => 'Failed',
'message' => 'No invoices found for this customer in the given date range.',
]);
}
$zipDirectory = storage_path('app/bulk_invoice'); // Update this with the actual directory path
if (!file_exists($zipDirectory)) {
mkdir($zipDirectory, 0755, true);
}
$zip_file = "{$zipDirectory}/invoices_{$request->input('startDate')}_to_{$request->input('endDate')}_{$company->reference}.zip";
$zip = new ZipArchive();
if ($zip->open($zip_file, ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
foreach ($invoicebookings as $booking) {
$invoice_file = $booking->documents()->where('document_type', DocumentType::INVOICE)->whereNull('deleted_at')->first()->files()->first();
$zip->addFile(Storage::disk('documents')->path($invoice_file->file->file_info->original->file), 'invoice-' . $booking->created_at->format('d_m_Y') . '_' . $booking->marking . '.pdf');
}
$zip->close();
while (ob_get_level()) {
ob_end_clean();
}
return response()->download($zip_file);
} else {
return response()->json([
'status' => 'Error',
'message' => 'Failed to create the Zip archive.',
]);
}
} catch (\Exception $e) {
// Log the exception for debugging
Log::error('Error in BulkDownloadCustomerInvoicesLogic: ' . $e->getMessage());
return response()->json([
'status' => 'Error',
'message' => 'An error occurred while processing the request.',
]);
}
}
}
@@ -0,0 +1,78 @@
<?php
namespace App\Classes\Modules\Companies\ControllersLogic;
use ZipArchive;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Company;
use App\Models\Group;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class BulkDownloadSupplierWhiteFormsLogic
{
/**
* @param Request $request
* @return void
* @throws MalformedRequestException
*/
public function execute(Request $request)
{
try {
$startDate = Carbon::createFromFormat('d-m-Y', $request->input('startDate'))->startOfDay();
$endDate = Carbon::createFromFormat('d-m-Y', $request->input('endDate'))->endOfDay();
$companyReference = Company::find($request->input('supplier'))->reference;
$groups = Group::where('issuer', $request->input('supplier'))
->whereDate('created_at', '>=', $startDate)
->whereDate('created_at', '<=', $endDate)
->orderBy('created_at', 'DESC')
->get();
if (count($groups) > 0) {
$zipDirectory = storage_path('app/bulk_whiteform'); // Update this with the actual directory path
if (!file_exists($zipDirectory)) {
mkdir($zipDirectory, 0755, true);
}
$zip_file = "{$zipDirectory}/currency_vendor_orders_{$request->input('startDate')}_to_{$request->input('endDate')}_{$companyReference}.zip";
$zip = new ZipArchive();
if ($zip->open($zip_file, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE)) {
foreach($groups as $group) {
$document = $group->documents()->where('document_type', DocumentType::CURRENCY_VENDOR_ORDER)->whereNull('deleted_at')->first()->files()->first();
$created_at = $group->created_at->format('Y-m-d');
$zip->addFile(Storage::disk('documents')->path($document->file->file_info->original->file), $created_at . "_" . $group->amount . "_" . $group->reference . '.pdf');
}
$zip->close();
while (ob_get_level()) {
ob_end_clean();
}
return response()->download($zip_file);
}
}
return response()->json([
'status' => 'Failed',
'message' => 'No white form found for this supplier in the given date range.',
]);
} catch (\Exception $e) {
// Log the exception for debugging
Log::error('Error in BulkDownloadSupplierWhiteFormsLogic: ' . $e->getMessage());
return response()->json([
'status' => 'Error',
'message' => 'An error occurred while processing the request.',
]);
}
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Classes\Modules\Exports\Services;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use App\Models\TransactionMappingLog;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class ExportsImportedInvoiceMappeds implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
{
use Exportable;
private $dateTime;
private $count;
private $counter = 1;
public function __construct(Request $request)
{
$this->dateTime = $request->input('date').' '.$request->input('time');
$this->count = 0;
}
public function headings(): array
{
return [
'No',
'Doc No',
'Date',
'Debtor Code',
'Debtor Name',
'Shipping Info',
'Net Total',
'Cancelled',
'Mapped Status',
'Mapped Reference No',
'MapPayment Received Date'
];
}
/**
* @return \Illuminate\Support\Collection|mixed
*/
public function query()
{
return TransactionMappingLog::where('imported_date', $this->dateTime);
}
/**
* @param Transaction $transaction
*
* @return array
*/
public function map($transaction): array
{
$this->count += 1;
$data = $transaction->data;
return [
$this->count,
Arr::get($data,'doc_no'),
Arr::get($data,'date'),
Arr::get($data,'debtor_code'),
Arr::get($data,'debtor_name'),
Arr::get($data,'shipping_info'),
Arr::get($data,'net_total'),
Arr::get($data,'cancelled'),
Arr::get($data,'mapped_status'),
Arr::get($data,'mapped_result_reference'),
Arr::get($data,'payment_received_date'),
];
}
}
@@ -0,0 +1,86 @@
<?php
namespace App\Classes\Modules\Exports\Services;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use App\Models\TransactionMappingLog;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class ExportsImportedReceiptMappeds implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
{
use Exportable;
private $dateTime;
private $count;
private $counter = 1;
public function __construct(Request $request)
{
$this->dateTime = $request->input('date').' '.$request->input('time');
$this->count = 0;
}
public function headings(): array
{
return [
'Check',
'Doc No',
'Doc Date',
'Debtor Code',
'Company Name',
'Description',
'Payment Amount',
'Created User',
'Curr.',
'To Home Rate',
'Local Payment Amount',
'Cancelled',
'Mapped Status',
'Mapped Reference No',
];
}
/**
* @return \Illuminate\Support\Collection|mixed
*/
public function query()
{
return TransactionMappingLog::where('imported_date', $this->dateTime);
}
/**
* @param Transaction $transaction
*
* @return array
*/
public function map($transaction): array
{
$this->count += 1;
$data = $transaction->data;
return [
$this->count,
Arr::get($data,'doc_no'),
Arr::get($data,'doc_date'),
Arr::get($data,'debtor_code'),
Arr::get($data,'company_name'),
Arr::get($data,'description'),
Arr::get($data,'payment_amount'),
Arr::get($data,'created_user'),
Arr::get($data,'curr'),
Arr::get($data,'to_home_rate'),
Arr::get($data,'local_payment_amount'),
Arr::get($data,'cancelled'),
Arr::get($data,'2nd_doc_no'),
Arr::get($data,'mapped_status'),
Arr::get($data,'mapped_result_reference'),
];
}
}
@@ -4,7 +4,6 @@ namespace App\Classes\Modules\Exports\Services;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\StatementTransactionOwnerType;
use App\Models\StatementTransactionOwner;
use App\Models\Transaction;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
@@ -18,6 +17,8 @@ use App\Classes\Modules\Accounting\Processors\ListShippingPortalTransactions;
use App\Classes\ValueObjects\Constants\ShippingTransactionType;
use App\Classes\ValueObjects\Constants\TransactionType;
use Illuminate\Support\Facades\Log;
use App\Classes\General\Eloquent\ApplyFiltersToQuery;
use App\Models\StatementTransaction;
class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
{
@@ -33,7 +34,7 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading
public function headings(): array
{
return [
$header = [
'DocNo',
'DocDate',
'DebtorCode',
@@ -49,6 +50,7 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading
'AccNo',
'DeptNo'
];
return $header;
}
/**
@@ -56,9 +58,10 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading
*/
public function query()
{
return StatementTransactionOwner::whereNull('invoice_reference')
->whereIn('type', [StatementTransactionOwnerType::SALES, StatementTransactionOwnerType::WALLET_TOP_UP])
->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED]);
$data = (new ApplyFiltersToQuery())->execute(StatementTransaction::query(), json_decode($this->request->input('filter'), true));
if ($this->request->has('bankStatementTransactionId')) $data = $data->whereIn('id',json_decode($this->request->input('bankStatementTransactionId'), true));
return $data;
}
/**
@@ -68,11 +71,12 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading
*/
public function map($transaction): array
{
$statementTransactionOwner = $transaction->owners()->whereIn('status', [ApprovalStatus::APPROVED])->first();
$logArray = [
'counter' => $this->counter,
'system' => $transaction->system,
'StatementTransactionOwner_id' => $transaction->id,
'transaction_table_id' => $transaction->owner_id,
'system' => $statementTransactionOwner->system,
'StatementTransactionOwner_id' => $statementTransactionOwner->id,
'transaction_table_id' => $statementTransactionOwner->owner_id,
];
$this->counter += 1;
$logArray = json_encode($logArray);
@@ -82,8 +86,8 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading
$textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' ' . $logArray . PHP_EOL;
file_put_contents($filePath, $textToAppend, FILE_APPEND);
if ($transaction->system == 'EXCHANGE') {
$row = (App()->make($transaction->owner_type))->where('id', $transaction->owner_id)->first();
if ($statementTransactionOwner->system == 'EXCHANGE') {
$row = (App()->make($statementTransactionOwner->owner_type))->where('id', $statementTransactionOwner->owner_id)->first();
$company = $row->type === TransactionType::PAYMENT ? $row->owner->company : $row->owner->owner;
$booking = $row->owner;
@@ -104,46 +108,61 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading
'500-0000',
'CIEF'
];
} else {
} elseif ($statementTransactionOwner->owner_id) {
$row = (App()->make(ListShippingPortalTransactions::class))->execute([
'id' => $transaction->owner_id,
'id' => $statementTransactionOwner->owner_id,
'with_company' => true,
]);
if (empty($row) || $row[0]['status'] != 'success') {
if (!empty($row) && $row[0]['status'] != 'success') {
$textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' Fetch Shipping Transaction Fail ' . json_encode([
'id' => $transaction->owner_id,
'id' => $statementTransactionOwner->owner_id,
'with_company' => true,
'StatementTransactionOwner_id' => $transaction->id,
'StatementTransactionOwner_id' => $statementTransactionOwner->id,
]) . PHP_EOL;
file_put_contents($errorFilePath, $textToAppend, FILE_APPEND);
$textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' Shipping Portal Respnose ' . json_encode($row) . PHP_EOL;
file_put_contents($errorFilePath, $textToAppend, FILE_APPEND);
Log::info('Error in Exports Invoice Transactions ' . $this->counter);
return [
'<<New>>',
Carbon::parse($row['created_at'])->format('m/d/Y H:m'),
$row['debtor_code'],
$row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['marking'],
'',
'MYR',
$row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['bill_no'],
$row['type'] === ShippingTransactionType::PAYMENT ? '' : 'W1',
$row['type'] === ShippingTransactionType::PAYMENT ? 'PLEASE REFER TO THE ATTACHED APPENDIX REF `' . $row['order_reference'] : 'CREDIT SALES',
'',
1,
round($row['amount'], 2),
'500-0000',
'CIEF'
];
return [];
}
$row = $row[0];
return [
'<<New>>',
Carbon::parse($row['updated_at'])->format('m/d/Y H:m'),
$row['debtor_code'],
$row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['marking'],
'',
'MYR',
$row['type'] === ShippingTransactionType::PAYMENT ? $row['order_reference'] : $row['bill_no'],
$row['type'] === ShippingTransactionType::PAYMENT ? '' : 'W1',
$row['type'] === ShippingTransactionType::PAYMENT ? 'PLEASE REFER TO THE ATTACHED APPENDIX REF `' . $row['order_reference'] : 'CREDIT SALES',
'',
1,
round($row['amount'], 2),
'500-0000',
'CIEF'
];
}
return [
'Transaction Not Found',
$transaction->posting_date->format('m/d/Y H:m'),
$transaction->transaction_description.' - '.$transaction->transaction_description_2,
$statementTransactionOwner->system,
'',
'',
'',
'',
'',
'',
0,
$transaction->amount,
'',
'',
'',
''
];
}
}
@@ -0,0 +1,199 @@
<?php
namespace App\Classes\Modules\Exports\Services;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Illuminate\Http\Request;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use App\Classes\General\Eloquent\ApplyFiltersToQuery;
use App\Models\StatementTransaction;
use App\Models\Company;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Concerns\WithCustomStartCell;
use Maatwebsite\Excel\Events\AfterSheet;
class ExportsReceiptTransactions implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize, WithEvents, WithCustomStartCell
{
use Exportable;
private $request;
private $counter = 1;
public function __construct(Request $request)
{
$this->request = $request;
}
public function startCell(): string
{
return 'A2';
}
public function registerEvents(): array {
return [
AfterSheet::class => function(AfterSheet $event) {
$sheet = $event->sheet;
$sheet->mergeCells('A1:A1');
$sheet->setCellValue('A1', '"');
$sheet->mergeCells('M1:Y1');
$sheet->setCellValue('M1', "Payment Detail Column");
$sheet->mergeCells('Z1:AB1');
$sheet->setCellValue('Z1', "Knock Off Detail");
$styleArray = [
'alignment' => [
'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
],
];
$cellRange = 'A1:AB1';
$event->sheet->getDelegate()->getStyle($cellRange)->applyFromArray($styleArray);
},
];
}
public function headings(): array
{
$header = [
[
' ',
'(20 chars)',
'(Date: dd/MM/yyyy)',
'(12 chars)',
'(40 chars)',
'(25 chars)',
'(10 chars)',
'(10 chars)',
'(5 chars)',
'(Number, use System Currency Rate Decimal)',
'(Number, use System Currency Rate Decimal)',
'(Rich Text)',
'(20 chars)',
'(20 chars)',
'(Number, use System Currency Decimal)',
'(Number, use System Currency Decimal)',
'(Number, use System Currency Rate Decimal)',
'(14 chars)',
'(30 chars)',
'(10 chars)',
'(10 chars)',
'(20 chars)',
'(Integer)',
'(Boolean. Indicate T for stock control or F for non stock control)',
'(Returned Cheque Date: dd/MM/yyyy)',
'(2 chars, RI for Invoice, RD for D/N)',
'',
'(Number, use System Currency Decimal)',
],
[
'DocNo',
'DocDate',
'DebtorCode',
'Description',
'DocNo2',
'ProjNo',
'DeptNo',
'CurrencyCode',
'ToHomeRate',
'ToDebtorRate',
'Note',
'PaymentMethod',
'ChequeNo',
'PaymentAmt',
'BankCharge',
'ToBankRate',
'BankChargeTaxType',
'BankChargeTaxRefNo',
'BankChargeProjNo',
'BankChargeDeptNo',
'PaymentBy',
'FloatDay',
'IsRCHQ',
'RCHQDate',
'KnockOffDocType',
'KnockOffDocNo',
'KnockOffAmt',
'',
]
];
return $header;
}
/**
* @return \Illuminate\Support\Collection|mixed
*/
public function query()
{
$data = (new ApplyFiltersToQuery())->execute(StatementTransaction::query(), json_decode($this->request->input('filter'), true));
if ($this->request->has('bankStatementTransactionId')) $data = $data->whereIn('id',json_decode($this->request->input('bankStatementTransactionId'), true));
return $data;
}
/**
* @param StatementTransaction $transaction
*
* @return array
*/
public function map($transaction): array
{
$statementTransactionOwner = $transaction->owners()->whereIn('status', [ApprovalStatus::APPROVED])->first();
$logArray = [
'counter' => $this->counter,
'system' => $statementTransactionOwner->system,
'StatementTransactionOwner_id' => $statementTransactionOwner->id,
'transaction_table_id' => $statementTransactionOwner->owner_id,
];
$this->counter += 1;
$logArray = json_encode($logArray);
$filePath = storage_path('logs/exports_receipt_transactions.log');
$errorFilePath = storage_path('logs/exports_receipt_transactions_error.log');
$textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' ' . $logArray . PHP_EOL;
file_put_contents($filePath, $textToAppend, FILE_APPEND);
$company = Company::where('name',$transaction->transaction_description_2)->first();
return [
'<<New>>',
Carbon::parse($transaction->posting_date)->format('d/m/Y'),
($company ? $company->debtor : null),
'Payment for '.$transaction->transaction_description,
'',
'',
'',
'MYR',
1,
1,
'',
'MBB',
'',
$transaction->amount,
'',
1,
'',
'',
'',
'',
'',
'0',
'',
'',
'RI',
$transaction->transaction_ref,
$transaction->amount,
'',
];
}
}
@@ -0,0 +1,111 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
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\TransactionResource;
use App\Models\Booking;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Update Purchase Order',
'message' => 'You have successfully updated you booking\'s purchase order'
];
}
/** @var FetchesBooking */
private $fetchesBooking;
/** @var GeneratesTransactionBillNumber */
private $generatesTransactionBillNumber;
/** @var CreatePurchaseOrderTransactionProcessor */
private $createPurchaseOrderTransactionProcessor;
/**
* CreatePurchaseOrderTransactionLogic constructor.
* @param FetchesBooking $fetchesBooking
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
*/
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor)
{
$this->fetchesBooking = $fetchesBooking;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
}
/**
* @param Request $request
* @param string $id
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request, $id = '') : JsonResponse
{
$files = $request->file('files');
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
foreach ($object->getFiles() as $file){
$collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true);
$sheet = $collection->first()->skip(1);
$products = $sheet->map(function ($row) {
Log::info($row);
$stockCode = $row[0];
$description = $row[1];
$quantity = $row[2];
$unit_price = $row[3];
return [
'stockCode' => $stockCode,
'description' => $description,
'quantity' => $quantity,
'unit_price' => $unit_price
];
})->all();
}
// dd($products);
/** @var Booking $booking */
$booking = $this->fetchesBooking->execute(['id' => $request->route('id') ?? $id]);
$billNumber = $this->generatesTransactionBillNumber->execute('PO-');
$total = collect($products)->sum(function($product){
return $product['quantity'] * floatval(str_replace(',', '', $product['unit_price']));
});
$object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1,
1, PaymentMethodType::CASH,
$total, $total, $booking->fix_currency_id, $booking->fix_currency_id,
1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $products);
$transaction = $this->createPurchaseOrderTransactionProcessor->execute($booking, $object);
return $this->resourceResponse(new TransactionResource($transaction));
}
}
@@ -44,7 +44,19 @@ class ListWalletTransactionsLogic extends AbstractControllerLogic
$query = $this->listsTransactions->execute($this->listsTransactions->deserializeFilters($request->input('filters')));
if (str_contains($request->input('filters'), "owner_id") && $query->count() > 0) {
$currentWalletBalance = Wallet::find($query->first()->owner_id)->amount;
$wallet_total_incoming = Transaction::where('owner_type', $query->first()->owner_type)
->where('owner_id', $query->first()->owner_id)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereIn('type', [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE])
->sum('amount');
$wallet_total_outgoing = Transaction::where('owner_type', $query->first()->owner_type)
->where('owner_id', $query->first()->owner_id)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereIn('type', [TransactionType::PAYMENT, TransactionType::DEBIT_NOTE])
->sum('amount');
$currentWalletBalance = $wallet_total_incoming - $wallet_total_outgoing;
$incoming = Transaction::where('owner_type', $query->first()->owner_type)
->where('owner_id', $query->first()->owner_id)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
@@ -21,7 +21,7 @@ use App\Classes\ValueObjects\Constants\DocumentType;
use App\Models\Booking;
use App\Models\SegmentConstant;
class CreateInvoiceTransactionProcessorWithInvoiceNo
class CreateInvoiceTransactionWithInvoiceNoProcessor
{
/** @var CreatesTransaction */
@@ -35,7 +35,7 @@ class GeneratesTransactionBillNumber
$attempt = 0;
while ($attempt < 10) { // Retry up to 10 times
// $billNumber = $prefix . $date->format('Y') . $date->format('m') . '-' . intval(microtime(true));
$billNumber = $prefix . $date->format('Y') . $date->format('m') . '-' . mt_rand(1000000000, 9999999999);
$billNumber = $prefix . $date->format('Y') . $date->format('m') . '-' . mt_rand(1000000, 9999999);
if (!$this->checksIfTransactionBillNumberExists->execute($billNumber)) {
return $billNumber;
}
@@ -8,9 +8,21 @@ final class SystemType {
public const SHIPPING_PORTAL = 'SHIPPING_PORTAL';
public const CNTR = 'CNTR';
public const LITE = 'LITE';
public const PROBASHI = 'PROBASHI';
public const PETS = 'PETS';
public const SYSTEM_NAMES = [
'exchange' => self::EXCHANGE,
'shipping_portal' => self::SHIPPING_PORTAL,
'izyim' => self::SHIPPING_PORTAL,
'lite' => self::LITE,
'cntr' => self::CNTR,
'probashi' => self::PROBASHI,
'pets' => self::PETS
];
}
@@ -0,0 +1,44 @@
<?php
namespace App\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
class DeleteBulkInvoiceFiles extends Command
{
protected $signature = 'delete:bulk-download-files';
protected $description = 'Delete all the bulk download files';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$directories = [
storage_path('app/bulk_invoice'),
storage_path('app/bulk_whiteform'),
];
foreach ($directories as $directory) {
$start = new Carbon();
$this->info(Carbon::now() . ' Start cleaning - ' . $directory);
if (File::isDirectory($directory)) {
File::cleanDirectory($directory);
$this->info('All files have been deleted.');
} else {
$this->info('Directory does not exist.');
}
$end = new Carbon();
$elapsedTime = $start->diff($end)->format('%H:%I:%S');
$this->info(Carbon::now() . ' Process ended. ElapsedTime: ' . $elapsedTime);
}
}
}
+4 -4
View File
@@ -38,10 +38,10 @@ class Kernel extends ConsoleKernel
->dailyAt('01:00')
->appendOutputTo(storage_path().'/logs/soft-delete-seasonal-segmant-company.log')
->withoutOverlapping();
$schedule->command('regenerateInvoice')
->everyMinute()
->appendOutputTo(storage_path().'/logs/regenerateInvoice.log')
$schedule->command('delete:bulk-download-files')
->hourly()
->appendOutputTo(storage_path().'/logs/delete-bulk-download-files.log')
->withoutOverlapping();
}
@@ -22,6 +22,7 @@ use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use DateTime;
use App\Models\StatementTransaction;
class BankStatementController extends Controller
{
@@ -88,7 +89,9 @@ class BankStatementController extends Controller
public function rerun()
{
CreateBankStatementTransactionOwners::dispatch();
foreach (StatementTransaction::doesntMapStatement()->get()->chunk(30) as $key => $transaction) {
CreateBankStatementTransactionOwners::dispatch($transaction);
}
return redirect()->back()->with('success', 'Rerun triggered successfully');
}
@@ -129,7 +132,7 @@ class BankStatementController extends Controller
->download($statement->date_from->format('Y-m-d') . '_' . $statement->date_to->format('Y-m-d') . '_statement.csv');
}
public function fetch(Request $request, ListBankStatementDetailsLogic $logic): JsonResponse
public function fetch(Request $request, ListBankStatementTransactionsLogic $logic): JsonResponse
{
return $logic->execute($request);
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Accounting;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Classes\Modules\Accounting\ControllersLogic\HistoryImportedTransactionMappedControllerLogic;
class HistoryImportedTransactionMappedController
{
/**
* @param Request $request
* @param ApprovePaymentLogic $logic
* @return JsonResponse
*/
public function getImported(Request $request, HistoryImportedTransactionMappedControllerLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Bookings;
use App\Classes\Modules\Bookings\ControllersLogic\ExpireBookingPaymentControllerLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ExpireBookingPaymentController
{
/**
* @param Request $request
* @param DeleteBookingPaymentControllerLogic $logic
* @return JsonResponse
*/
public function expire(Request $request, ExpireBookingPaymentControllerLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Companies;
use App\Classes\Modules\Companies\ControllersLogic\BulkDownloadCustomerInvoicesLogic;
use Illuminate\Http\Request;
class BulkDownloadCustomerInvoicesController
{
/**
* @param Request $request
* @param BulkDownloadCustomerInvoicesLogic $logic
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function download(Request $request, BulkDownloadCustomerInvoicesLogic $logic) {
return $logic->execute($request);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Companies;
use App\Classes\Modules\Companies\ControllersLogic\BulkDownloadSupplierWhiteFormsLogic;
use Illuminate\Http\Request;
class BulkDownloadSupplierWhiteFormsController
{
/**
* @param Request $request
* @param BulkDownloadSupplierWhiteFormsLogic $logic
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function download(Request $request, BulkDownloadSupplierWhiteFormsLogic $logic) {
return $logic->execute($request);
}
}
@@ -15,6 +15,10 @@ use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Maatwebsite\Excel\Excel;
use App\Classes\Modules\Exports\Services\ExportsImportedInvoiceMappeds;
use App\Models\TransactionMappingLog;
use App\Classes\Modules\Exports\Services\ExportsReceiptTransactions;
use App\Classes\Modules\Exports\Services\ExportsImportedReceiptMappeds;
class ExportCustomersToExcelController
{
@@ -64,6 +68,13 @@ class ExportCustomersToExcelController
return $response;
}
public function receiptTransactions(Request $request){
$exportsTransactions = new ExportsReceiptTransactions($request);
$response = $exportsTransactions->download('receipt-transactions.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
ob_end_clean();
return $response;
}
public function bookingTransactions(ExportsBookingTransactions $exportsBookingTransactions, Request $request){
$response = $exportsBookingTransactions->download('bookingTransactions.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
ob_end_clean();
@@ -75,4 +86,16 @@ class ExportCustomersToExcelController
ob_end_clean();
return $response;
}
public function importedInvoiceMapped(ExportsImportedInvoiceMappeds $exportsImportedInvoiceMappeds, Request $request) {
$response = $exportsImportedInvoiceMappeds->download($request->input('fileName').'.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
ob_end_clean();
return $response;
}
public function importedReceiptMapped(ExportsImportedReceiptMappeds $exportsImportedReceiptMappeds, Request $request) {
$response = $exportsImportedReceiptMappeds->download($request->input('fileName').'.xls', Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
ob_end_clean();
return $response;
}
}
@@ -2,94 +2,194 @@
namespace App\Http\Controllers\Imports;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Imports\Services\GenericImport;
use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\Segment;
use App\Models\User;
use Carbon\Carbon;
use DateTime;
use Carbon\Carbon;
use App\Models\User;
use App\Models\Company;
use App\Models\Segment;
use App\Models\Transaction;
use Illuminate\Http\Request;
use App\Models\SeasonalSegment;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use App\Models\TransactionMappingLog;
use App\Classes\ValueObjects\Constants\HttpStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Imports\Services\GenericImport;
use App\Classes\ValueObjects\Response\ApiResponseObject;
use App\Classes\Modules\Accounting\Processors\ChecksBillNumber;
use App\Classes\Modules\Segments\Services\CreatesSeasonalSegment;
use App\Classes\Modules\Companies\Processors\AssignSegmentProcessor;
use App\Models\Company;
use App\Models\SeasonalSegment;
use App\Models\Transaction;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject;
class ImportStatementInvoiceController
{
private $responseTitle;
private $responseMessage;
public function __construct() {
$this->responseTitle = 'Import Invoice Mapping';
$this->responseMessage = 'You have successfully imported invoice mapping';
}
public function mapping($row) {
// Shipping Info
// TOPUP -> map with transaction.bill_no
if (str_starts_with($row['shipping_info'], 'TOPUP')) {
// find in exchange first, if cannont then find in izyim
foreach (['exchange','izyim'] as $system) {
[$returnReference, $transactionDate] = $this->mappingTopUp($row, $system);
if ($returnReference) {
// $row['mapped_result_reference'] = $data['owner_reference'];
// $row['payment_received_date'] = date('Y-m-d', strtotime($data['created_at']));
$row['mapped_result_reference'] = $returnReference;
$row['payment_received_date'] = $transactionDate ? date('d-m-Y', strtotime($transactionDate)) : null;
$row['mapped_status'] = 'success';
return $row;
}
}
}
[$returnReference, $transactionDate] = $this->mappingExchange($row);
if ($returnReference) {
$row['mapped_result_reference'] = $returnReference;
$row['payment_received_date'] = date('d-m-Y', strtotime($transactionDate));
$row['mapped_status'] = 'success';
return $row;
}
// if still unable to map, will try to check the shipping_info without TOPUP
// foreach (['exchange','izyim'] as $system) {
// $returnReference = $this->mappingTopUp($row, $system);
// if ($returnReference) {
// $row['mapped_result_reference'] = $returnReference;
// $row['mapped_status'] = 'success';
// }
// }
return $row;
}
/**
* @param Request $request
* @return array
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function import(Request $request)
public function import(Request $request) : JsonResponse
{
ini_set('memory_limit', '-1');
try {
ini_set('memory_limit', '-1');
$importDate = date('Y-m-d H:i:s');
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
$file = json_decode($object->getFiles()[0])->file_info->original->file;
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
$file = json_decode($object->getFiles()[0])->file_info->original->file;
$import = new GenericImport();
Excel::import($import, $file);
$excelRows = $import->rows;
$excelRows = $excelRows->toArray();
$import = new GenericImport();
Excel::import($import, $file);
$excelRows = $import->rows;
$excelRows = $excelRows->toArray();
$data = [];
foreach ($excelRows as $row) {
$row['mapped_result_reference'] = null;
$row['payment_received_date'] = null;
$row['mapped_status'] = 'failed';
$row['date'] = in_array(gettype($row['date']), ['integer', 'double']) ? $this->changeExcelDate($row['date']) : date('Y-m-d', strtotime($row['date']));
$row = $this->mapping($row);
TransactionMappingLog::create([
'imported_date'=>$importDate,
'type' => 'invoices',
'data'=>$row,
]);
array_push($data, $row);
// if 5 digits -> exchange booking reference
// find transation
// find statement_transaction_owners, and fill up the details
// if <5 digits, find the transaction id (order number in izyim), find the payment in izyim
// find transation
// find statement_transaction_owners, and fill up the details
// dd([
// 'type' => $statementTransactionOwnerType,
// 'system' => $system,
// // 'owner_type' => Transaction::class,
// // todo-new: make sure owner_type is a class
// 'owner_type' => $owner_type,
// 'owner_id' => $owner_id,
// 'owner_reference' => $owner_reference
// ]);
// $bankStatementTransaction->owners()->firstOrCreate([
// 'type' => $statementTransactionOwnerType,
// 'system' => $system,
// // 'owner_type' => Transaction::class,
// // todo-new: make sure owner_type is a class
// 'owner_type' => $owner_type,
// 'owner_id' => $owner_id,
// 'owner_reference' => $owner_reference
// ]);
foreach ($excelRows as $row) {
dd($row);
// $row['debtor_code']
// attempt 1 - try map by amount and date
// $transactionDate = $this->changeExcelDate($row['date']);
// $transaction = Transaction::where('original_amount', $row['total'])->whereDate('created_at', $transactionDate)->get();
// if ($transaction) {
// // check company
// // $company = Company::where('debtor', $row['debtor_code'])->first();
// // dd($company);
// // try to verify is it the correct transaction
// }
// Shipping Info
// TOPUP -> map with transaction.bill_no
if (str_starts_with($row['shipping_info'], 'TOPUP')) {
// find in exchange first, if cannont then find in izyim
// (App()->make(ChecksBillNumber::class))->execute($bill_no, 'exchange');
}
// if 5 digits -> exchange booking reference
// find transation
// find statement_transaction_owners, and fill up the details
// if <5 digits, find the transaction id (order number in izyim), find the payment in izyim
// find transation
// find statement_transaction_owners, and fill up the details
// dd([
// 'type' => $statementTransactionOwnerType,
// 'system' => $system,
// // 'owner_type' => Transaction::class,
// // todo-new: make sure owner_type is a class
// 'owner_type' => $owner_type,
// 'owner_id' => $owner_id,
// 'owner_reference' => $owner_reference
// ]);
// $bankStatementTransaction->owners()->firstOrCreate([
// 'type' => $statementTransactionOwnerType,
// 'system' => $system,
// // 'owner_type' => Transaction::class,
// // todo-new: make sure owner_type is a class
// 'owner_type' => $owner_type,
// 'owner_id' => $owner_id,
// 'owner_reference' => $owner_reference
// ]);
return $this->response($this->responseTitle, $this->responseMessage, HttpStatus::OK_WITH_MESSAGE, ['data'=>$data,'importedDate'=>$importDate]);
} catch (\Exception $exception){
return $this->response('import invoice failed',$exception->getMessage(), ($exception->getCode()? $exception->getCode() : HttpStatus::SERVER_ERROR));
}
}
public function response(String $responseTitle, String $responseMessage, int $httpStatus, ?array $data = []) : JsonResponse {
return (new ApiResponseObject($responseTitle, $responseMessage, $httpStatus, $data))->handler();
}
private function mappingTopUp(Array $row, String $system) {
try {
if ($data = (App()->make(ChecksBillNumber::class))->execute($row['shipping_info'], $system)) {
// if ($system == 'izyim' && isset($data['owner_reference'])) return $data;
if ($system == 'izyim' && isset($data['owner_reference'])) return [$data['owner_reference'], null];
if ($system == 'exchange') return $this->updateTransactionOwnerReference($data, $row['doc_no']);
}
} catch (\Throwable $th) {
return false;
}
}
private function mappingExchange(Array $row) {
$date = $row['date'];
$transaction = Transaction::getReceiverWithJoinStatementTransactionAndOwner($row)->select('transactions.*')->where('owner_reference', $row['shipping_info'])->first();
if ($transaction && $transaction->count() == 0) {
$transaction = Transaction::getReceiverWithJoinStatementTransactionAndOwner($row)->select('transactions.*')->where('statement_transactions.amount', $row['net_total'])->whereRaw("DATE(posting_date) = '$date'")->first();
}
if ($transaction && $transaction->count() == 0) {
$transaction = Transaction::getReceiverWithJoinStatementTransactionAndOwner($row)->select('transactions.*')->where(DB::raw('FLOOR(statement_transactions.amount)'), floor($row['net_total']))->whereRaw("DATE(posting_date) = '$date'")->first();
}
if ($transaction && $transaction->count() > 0) {
return $this->updateTransactionOwnerReference($transaction, $row['doc_no']);
}
return [false, false];
}
public function updateTransactionOwnerReference($transaction, String $docNo) {
$transactionOwner = $transaction->transaction_owner;
if ($transactionOwner) {
$transactionOwner->update([
'invoice_reference'=>$docNo,
'status'=>ApprovalStatus::COMPLETED
]);
return [$transactionOwner->owner_reference, $transaction->created_at];
}
return [false, false];
}
public function changeExcelDate($date)
{
$unixTime = (($date - 25569) * 86400);
@@ -2,24 +2,40 @@
namespace App\Http\Controllers\Imports;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Imports\Services\GenericImport;
use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\Segment;
use App\Models\User;
use Carbon\Carbon;
use DateTime;
use Carbon\Carbon;
use App\Models\User;
use App\Models\Company;
use App\Models\Segment;
use App\Models\Transaction;
use Illuminate\Http\Request;
use App\Models\SeasonalSegment;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
use App\Models\TransactionMappingLog;
use App\Models\StatementTransactionOwner;
use App\Classes\ValueObjects\Constants\HttpStatus;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Imports\Services\GenericImport;
use App\Classes\ValueObjects\Response\ApiResponseObject;
use App\Classes\Modules\Segments\Services\CreatesSeasonalSegment;
use App\Classes\Modules\Companies\Processors\AssignSegmentProcessor;
use App\Models\Company;
use App\Models\SeasonalSegment;
use App\Models\Transaction;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Segments\DataTransferObjects\SeasonalSegmentObject;
class ImportStatementReceiptsController
{
private $responseTitle;
private $responseMessage;
private $removeStr = 'Payment for ';
public function __construct() {
$this->responseTitle = 'Import Receipt Mapping';
$this->responseMessage = 'You have successfully imported receipt mapping';
}
/**
* @param Request $request
* @return array
@@ -27,20 +43,74 @@ class ImportStatementReceiptsController
*/
public function import(Request $request)
{
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
$file = json_decode($object->getFiles()[0])->file_info->original->file;
try {
$importDate = date('Y-m-d H:i:s');
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
$file = json_decode($object->getFiles()[0])->file_info->original->file;
$import = new GenericImport();
Excel::import($import, $file);
$excelRows = $import->rows;
$excelRows = $excelRows->toArray();
$import = new GenericImport();
Excel::import($import, $file);
$excelRows = $import->rows;
$excelRows = $excelRows->toArray();
foreach ($excelRows as $row) {
// if has date column
// $transactionDate = $this->changeExcelDate($row['date']);
$data = [];
foreach ($excelRows as $row) {
$row['mapped_result_reference'] = null;
$row['mapped_status'] = 'failed';
$row['date'] = in_array(gettype($row['doc_date']), ['integer', 'double']) ? $this->changeExcelDate($row['doc_date']) : date('Y-m-d', strtotime($row['doc_date']));
if ($invRefer = $this->getInvoiceReference($row['description'])) {
$returnReference = $this->mappingExchange($invRefer, $row['doc_no']);
if ($returnReference) {
$row['mapped_result_reference'] = $returnReference;
$row['mapped_status'] = 'success';
}
}
TransactionMappingLog::create([
'imported_date'=>$importDate,
'type' => 'receipts',
'data'=>$row,
]);
array_push($data, $row);
}
return $this->response($this->responseTitle, $this->responseMessage, HttpStatus::OK_WITH_MESSAGE, ['data'=>$data,'importedDate'=>$importDate]);
} catch (\Exception $exception){
return $this->response('import invoice failed',$exception->getMessage(), ($exception->getCode()? $exception->getCode() : HttpStatus::SERVER_ERROR));
}
}
private function getInvoiceReference($invRefer) {
$arrStr = explode($this->removeStr, $invRefer);
if (isset($arrStr[1])) return $arrStr[1];
return null;
}
public function response(String $responseTitle, String $responseMessage, int $httpStatus, ?array $data = []) : JsonResponse {
return (new ApiResponseObject($responseTitle, $responseMessage, $httpStatus, $data))->handler();
}
private function mappingExchange($invRefer, $docNo) {
$transactionOwner = StatementTransactionOwner::where('invoice_reference',$invRefer)->whereNull('receipt_reference')->where('status', ApprovalStatus::COMPLETED)->first();
if ($transactionOwner) {
return $this->updateTransactionOwnerReference($transactionOwner, $docNo);
}
return false;
}
public function updateTransactionOwnerReference($transactionOwner, String $docNo) {
if ($transactionOwner) {
$transactionOwner->update([
'receipt_reference'=>$docNo,
'status'=>ApprovalStatus::COMPLETED
]);
return $transactionOwner->owner_reference;
}
return false;
}
public function changeExcelDate($date)
{
$unixTime = (($date - 25569) * 86400);
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Transactions;
use App\Classes\Modules\Transactions\ControllersLogic\ImportPurchaseOrderTransactionLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ImportPurchaseOrderTransactionController
{
/**
* @param Request $request
* @param ImportPurchaseOrderTransactionLogic $logic
* @return JsonResponse
*/
public function import(Request $request, ImportPurchaseOrderTransactionLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -21,6 +21,10 @@ class BankStatementDetailResource extends JsonResource
'id' => $this->id,
'date' => Carbon::parse($transaction->posting_date)->format('Y-m-d'),
'transaction_description_1' => $transaction->transaction_description,
'transaction_description_2' => $transaction->transaction_description_2,
'transaction_description_3' => $transaction->transaction_description_3,
'transaction_description_4' => $transaction->transaction_description_4,
'transaction_description_5' => $transaction->transaction_description_5,
'pay_for' => $transaction->transaction_description_2,
'system_references' => $this->system,
'amount' => $transaction->amount,
@@ -28,7 +28,7 @@ class BankStatementTransactionOwnerResource extends JsonResource
}
if($this->type === StatementTransactionOwnerType::WALLET_TOP_UP){
$referenceLink = route('booking.details', $this->owner_reference);
$referenceLink = route('wallet.details', $this->owner_reference);
}
}
}
@@ -34,7 +34,8 @@ class BankStatementTransactionResource extends JsonResource
'owners' => [
'approved' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::APPROVED])->get()),
'pending_verification' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION])->get()),
'rejected' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::REJECTED])->get())
'rejected' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::REJECTED])->get()),
'completed' => BankStatementTransactionOwnerResource::collection($this->owners()->whereIn('status', [ApprovalStatus::COMPLETED])->get()),
]
];
}
@@ -16,6 +16,7 @@ class TransactionDetailResource extends JsonResource
{
return [
'id' => $this->id,
'stockCode' => $this->product_code,
'description' => $this->product_name,
'quantity' => $this->quantity,
@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class TransactionMappingLogResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'imported_date' => $this->imported_date,
'type' => $this->type
];
}
}
+2
View File
@@ -16,6 +16,8 @@ class AccountStatement extends Model
'total_amount',
'begin_balance',
'end_balance',
'total_rows',
'mapped_rows',
];
protected $casts = [
+19 -7
View File
@@ -109,7 +109,7 @@ class Company extends AbstractModel implements Documentable
{
return $this->hasManyDeep(Transaction::class, [Booking::class], ['company_id', 'owner_id'], ['id', 'id']);
}
/**
* @return HasMany
*/
@@ -130,13 +130,25 @@ class Company extends AbstractModel implements Documentable
* @return Builder
*/
public function services(): Builder {
return ServiceType::where('status', ApprovalStatus::APPROVED)->whereHas('constants', function($query) {
$query->Where(function($query){
$query->where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->is_active', true);
})->orWhere(function($query) {
$query->where('reference', SegmentConstants::CUSTOM_SERVICE_TYPE)->where('detail->is_active', true)->whereIn('segment_id', $this->segments->pluck('id'));
if ($this->business_type === 3) {
return ServiceType::where('status', ApprovalStatus::APPROVED)->whereHas('constants', function($query) {
$query->where(function($query) {
$query->where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->is_active', true);
})->orWhere(function($query) {
$query->where('reference', SegmentConstants::CUSTOM_SERVICE_TYPE)->where('detail->is_active', true);
});
});
});
} else {
// Existing logic for other company types
return ServiceType::where('status', ApprovalStatus::APPROVED)->whereHas('constants', function($query) {
$query->Where(function($query){
$query->where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->is_active', true);
})->orWhere(function($query) {
$query->where('reference', SegmentConstants::CUSTOM_SERVICE_TYPE)->where('detail->is_active', true)->whereIn('segment_id', $this->segments->pluck('id'));
});
});
}
}
public function servicesConfigurations(): Collection {
+13
View File
@@ -5,6 +5,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
class StatementTransaction extends Model
{
@@ -47,4 +48,16 @@ class StatementTransaction extends Model
{
return $this->hasMany(StatementTransactionOwner::class);
}
public function owner_status()
{
return $this->owners()->whereIn('status',[ApprovalStatus::APPROVED,ApprovalStatus::COMPLETED,ApprovalStatus::PENDING_VERIFICATION]);
}
public function scopeDoesntMapStatement($query)
{
$query->whereDoesntHave('owners', function($query){
return $query->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
})->orderBy('posting_date');
}
}
+12
View File
@@ -5,6 +5,7 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class StatementTransactionOwner extends Model
{
@@ -21,9 +22,20 @@ class StatementTransactionOwner extends Model
'receipt_reference',
'status',
];
public function owner(): morphTo
{
return $this->morphTo();
}
public function transaction(): BelongsTo
{
return $this->belongsTo(StatementTransaction::class, 'statement_transaction_id', 'id');
}
public function scopeGetSiblingsOwner($query) {
$query->where('system', $this->system)
->where('owner_type', $this->owner_type)
->where('owner_id', $this->owner_id);
}
}
+35
View File
@@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Staudenmeir\EloquentHasManyDeep\HasTableAlias;
use App\Models\StatementTransactionOwner;
class Transaction extends AbstractModel implements Documentable, Transactionable, Voucherifiable
@@ -53,6 +54,14 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
return $this->MorphOne(Transaction::class, 'owner')->where('type', TransactionType::CREDIT_NOTE);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function transaction_owner()
{
return $this->MorphOne(StatementTransactionOwner::class, 'owner','owner_type','owner_id');
}
/**
* @return BelongsTo
*/
@@ -69,6 +78,16 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
return $this->BelongsTo( Company::class, 'issuer', 'id');
}
/**
* Get the user that owns the Transaction
*
* @return BelongsTo
*/
public function receiverCompany(): BelongsTo
{
return $this->belongsTo(Company::class, 'receiver', 'id');
}
/**
* @return BelongsTo
*/
@@ -202,6 +221,22 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
return $query->whereIn('status', [ApprovalStatus::APPROVED]);
}
/**
* @param Builder $query
* @return Builder
*/
public function scopeGetReceiverWithJoinStatementTransactionAndOwner(Builder $query, Array $row) {
$query->join('companies',function($q) use ($row) {
$q->on('companies.id','=','transactions.receiver');
$q->where('debtor',$row['debtor_code']);
})
->join('statement_transaction_owners', function ($q) {
$q->on('statement_transaction_owners.owner_id','=','transactions.id');
$q->where('statement_transaction_owners.owner_type','=',Transaction::class);
})
->join('statement_transactions','statement_transactions.id','=','statement_transaction_owners.statement_transaction_id');
}
/**
* @return MorphMany
*/
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class TransactionMappingLog extends Model
{
protected $fillable = ['imported_by','data','imported_date','type'];
protected $casts = [
'data' => 'array',
];
public static function boot() {
parent::boot();
static::creating(function ($model) {
$model->imported_by = auth()->user()->id;
});
}
}
+4
View File
@@ -104,6 +104,10 @@ return [
'path' => storage_path('logs/regenerateInvoice.log'),
'level' => 'info',
],
'guzzleShippingPortal' => [
'driver' => 'errorlog',
'level' => 'debug',
],
],
];
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTransactionMappingLogsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('transaction_mapping_logs', function (Blueprint $table) {
$table->id();
$table->bigInteger('imported_by')->unsigned();
$table->foreign('imported_by')->references('id')->on('users');
$table->dateTime('imported_date')->nullable();
$table->text('data')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('transaction_mapping_logs');
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddMappedRateToAccountStatementsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('account_statements', function (Blueprint $table) {
$table->integer('total_rows')->unsigned()->default(0)->after('end_balance');
$table->integer('mapped_rows')->unsigned()->default(0)->after('end_balance');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('account_statements', function (Blueprint $table) {
$table->dropColumn('total_rows');
$table->dropColumn('mapped_rows');
});
}
}
@@ -0,0 +1,40 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddTypeToTransactionMappingLogsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('transaction_mapping_logs', function (Blueprint $table) {
$table->string('type',50)->default('invoices')->after('imported_date');
});
foreach (DB::table('transaction_mapping_logs')->get() as $key => $value) {
$data = json_decode($value->data);
DB::table('transaction_mapping_logs')->where('id',$value->id)->update([
'type' => (isset($data->description) ? 'receipts' : 'invoices')
]);
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('transaction_mapping_logs', function (Blueprint $table) {
$table->dropColumn('type');
});
}
}
Binary file not shown.
@@ -77,6 +77,13 @@
import { required } from "vuelidate/lib/validators";
export default {
props:{
editMapped: {
type: Boolean,
default: false,
required: false
}
},
data(){
return {
error: '',
@@ -116,6 +123,8 @@
system_references : this.system_references ? this.system_references.toLowerCase() : '',
transaction_reference : this.transaction_reference
};
if (this.editMapped) this.parameters['editMapped'] = true;
this.submit(this.route('api.accounting.statement.details.update', this.data.id), 'put', this.section, true, true);
},
@@ -0,0 +1,39 @@
<template>
<div class="row m-b-10 parentContainer">
<div class="col">
<div class="row p-b-10 b-b b-grey">
<div class="col-2">{{ item.imported_date }}</div>
<div class="col-2">
<button class="btn btn-xs btn-outline-success b-rad-none m-r-5" @click="downloadInvoiceMapped(item.imported_date)">
Download Invoices Mapped
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
stage: {
type: Number,
default: 0
},
section:{
type: String,
required: true
},
},
methods: {
downloadInvoiceMapped(importedDate) {
var arrDateTime = importedDate.split(" ");
const fileName = 'InvoiceMapped';
window.open(this.route('importedInvoiceMapped.export')+'?date='+arrDateTime[0]+'&time='+arrDateTime[1]+'&fileName='+fileName, '_blank');
},
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,39 @@
<template>
<div class="row m-b-10 parentContainer">
<div class="col">
<div class="row p-b-10 b-b b-grey">
<div class="col-2">{{ item.imported_date }}</div>
<div class="col-2">
<button class="btn btn-xs btn-outline-success b-rad-none m-r-5" @click="downloadInvoiceMapped(item.imported_date)">
Download Receipts Mapped
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
stage: {
type: Number,
default: 0
},
section:{
type: String,
required: true
},
},
methods: {
downloadInvoiceMapped(importedDate) {
var arrDateTime = importedDate.split(" ");
const fileName = 'ReceiptMapped';
window.open(this.route('importedReceiptMapped.export')+'?date='+arrDateTime[0]+'&time='+arrDateTime[1]+'&fileName='+fileName, '_blank');
},
},
mixins: [componentHandler]
}
</script>
@@ -4,6 +4,8 @@
<div class="row p-b-10 b-b b-grey">
<div class="col-2">{{ item.posting_date }}</div>
<div class="col-2">{{ item.transaction_description_1 + ' - ' + item.transaction_description_2 }}</div>
<!-- Mapping Approval tab -->
<div class="col-5" v-if="item.owners.pending_verification.length === 1">
<div class="row">
<div class="col">{{item.owners.pending_verification[0].system}}</div>
@@ -11,6 +13,47 @@
<div class="col"><a :href="item.owners.pending_verification[0].reference_link" target="_blank">{{item.owners.pending_verification[0].reference}}</a></div>
</div>
</div>
<!-- Pending Export tab -->
<div class="col-5" v-if="item.owners.approved.length === 1">
<div class="row">
<div class="col">{{item.owners.approved[0].system}}</div>
<div class="col">{{ typeString(item.owners.approved[0].type) }}</div>
<div class="col">
<div class="col d-flex justify-content-between">
<a :href="item.owners.approved[0].reference_link" target="_blank">{{item.owners.approved[0].reference}}</a>
<div v-if="stage === 4">
<!-- revert button -->
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="approveCorrectMappingTransaction">
<i class="fa fa-times fa-fw"></i>
</button>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="approveCorrectMappingTransaction">
<general-confirmation-form-component
:contentText="returnTextRevert(item.owners.approved[0].reference)"
modalType="confirm"
class="text-center"
:apiRoute="route('api.accounting.bankStatement.details.status.update', item.owners.approved[0].id, 'pending_verification')"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
<!-- edit button -->
<button class="btn btn-xs btn-outline-success b-rad-none m-r-5 requestModal" data-type="updateOwner">
<i class="fa fa-edit"></i>
</button>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="updateOwner">
<edit-single-item-in-list-component :data="item" :section="section" editMapped="true"></edit-single-item-in-list-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
<!-- Mapping Review tab -->
<div class="col-5" v-if="item.owners.pending_verification.length > 1">
<div class="row">
<div class="col">
@@ -31,7 +74,7 @@
<div class="row parentContainer" v-for="owner in item.owners.pending_verification">
<div class="col d-flex justify-content-between">
<a :href="owner.reference_link" target="_blank">{{ owner.reference }}</a>
<div v-if="stage === 2">
<div v-if="stage === 2" style="display: flex;">
<button class="btn btn-xs btn-outline-primary b-rad-none m-r-5 requestModal" data-type="approveCorrectMappingTransaction">
<i class="fa fa-check fa-fw"></i>
</button>
@@ -46,13 +89,30 @@
>
</general-confirmation-form-component>
</modal-component>
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="revertPendingMappingTransaction">
<i class="fa fa-times fa-fw"></i>
</button>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="revertPendingMappingTransaction">
<general-confirmation-form-component
contentText="Are you sure you want to reject this mapping?"
modalType="delete"
class="text-center"
:apiRoute="route('api.accounting.statement_transaction.owner.status.update', owner.id, 'reject')"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-5" v-if="!item.owners.pending_verification.length">
<!-- unknow tab -->
<div class="col-5" v-if="!item.owners.pending_verification.length && !item.owners.approved.length">
<div class="row">
<div class="col">
<button class="btn btn-xs btn-outline-success b-rad-none m-r-5 requestModal" data-type="updateOwner">
@@ -81,7 +141,13 @@
</div>
<div class="col-1">{{ item.amount }}</div>
<div class="col-1 text-success" v-if="item.owners.approved.length">{{ [6, 7, 8, 9, 10, 11, 12, 13, 14].include(item.owners.approved[0].type) ? 'Miscellaneous' : 'Approved' }}</div>
<div class="col-1 text-success" v-if="item.owners.approved.length">{{ [6, 7, 8, 9, 10, 11, 12, 13, 14].includes(item.owners.approved[0].type) ? 'Miscellaneous' : 'Approved' }}</div>
<!-- Pending Export tab for checkbox here -->
<div class="col-1" v-if="item.owners.approved.length === 1">
<input type="checkbox" class="request_export_item" v-model="selectAll" :value="item.id">
</div>
<div class="col-1 text-danger" v-if="!item.owners.approved.length">Pending...</div>
<div class="col-1" v-if="stage === 1">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="deleteMappingTransaction">
@@ -92,7 +158,7 @@
contentText="Are you sure you want to reject this mapping?"
modalType="delete"
class="text-center"
:apiRoute="route('api.accounting.statement_transaction.owner.status.update', item.id, 'reject')"
:apiRoute="route('api.accounting.statement_transaction.owner.status.update', item.owners.pending_verification[0].id, 'reject')"
apiMethod="post"
:section="section"
>
@@ -117,6 +183,9 @@
section:{
type: String,
required: true
},
selectAll: {
type:Boolean
}
},
methods: {
@@ -154,6 +223,9 @@
},
returnTextWithVariable(variable) {
return "Are you sure you want to choose this mapping " + variable + "?";
},
returnTextRevert(variable) {
return "Are you sure you want to revert this mapping " + variable + "?";
}
},
mixins: [componentHandler]
@@ -0,0 +1,72 @@
<template>
<div class="row m-b-10 parentContainer">
<div class="col">
<div class="row p-b-10 b-b b-grey">
<div class="col-2">{{ item.posting_date }}</div>
<div class="col-2">{{ item.transaction_description_1 + ' - ' + item.transaction_description_2 }}</div>
<div class="col-7">
<div class="row">
<div class="col">{{item.owners.completed[0].system}}</div>
<div class="col">{{ typeString(item.owners.completed[0].type) }}</div>
<div class="col"><a :href="item.owners.completed[0].reference_link" target="_blank">{{item.owners.completed[0].reference}}</a></div>
<div class="col">{{item.owners.completed[0].invoice_reference}}</div>
<div class="col">{{item.owners.completed[0].receipt_reference}}</div>
</div>
</div>
<div class="col-1">{{ item.amount }}</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
stage: {
type: Number,
default: 0
},
section:{
type: String,
required: true
},
},
methods: {
typeString(type){
switch(type) {
case 1:
return 'Sales';
case 2:
return 'Wallet Top Up';
case 3:
return 'Wallet Withdrawal';
case 4:
return 'Payment Refund';
case 5:
return 'Supplier Payment';
case 6:
return 'Internal Bank Transfer Out';
case 7:
return 'Internal Bank Transfer In';
case 8:
return 'Salary Payment';
case 9:
return 'Statutory Payment';
case 10:
return 'FPX Charges';
case 11:
return 'FPX Charges Refund';
case 12:
return 'Bank Charges';
case 13:
return 'Credit Card Payment';
case 14:
return 'Non-Operational Payment';
}
},
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,134 @@
<template>
<div class="row h-100 parentContainer">
<div class="col-12" style="min-height: 20px;">
<loading-component style="height: 20px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
<h3>{{ componentTitle }}</h3>
<div class="row m-b-10 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="text-right">
<button class="btn btn-xs btn-outline-success b-rad-none m-r-5" @click="downloadInvoiceMapped">
Download Invoices Mapped
</button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive p-0">
<table class="table table-hover">
<thead>
<tr>
<th v-for="item in tableHeaders">{{ item }}</th>
</tr>
</thead>
<tbody v-show="!isLoading">
<tr v-for="(item, index) in $store.getters.getListData(section)">
<td>{{index+1}}</td>
<td>{{item.doc_no }}</td>
<td>{{item.date}}</td>
<td>{{item.debtor_code }}}</td>
<td>{{item.debtor_name}}</td>
<td>{{item.shipping_info}}</td>
<td>{{item.net_total}}</td>
<td>{{item.cancelled}}</td>
<td>{{item.mapped_status}}</td>
<td>{{item.mapped_result_reference}}</td>
<td>{{item.payment_received_date}}</td>
</tr>
</tbody>
</table>
</div>
<!-- /.card-body -->
<div class="card-footer">
</div>
</div>
</div>
<div class="col-12">
<pagination-component :section="section" class="mb-5" ref="pagination"></pagination-component>
</div>
</div>
</template>
<script>
export default {
props: {
files: {
required: true
},
type: {
type: String,
required: true,
},
section:{
type: String,
required: true
},
},
data() {
return {
isLoading: false,
error: '',
importedDate: null,
}
},
computed: {
pendingQueue() {
return this.$store.getters.isInCompleteQueue(this.section);
}
},
watch: {
pendingQueue(inComplete, oldValue){
if(inComplete){
this.importInvoice();
}
},
},
created(){
this.appendComponentTitle();
this.appendComponentTableHeader();
this.$store.dispatch('updateListQueue', {'name': this.section});
},
methods: {
appendComponentTitle() {
this.componentTitle = 'Imported Invoices Mapped';
},
appendComponentTableHeader() {
this.tableHeaders = ['No','Doc No','Date','Debtor Code','Debtor Name','Shipping Info','Net Total','Cancelled','Mapped Status','Mapped Reference No','Payment Received Date'];
},
importInvoice(){
this.isLoading = true;
this.parameters = {
files: this.files
};
this.submit(this.route('api.import_invoices.upload'), 'post', this.section, true, false);
},
successHandler(response){
this.$store.dispatch('completeList', {'name': this.section, 'data': response.payload.data});
this.importedDate = response.payload.importedDate;
this.isLoading = false;
},
errorHandler(error){
this.isLoading = false;
this.error = error.message;
},
downloadInvoiceMapped() {
var arrDateTime = this.importedDate.split(" ");
const fileName = 'importInvoiceMapping';
window.open(this.route('importedInvoiceMapped.export')+'?date='+arrDateTime[0]+'&time='+arrDateTime[1]+'&fileName='+fileName, '_blank');
},
}
}
</script>
@@ -0,0 +1,138 @@
<template>
<div class="row h-100 parentContainer">
<div class="col-12" style="min-height: 20px;">
<loading-component style="height: 20px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
</div>
<div class="col-12">
<div class="card">
<div class="card-header">
<h3>{{ componentTitle }}</h3>
<div class="row m-b-10 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="text-right">
<button class="btn btn-xs btn-outline-success b-rad-none m-r-5" @click="downloadInvoiceMapped">
Download Receipts Mapped
</button>
</div>
</div>
<!-- /.card-header -->
<div class="card-body table-responsive p-0">
<table class="table table-hover">
<thead>
<tr>
<th v-for="item in tableHeaders">{{ item }}</th>
</tr>
</thead>
<tbody v-show="!isLoading">
<tr v-for="(item, index) in $store.getters.getListData(section)">
<td>{{index+1}}</td>
<td>{{item.check }}</td>
<td>{{item.doc_no}}</td>
<td>{{item.doc_date }}}</td>
<td>{{item.debtor_code}}</td>
<td>{{item.company_name}}</td>
<td>{{item.description}}</td>
<td>{{item.payment_amount}}</td>
<td>{{item.created_user}}</td>
<td>{{item.curr}}</td>
<td>{{item.to_home_rate}}</td>
<td>{{item.local_payment_amount}}</td>
<td>{{item.cancelled}}</td>
<td>{{item.mapped_status}}</td>
<td>{{item.mapped_result_reference}}</td>
</tr>
</tbody>
</table>
</div>
<!-- /.card-body -->
<div class="card-footer">
</div>
</div>
</div>
<div class="col-12">
<pagination-component :section="section" class="mb-5" ref="pagination"></pagination-component>
</div>
</div>
</template>
<script>
export default {
props: {
files: {
required: true
},
type: {
type: String,
required: true,
},
section:{
type: String,
required: true
},
},
data() {
return {
isLoading: false,
error: '',
importedDate: null,
}
},
computed: {
pendingQueue() {
return this.$store.getters.isInCompleteQueue(this.section);
}
},
watch: {
pendingQueue(inComplete, oldValue){
if(inComplete){
this.importInvoice();
}
},
},
created(){
this.appendComponentTitle();
this.appendComponentTableHeader();
this.$store.dispatch('updateListQueue', {'name': this.section});
},
methods: {
appendComponentTitle() {
this.componentTitle = 'Imported Receipts Mapped';
},
appendComponentTableHeader() {
this.tableHeaders = ['Check','Doc No','Doc Date','Debtor Code','Company Name','Description','Payment Amount','Created User','Curr.','To Home Rate','Local Payment Amount','Cancelled','Mapped Status','Mapped Reference No'];
},
importInvoice(){
this.isLoading = true;
this.parameters = {
files: this.files
};
this.submit(this.route('api.import_receipts.upload'), 'post', this.section, true, false);
},
successHandler(response){
this.$store.dispatch('completeList', {'name': this.section, 'data': response.payload.data});
this.importedDate = response.payload.importedDate;
this.isLoading = false;
},
errorHandler(error){
this.isLoading = false;
this.error = error.message;
},
downloadInvoiceMapped() {
var arrDateTime = this.importedDate.split(" ");
const fileName = 'ReceiptMapped';
window.open(this.route('importedReceiptMapped.export')+'?date='+arrDateTime[0]+'&time='+arrDateTime[1]+'&fileName='+fileName, '_blank');
},
}
}
</script>
@@ -0,0 +1,275 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div class="justify-content-center align-items-center m-t-50 m-b-50" v-show="step === 0">
<div class="row">
<div class="col-3">
<div class="row text-center">
<div class="col b-a b-grey padding-20 m-r-15 pointer bg-complete text-white" @click="getReportFilter()">Mapped Report</div>
</div>
</div>
<div class="col-3">
<div class="row text-center">
<div class="col b-a b-grey padding-20 m-r-15 pointer bg-complete text-white" @click="getHistoryImportedInvReport()">History Imported Invoices Report</div>
</div>
</div>
<div class="col-3">
<div class="row text-center">
<div class="col b-a b-grey padding-20 m-r-15 pointer bg-complete text-white" @click="getHistoryImportedRecReport()">History Imported Receipts Report</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" v-if="step > 0 && report == 'mappedRecords'">
<div class="col">
<div class="row">
<div class="col">
<div class="justify-content-center align-items-center m-t-50 m-b-50">
<div class="row mb-3">
<div class="col-4">
<validation-wrapper-component :validator="$v.parameters.owner_reference">
<label class="text-primary">Reference</label>
<input type="text" class="form-control" v-model="parameters.owner_reference">
</validation-wrapper-component>
</div>
<div class="col-4">
<validation-wrapper-component :validator="$v.parameters.invoice_reference">
<label class="text-primary">Invoice Reference</label>
<input type="text" class="form-control" v-model="parameters.invoice_reference">
</validation-wrapper-component>
</div>
<div class="col-4">
<validation-wrapper-component :validator="$v.parameters.receipt_reference">
<label class="text-primary">Receipt Reference</label>
<input type="text" class="form-control" v-model="parameters.receipt_reference">
</validation-wrapper-component>
</div>
</div>
<div class="row">
<div class="col-3">
<validation-wrapper-component :validator="$v.parameters.startDate">
<label class="all-caps">Start Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.startDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-3">
<validation-wrapper-component :validator="$v.parameters.endDate">
<label class="all-caps">End Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.endDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-3">
<div class="row text-center">
<div class="col b-a b-grey padding-20 m-r-15 pointer bg-complete text-white" @click="getReportFilter()">Mapped Report</div>
</div>
</div>
</div>
</div>
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="row p-t-10 p-b-10 b-b b-grey text-master-light">
<div class="col-2">Date</div>
<div class="col-2">Description</div>
<div class="col-7">
<div class="row">
<div class="col">System</div>
<div class="col">Type</div>
<div class="col">Reference</div>
<div class="col">Invoice Reference</div>
<div class="col">Receipt Reference</div>
</div>
</div>
<div class="col-1">Amount</div>
</div>
</div>
</div>
<list-component :key="step" ref="TransactionsMappedList" section="TransactionsMappedSection" :endpoint="route('api.accounting.bank.transaction')" :options="filter">
<template slot="list" slot-scope="{data}">
<statement-transaction-mapped-component :data="data" :section="section"></statement-transaction-mapped-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
<!-- report for imported Invoices -->
<div class="row" v-if="step > 0 && report == 'importedInv'">
<div class="col">
<div class="row">
<div class="col">
<div class="justify-content-center align-items-center m-t-50 m-b-50">
<div class="row">
<div class="col-3">
<validation-wrapper-component :validator="$v.parameters.startDate">
<label class="all-caps">Start Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.startDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-3">
<validation-wrapper-component :validator="$v.parameters.endDate">
<label class="all-caps">End Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.endDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-3">
<div class="row text-center">
<div class="col b-a b-grey padding-20 m-r-15 pointer bg-complete text-white" @click="getHistoryImportedInvReport()">History Imported Invoices Report</div>
</div>
</div>
</div>
</div>
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="row p-t-10 p-b-10 b-b b-grey text-master-light">
<div class="col-2">Date</div>
</div>
</div>
</div>
<list-component :key="step" ref="HistoryImportedInvList" section="HistoryImportedInvSection" :endpoint="route('api.accounting.history.imported')" :options="filter">
<template slot="list" slot-scope="{data}">
<history-imported-invoices :data="data" :section="section"></history-imported-invoices>
</template>
</list-component>
</div>
</div>
</div>
</div>
<!-- report for imported Receipts -->
<div class="row" v-if="step > 0 && report == 'importedRec'">
<div class="col">
<div class="row">
<div class="col">
<div class="justify-content-center align-items-center m-t-50 m-b-50">
<div class="row">
<div class="col-3">
<validation-wrapper-component :validator="$v.parameters.startDate">
<label class="all-caps">Start Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.startDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-3">
<validation-wrapper-component :validator="$v.parameters.endDate">
<label class="all-caps">End Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.endDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-3">
<div class="row text-center">
<div class="col b-a b-grey padding-20 m-r-15 pointer bg-complete text-white" @click="getHistoryImportedRecReport()">History Imported Receipts Report</div>
</div>
</div>
</div>
</div>
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="row p-t-10 p-b-10 b-b b-grey text-master-light">
<div class="col-2">Date</div>
</div>
</div>
</div>
<list-component :key="step" ref="HistoryImportedRecList" section="HistoryImportedRecSection" :endpoint="route('api.accounting.history.imported')" :options="filter">
<template slot="list" slot-scope="{data}">
<history-imported-receipts :data="data" :section="section"></history-imported-receipts>
</template>
</list-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
export default {
data() {
return {
parameters: {
startDate: '',
endDate: '',
owner_reference: '',
invoice_reference: '',
receipt_reference: ''
},
step: 0,
filter: {},
report: ''
}
},
validations: {
parameters: {
startDate: {
required
},
endDate: {
required
},
owner_reference: {},
invoice_reference: {},
receipt_reference: {}
},
},
created(){
this.parameters.startDate = this.startDate();
this.parameters.endDate = this.endDate();
},
methods: {
startDate() {
var date = new Date();
return '01-'+(date.getMonth() + 1)+'-'+date.getFullYear();
},
endDate() {
var date = new Date();
var lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0);
return lastDay.getDate()+'-'+(lastDay.getMonth() + 1)+'-'+lastDay.getFullYear();
},
getReportFilter() {
this.filter = {min_amount: 0, is_mapped: true, statement_transaction_owner_type_in: [1, 2], statement_transaction_owner_status_in: [3], per_page: 100, order_by: {column: 'posting_date', DESC: true}};
this.filter = {...this.filter, ...{statement_transaction_posting_start: this.parameters.startDate}};
this.filter = {...this.filter, ...{statement_transaction_posting_end:this.parameters.endDate}}
if (typeof this.parameters.owner_reference != 'undefined' && this.parameters.owner_reference != '') this.filter = {...this.filter, ...{statement_transaction_owner_reference: this.parameters.owner_reference}};
if (typeof this.parameters.invoice_reference != 'undefined' && this.parameters.invoice_reference != '') this.filter = {...this.filter, ...{statement_transaction_invoice_reference: this.parameters.invoice_reference}};
if (typeof this.parameters.receipt_reference != 'undefined' && this.parameters.receipt_reference != '') this.filter = {...this.filter, ...{statement_transaction_receipt_reference: this.parameters.receipt_reference}};
this.step = this.step + 1;
this.report = 'mappedRecords';
},
getHistoryImportedRecReport() {
this.filter = {
imported_date_from: this.parameters.startDate,
imported_date_to:this.parameters.endDate,
group_by_imported_date:true,
type:'receipts'
};
this.step = this.step + 1;
this.report = 'importedRec';
},
getHistoryImportedInvReport() {
this.filter = {
imported_date_from: this.parameters.startDate,
imported_date_to:this.parameters.endDate,
group_by_imported_date:true,
type:'invoices'
};
this.step = this.step + 1;
this.report = 'importedInv';
}
},
}
</script>
@@ -65,10 +65,12 @@
<thead>
<tr>
<th>ID</th>
<th>Date</th>
<th>Posting Date</th>
<th>Description 1</th>
<th>Pay For</th>
<th>System References</th>
<th>Description 2</th>
<th>Description 3</th>
<th>Description 4</th>
<th>Description 5</th>
<th>Amount</th>
<th>Action</th>
</tr>
@@ -76,11 +78,12 @@
<tbody v-show="!isLoading">
<tr v-for="item in $store.getters.getListData(section)" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.date}}</td>
<td v-if="item.transaction_description_1 != null">{{item.transaction_description_1 | truncate(30, '...')}}</td>
<td v-if="item.transaction_description_1 == null"></td>
<td>{{item.pay_for}}</td>
<td>{{item.system_references}}</td>
<td>{{item.posting_date}}</td>
<td>{{item.transaction_description_1}}</td>
<td>{{item.transaction_description_2}}</td>
<td>{{item.transaction_description_3}}</td>
<td>{{item.transaction_description_4}}</td>
<td>{{item.transaction_description_5}}</td>
<td>{{item.amount}}</td>
<td>
<a href="#">
@@ -184,11 +187,7 @@ export default {
},
created(){
this.setDecoratorDefault();
if (this.statement === 0) {
this.filters = { 'per_page': 10, order_by: {column: 'id', DESC: true} };
} else {
this.filters = { 'per_page': 10, order_by: {column: 'id', DESC: true}, 'has_account_statement_id': this.statement };
}
this.filters = { 'per_page': 10, order_by: {column: 'id', DESC: true} };
this.$store.dispatch('updateListQueue', {'name': this.section, 'page': 1, 'filters': this.filters});
},
methods: {
@@ -55,7 +55,7 @@
<div class="col"></div>
<div class="col-auto pointer bold text-danger" @click="step=0">X</div>
</div>
<div class="row parentContainer" v-if="step > 0 && stage !== 4">
<div class="row parentContainer" v-if="step > 0">
<div class="col">
<div class="row">
<div class="col">
@@ -68,11 +68,38 @@
class="text-center"
:apiRoute="route('api.accounting.statement_transaction.owner.groupApprove')"
apiMethod="post"
:params="filter"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div>
<!-- filter for Pending Export tab -->
<div v-if="stage === 4">
<div class="row pt-3 pb-3">
<div class="col-5 col-md-3 ">
<validation-wrapper-component :validator="$v.parameters.startDate">
<label class="all-caps">Start Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.startDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-5 col-md-3 ">
<validation-wrapper-component :validator="$v.parameters.endDate">
<label class="all-caps">End Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.endDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-2 col-md-auto">
<div class="btn btn-lg btn-primary fs-11 w-100 h-100 d-flex justify-content-center align-items-center" @click="startMapping(4)">
<span>
Search
</span>
</div>
</div>
</div>
</div>
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="row p-t-10 p-b-10 b-b b-grey text-master-light">
@@ -86,19 +113,23 @@
</div>
</div>
<div class="col-1">Amount</div>
<div class="col-1" v-if="stage === 4"></div>
<div class="col-1" v-if="stage === 4">Select <input type="checkbox" v-model="selectAll">
</div>
</div>
</div>
</div>
<list-component ref="bankTransactionsList" section="bankTransactionSection" :endpoint="route('api.accounting.bank.transaction')" :options="this.filter">
<list-component :key="stage" ref="bankTransactionsList" section="bankTransactionSection" :endpoint="route('api.accounting.bank.transaction')" :options="this.filter">
<template slot="list" slot-scope="{data}">
<statement-transaction-component :data="data" :section="section" :stage="stage"></statement-transaction-component>
<statement-transaction-component :data="data" :section="section" :stage="stage" :selectAll="selectAll"></statement-transaction-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
<div class="row" v-if="stage === 4">
<div class="row parentContainer" v-if="stage === 4">
<div class="col">
<div class="row text-center m-t-50 m-b-50 p-t-50 p-b-50" v-show="exportStage === 0">
<div class="col">
@@ -122,7 +153,7 @@
</file-input-component>
</div>
</div>
<div class="btn btn-lg btn-primary m-t-20" @click="importInvoice">Import Invoices</div>
<div class="btn btn-lg btn-primary m-t-20 requestModal" data-type="ModalImportInvoice" @click="importInvoice">Import Invoices</div>
<!-- todo-new: delete later --><br><div class="btn btn-lg btn-primary m-t-20" @click="exportStage++">Nest Step</div>
<br>
<div class="row">
@@ -130,11 +161,19 @@
<a href="https://docs.google.com/presentation/d/1XwKcdBCpHQSCQmsgnUHsypcqk5kdc8uZMqkjW9JXiw4/edit?usp=sharing" target="_blank">Learn How to do this step?</a>
</div>
</div>
<div class="row">
<div class="col-12">
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="ModalImportInvoice">
<imported-invoice-mapped-component section="importInvoiceMapping" v-if="invMappedTrue" :files="files"></imported-invoice-mapped-component>
</modal-component>
</div>
</div>
</div>
</div>
<div class="row text-center m-t-50 m-b-50 p-t-50 p-b-50" v-show="exportStage === 2">
<div class="col">
<div class="btn btn-lg btn-primary" @click="exportStage++">Export Receipts To AutoCount</div>
<div class="btn btn-lg btn-primary" @click="exportReceiptToAutoCount">Export Receipts To AutoCount</div>
<!-- todo-new: delete later --><br><div class="btn btn-lg btn-primary m-t-20" @click="exportStage++">Nest Step</div>
<br>
<div class="row">
<div class="col">
@@ -153,7 +192,7 @@
</file-input-component>
</div>
</div>
<div class="btn btn-lg btn-primary m-t-20" @click="importReceipts">Import Receipts</div>
<div class="btn btn-lg btn-primary m-t-20 requestModal" data-type="ModalImportReceipt" @click="importReceipts">Import Receipts</div>
<!-- todo-new: delete later --><br><div class="btn btn-lg btn-primary m-t-20" @click="exportStage++">Nest Step</div>
<br>
<div class="row">
@@ -161,6 +200,13 @@
<a href="https://docs.google.com/presentation/d/1xMiWjU1hqPaihMhbgflFSDsu1sC_0Lk7OFmwDaXVt-w/edit?usp=sharing" target="_blank">Learn How to do this step?</a>
</div>
</div>
<div class="row">
<div class="col-12">
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="ModalImportReceipt">
<imported-receipt-mapped-component section="importReceiptMapping" v-if="recMappedTrue" :files="files"></imported-receipt-mapped-component>
</modal-component>
</div>
</div>
</div>
</div>
<div class="row text-center m-t-50 m-b-50 p-t-50 p-b-50" v-show="exportStage === 4">
@@ -182,6 +228,10 @@ export default {
data(){
return {
parameters: {
startDate: '',
endDate: '',
},
type: null,
stage: null,
exportStage: 0,
@@ -190,28 +240,59 @@ export default {
files: [],
parameters: {},
section: 'bankTransactionSection',
invMappedTrue: false,
recMappedTrue: false,
selectAll: false,
}
},
validations: {
parameters: {
startDate: {
required
},
endDate: {
required
},
},
files: {
// required // todo-new: set required if is pdf section
}
},
methods: {
importInvoice(){
this.parameters = {
files: this.files
};
this.submit(this.route('api.import_invoices.upload'), 'post', this.section, true, false);
this.invMappedTrue = true;
},
importReceipts(){
this.parameters = {
files: this.files
};
this.submit(this.route('api.import_receipts.upload'), 'post', this.section, true, false);
this.recMappedTrue = true;
},
exportInvoiceToAutoCount(){
window.open(this.route('invoiceTransactions.export'), '_blank');
const checkedStatementTransactions = this.getCheckedStatementOwners();
let route = this.route('invoiceTransactions.export')+'?filter='+JSON.stringify(this.filter);
if (checkedStatementTransactions) {
route += '&bankStatementTransactionId='+checkedStatementTransactions;
}
window.open(route, '_blank');
},
exportReceiptToAutoCount(){
const checkedStatementTransactions = this.getCheckedStatementOwners();
this.filter['where_has_owners_and_null'] = 'statement_transaction_owners.receipt_reference';
this.filter['where_has_owners_and_not_null'] = 'statement_transaction_owners.invoice_reference';
let route = this.route('receiptTransactions.export')+'?filter='+JSON.stringify(this.filter);
if (checkedStatementTransactions) {
route += '&bankStatementTransactionId='+checkedStatementTransactions;
}
window.open(route, '_blank');
},
getCheckedStatementOwners() {
let bankStatementTransactionId = [];
$('.request_export_item:checked').each(function() {
bankStatementTransactionId.push($(this).val());
});
return (bankStatementTransactionId.length > 0 ? JSON.stringify(bankStatementTransactionId) : null);
},
successHandler(){
this.step += 1;
@@ -233,10 +314,14 @@ export default {
this.filter = {min_amount: 0, is_mapped: true, is_mapped_with_multiple: true, statement_transaction_owner_type_in: [1, 2], statement_transaction_owner_status_in: [1], per_page: 100, order_by: {column: 'posting_date', DESC: true}}
break;
case 3:
this.filter = {min_amount: 0, is_mapped: false, per_page: 100, order_by: {column: 'posting_date', DESC: true}}
this.filter = {min_amount: 0, is_mapped_false_or_mapped_but_status_in: [4], per_page: 100, order_by: {column: 'posting_date', DESC: true}}
break;
case 4:
this.filter = {min_amount: 0, is_mapped: true, statement_transaction_owner_type_in: [1, 2], statement_transaction_owner_status_in: [2], per_page: 100, order_by: {column: 'posting_date', DESC: true}}
if (typeof this.parameters.startDate != 'undefined' && this.parameters.startDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_start: this.parameters.startDate}};
if (typeof this.parameters.endDate != 'undefined' && this.parameters.endDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_end:this.parameters.endDate}};
break;
}
}
@@ -254,6 +339,11 @@ export default {
break;
case 4:
this.filter = {max_amount: 0, is_mapped: true, statement_transaction_owner_type_in: [3, 5], statement_transaction_owner_status_in: [2], per_page: 100, order_by: {column: 'posting_date', DESC: true}}
if (typeof this.parameters.startDate != 'undefined' && this.parameters.startDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_start: this.parameters.startDate}};
if (typeof this.parameters.endDate != 'undefined' && this.parameters.endDate != '') this.filter = {...this.filter, ...{statement_transaction_posting_end:this.parameters.endDate}};
break;
}
}
@@ -22,7 +22,7 @@
</div>
</div>
</div>
<div class="col d-none d-lg-block" v-if="$store.getters.isAdmin">
<div class="col-auto d-none d-lg-block" v-if="$store.getters.isAdmin">
<div class="font-heading fs-10 muted all-caps">Marking</div>
<div class="font-heading">
<a :href="route('customer.profile', item.company.reference)">{{this.item.company.reference}}</a>
@@ -34,7 +34,7 @@
<span class="flag-icon" :class="'flag-icon-'+item.convertible_currency.country.short_code.toLowerCase()"></span> {{this.item.convertible_currency.short_code}}
</div>
</div>
<div class="col-auto d-none d-lg-block">
<div class="col-3 d-none d-lg-block">
<div class="font-heading fs-10 muted all-caps">Transfer Type</div>
<div class="font-heading fs-12">{{this.item.service.name}}</div>
</div>
@@ -0,0 +1,98 @@
<template>
<div class="row m-b-15">
<div class="col-12">
<div class="row">
<div class="col-12">
<div class="row m-l-0 m-r-0">
<div class="col mb-2 mb-md-0 p-l-3 p-r-3 p-md-0">
<validation-wrapper-component selectable :validator="$v.parameters.supplier">
<label>Supplier</label>
<selectable-component :disableOnFetch="true" :endpoint="route('api.company.list') + '?filters=' + JSON.stringify({'business_type': 3, 'status_in': [1, 2, 0]})" section="exportSupplierFilterSection" valueColumn="id" :labelColumn="['name']" v-model="parameters.supplier"></selectable-component>
</validation-wrapper-component>
</div>
<div class="col mb-2 mb-md-0 p-l-3 p-r-3 p-md-0">
<validation-wrapper-component :validator="$v.parameters.startDate">
<label class="all-caps">Start Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.startDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col mb-2 mb-md-0 p-l-3 p-r-3 p-md-0">
<validation-wrapper-component :validator="$v.parameters.endDate">
<label class="all-caps">End Date</label>
<date-picker-component :parameters="parameters" v-model.lazy="parameters.endDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col-auto p-l-3 p-r-3 p-md-0">
<div class="btn btn-lg btn-primary fs-11 w-100 h-100 d-flex justify-content-center align-items-center" @click="bulkDownloadWhiteForm()">
<span>
Export
</span>
</div>
</div>
</div>
</div>
</div>
<div class="row align-items-center justify-content-center bg-white p-t-50 p-b-50 p-l-15 p-r-15 margin-15" v-if="returnData">
<div class="col-10">
<div class="row align-items-center justify-content-center hint-text">
<div class="col-4 hint-text"><img src="/images/not-found-illustration.png" class="w-100 hint-text" /></div>
</div>
<div class="row text-center">
<div class="col">
<div class="row m-t-20">
<div class="col">
<p class="all-caps no-margin fs-11" :class="[{'text-danger': returnData.status == 'Error'}]" style="letter-spacing: 2px;">{{ returnData.message }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from "../../../general/mixins/componentHandler";
import { required } from "vuelidate/lib/validators";
export default {
data(){
return {
returnData: null,
parameters: {
startDate: '',
endDate: '',
supplier: ''
},
}
},
validations: {
parameters: {
supplier: {
required
},
startDate: {
required
},
endDate: {
required
},
}
},
methods: {
bulkDownloadWhiteForm(){
this.submit(this.route('api.suppliers.white_forms'), 'post', this.section, false, false);
},
successHandler(response) {
this.isLoading = false;
if (response.message) {
this.returnData = response;
} else {
this.returnData = null;
}
},
},
mixins: [componentHandler]
};
</script>
@@ -0,0 +1,121 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col-8 p-l-0">
<small class="all-caps muted fs-10">Export Payment Transactions CSV</small>
<download-billing-with-dates-component section="paymentsReportSection" ></download-billing-with-dates-component>
</div>
</div>
<div class="row">
<div class="col-8 p-l-0">
<small class="all-caps muted fs-10">Download Zip White Form</small>
<download-supplier-white-form-component section="paymentsReportSection" ></download-supplier-white-form-component>
</div>
</div>
<div class="row">
<div class="col-12 col-md-6">
<div class="row">
<div class="col p-l-0">
<small class="all-caps muted fs-10">Filter Supplier</small>
</div>
</div>
<div class="row">
<div class="col padding-0">
<validation-wrapper-component selectable :validator="$v.filterSupplier">
<label>Supplier</label>
<selectable-component :disableOnFetch="true" :endpoint="route('api.company.list') + '?filters=' + JSON.stringify({'business_type': 3, 'status_in': [1, 2, 0]})" section="supplierFilterSection" valueColumn="id" :labelColumn="['name']" v-model="filterSupplier"></selectable-component>
</validation-wrapper-component>
</div>
<div class="col-2 p-l-0">
<button type="button" class="btn btn-lg btn-complete fs-11 h-100 d-flex justify-content-center align-items-center" @click="reset()">Reset</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col bg-white p-t-15 p-b-15">
<div class="row no-margin">
<div class="col-12 col-md-4">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Complete Payments</small>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="2" section="paymentVerificationSection" :endpoint="route('api.transaction.list')" :options="{'type': 1, 'status': 3, 'payment_method_not_in': [4]}">
<template slot="list" slot-scope="{data}">
<payment-verification-component section="paymentVerificationSection" :data="data" :no_action="true"></payment-verification-component>
</template>
</list-component>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Complete Currency Orders</small>
</div>
</div>
<div class="row">
<div class="col">
<list-component :key="currencyOrderKey" section="completeTransactionGroupsListSection" :options="{'per_page': 20, 'status': 3, ...this.filterSupplier && {'issuer_in': [this.filterSupplier]}}" :endpoint="route('api.transaction.group.list')">
<template slot="list" slot-scope="{data}">
<transaction-group-component section="transactionGroupsListSection" :data="data"></transaction-group-component>
</template>
</list-component>
<!-- <list-component key="2" section="currencyOrdersListSection" :options="{'per_page': 20, 'document_type_in': ['CURRENCY_VENDOR_ORDER'], 'status': 2, 'with_company': true}" :endpoint="route('api.document.list')">
<template slot="list" slot-scope="{data}">
<currency-order-component section="currencyOrdersListSection" :data="data"></currency-order-component>
</template>
</list-component> -->
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Open Currency Orders</small>
</div>
</div>
<div class="row">
<div class="col">
<list-component :key="currencyOrderKey" section="transactionGroupsListSection" :options="{'per_page': 20, 'status_in': [0, 1, 2], ...this.filterSupplier && {'issuer_in': [this.filterSupplier]}}" :endpoint="route('api.transaction.group.list')">
<template slot="list" slot-scope="{data}">
<transaction-group-component section="transactionGroupsListSection" :data="data"></transaction-group-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
filterSupplier: "",
currencyOrderKey: 1,
}
},
validations: {
filterSupplier: {}
},
watch: {
filterSupplier() {
this.currencyOrderKey += 1;
}
},
methods: {
reset() {
this.filterSupplier = ""
}
}
}
</script>
File diff suppressed because one or more lines are too long
@@ -289,6 +289,22 @@
<regenerate-booking-invoices-component :data="booking" :section="section" class="text-center"></regenerate-booking-invoices-component>
</modal-component>
</div>
<div class="row m-t-10" v-if="$store.getters.isSuperAdmin">
<div class="col-sm col-md-auto">
<div class="btn btn-sm btn block all-caps b-rad-none btn-danger pointer requestModal" data-type="deleteTransfer">Delete Transfer</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="deleteTransfer">
<general-confirmation-form-component
contentText="Are you sure you want to delete this Order?"
modalType="delete"
class="text-center"
:apiRoute="route('api.booking.payment.expire', booking.id)"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div>
</div>
<div class="col-12 col-sm-12 col-md-5 mt-3 mt-sm-0">
<booking-payment-quotation-component :data="booking" :section="section"></booking-payment-quotation-component>
@@ -0,0 +1,104 @@
<template>
<div class="row">
<div class="col">
<div class="row m-b-5" @keyup.enter="submitSearch">
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.marking">
<label class="all-caps">Marking</label>
<!-- <input type="text" class="form-control" v-model="parameters.marking" :class="[{ 'not-allowed': hasMarkingParameter }]" :disabled="hasMarkingParameter"> -->
<input type="text" class="form-control" v-model="parameters.marking" :class="[{ 'not-allowed': hasMarking }]" :disabled="hasMarking">
</validation-wrapper-component>
</div>
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.startDate">
<label class="all-caps">Start Date</label>
<date-picker-component v-model.lazy="parameters.startDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.endDate">
<label class="all-caps">End Date</label>
<date-picker-component v-model.lazy="parameters.endDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col col-md-auto d-flex justify-content-center align-items-center">
<button type="button" class="btn btn-lg btn-primary fs-11 w-100"
@click="submitSearch()">Download</button>
</div>
</div>
<loading-component style="height: 100px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div class="row align-items-center justify-content-center bg-white p-t-50 p-b-50 p-l-15 p-r-15 margin-15" v-if="!isLoading && returnData">
<div class="col-10">
<div class="row align-items-center justify-content-center hint-text">
<div class="col-4 hint-text"><img src="/images/not-found-illustration.png" class="w-100 hint-text" /></div>
</div>
<div class="row text-center">
<div class="col">
<div class="row m-t-20">
<div class="col">
<p class="all-caps no-margin fs-11" :class="[{'text-danger': returnData.status == 'Error'}]" style="letter-spacing: 2px;">{{ returnData.message }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
export default {
data() {
return {
key: 1,
returnData: null,
isLoading: false,
hasMarking: false,
parameters: {
marking: null,
startDate: null,
endDate: null,
},
}
},
validations: {
parameters: {
marking: { required },
startDate: { required },
endDate: { required },
}
},
created() {
const urlSearchParams = new URLSearchParams(window.location.search);
if (urlSearchParams.has('marking')) {
this.parameters.marking = urlSearchParams.get('marking');
}
this.hasMarkingParameter();
},
methods: {
submitSearch() {
this.isLoading = true;
this.submit(this.route('api.customers.invoices'), 'post', this.section, false, false);
},
errorHandler(error) {
this.isLoading = false;
console.log(error);
},
successHandler(response) {
this.isLoading = false;
if (response.message) {
this.returnData = response;
} else {
this.returnData = null;
}
},
hasMarkingParameter() {
const urlSearchParams = new URLSearchParams(window.location.search);
this.hasMarking = urlSearchParams.has('marking');
},
},
}
</script>
@@ -99,6 +99,7 @@
updateFilters(filters){
this.filters = filters;
this.setDecoratorDefault();
this.$store.dispatch('updateListQueue', {'name': this.section, 'page': 1, 'filters': this.filters});
this.submit(this.endpoint + '?page=1&filters=' + JSON.stringify(this.filters), 'get', this.section, false, false)
},
successHandler(response){
@@ -15,7 +15,7 @@
<div class="btn btn-sm btn-default btn-block bg-master-lighter" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-block b-rad-none" :class="[{'btn-danger': modalType !== 'confirm'}, {'btn-success': modalType === 'confirm'}]" @click="submit(apiRoute, apiMethod, section, true, true)">{{ buttonText }}</div>
<div class="btn btn-sm btn-block b-rad-none" :class="[{'btn-danger': modalType !== 'confirm'}, {'btn-success': modalType === 'confirm'}]" @click="submitForm()">{{ buttonText }}</div>
</div>
</div>
</div>
@@ -32,6 +32,10 @@
type: String,
required: true
},
params: {
type: Array,
required: false
},
modalType: {
type: String,
default: 'confirm'
@@ -50,6 +54,10 @@
},
},
methods: {
submitForm() {
if (this.params) this.parameters = this.params;
return this.submit(this.apiRoute, this.apiMethod, this.section, true, true);
}
},
mixins: [componentHandler, ModalFormHandler]
@@ -1,5 +1,5 @@
<template>
<select class="form-control">
<select class="form-control" :disabled="this.options.length === 0 && this.disableOnFetch">
<option disabled></option>
</select>
</template>
@@ -13,6 +13,10 @@
},
value: {
required: false
},
disableOnFetch: {
type: Boolean,
default: false
}
},
mounted(){
@@ -1,5 +1,5 @@
<template>
<select-component :options="$store.getters.getSelectableList(section)" :value="value" @input="(val) => {$emit('input', val)}"></select-component>
<select-component :disableOnFetch="disableOnFetch" :options="$store.getters.getSelectableList(section)" :value="value" @input="(val) => {$emit('input', val)}"></select-component>
</template>
<script>
@@ -23,6 +23,10 @@
valueColumn: {
type: String,
required: true
},
disableOnFetch: {
type: Boolean,
default: false
}
},
created(){
@@ -1,6 +1,29 @@
<template>
<div class="row">
<div class="col">
<div class="row m-b-5" @keyup.enter="submitSearch">
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.reference_no">
<label class="all-caps">Booking Reference</label>
<input type="text" class="form-control" v-model="parameters.reference_no">
</validation-wrapper-component>
</div>
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.startDate">
<label class="all-caps">Start Date</label>
<date-picker-component v-model.lazy="parameters.startDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col p-r-0">
<validation-wrapper-component :validator="$v.parameters.endDate">
<label class="all-caps">End Date</label>
<date-picker-component v-model.lazy="parameters.endDate"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col col-md-auto d-flex justify-content-center align-items-center">
<button type="button" class="btn btn-lg btn-primary fs-11 w-100" @click="submitSearch()">Search</button>
</div>
</div>
<div class="row">
<div class="col">
<div class="row padding-10">
@@ -11,7 +34,7 @@
<div class="col-2 fs-10 text-right">Balance</div>
</div>
<list-component :key="key" section="topUp" :endpoint="route('api.transaction.wallet.list')" :options="{status_in: [2, 3], owner_type: 'App\\Models\\Wallet', owner_id: wallet_id, per_page: showingTransactionCount}">
<list-component :key="key" section="topUp" :endpoint="route('api.transaction.wallet.list')" :options="options">
<template slot="list" slot-scope="{data}">
<customer-wallet-transaction-component :data="data" :deciamls="deciamls"></customer-wallet-transaction-component>
</template>
@@ -39,12 +62,50 @@ export default {
data(){
return {
key: 1,
parameters: {
reference_no: null,
startDate: null,
endDate: null,
},
options: {
status_in: [2, 3],
owner_type: 'App\\Models\\Wallet',
owner_id: this.wallet_id,
per_page: this.showingTransactionCount
}
}
},
validations: {
parameters: {
reference_no: { },
startDate: { },
endDate: { },
}
},
watch: {
showingTransactionCount() {
this.options.per_page = this.showingTransactionCount;
this.key ++;
}
}
},
methods: {
submitSearch() {
delete this.options.with_booking_marking_like;
delete this.options.created_after_or_equal;
delete this.options.created_before_or_equal;
if (this.parameters.reference_no) {
this.options.with_booking_marking_like = this.parameters.reference_no
}
if (this.parameters.startDate) {
this.options.created_after_or_equal = this.parameters.startDate
}
if (this.parameters.endDate) {
this.options.created_before_or_equal = this.parameters.endDate
}
this.key ++;
},
},
}
</script>
@@ -1,36 +1,37 @@
<template>
<div class="row">
<div class="col">
<div class="row">
<div class="col-8">
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
<div class="col no-padding">
<h6>Wallet Transaction History</h6>
</div>
</div>
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="d-flex align-items-center h-100">
<span class="btn btn-md fs-11 bg-primary text-white fs-12 bold m-r-5" :class="[{'bg-primary-darker': showingPreciseAmount}]" @click="showingPreciseAmount=!showingPreciseAmount">{{ showingPreciseAmount ? 'Showing Precise Wallet Transaction' : 'Show Precise Wallet Transaction'}}</span>
<a v-if="company" :href="route('wallet.details-export', company.reference, showingPreciseAmount)" target="_blank" class="btn btn-md btn-primary fs-11"><i class="fa fa-download m-r-5"></i>{{ showingPreciseAmount ? 'Download Precise Transaction' : 'Download Transaction'}}</a>
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div v-if="!isLoading">
<div class="row">
<div class="col-8">
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
<div class="col no-padding">
<h6>Wallet Transaction History</h6>
</div>
</div>
<div class="col-2">
<validation-wrapper-component selectable :validator="$v.showingTransactionCount">
<label>Showing Rows</label>
<select-component :options="[5, 10, 20, 30, 50]" v-model="showingTransactionCount"></select-component>
</validation-wrapper-component>
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="d-flex align-items-center h-100">
<span class="btn btn-md fs-11 bg-primary text-white fs-12 m-r-5" :class="[{'bg-primary-darker': showingPreciseAmount}]" @click="showingPreciseAmount=!showingPreciseAmount">{{ showingPreciseAmount ? 'Showing Precise Wallet Transaction' : 'Show Precise Wallet Transaction'}}</span>
<a v-if="company" :href="route('wallet.details-export', company.reference, showingPreciseAmount)" target="_blank" class="btn btn-md btn-primary fs-11"><i class="fa fa-download m-r-5"></i>{{ showingPreciseAmount ? 'Download Precise Transaction' : 'Download Transaction'}}</a>
</div>
</div>
<div class="col-2">
<validation-wrapper-component selectable :validator="$v.showingTransactionCount">
<label>Showing Rows</label>
<select-component :options="[5, 10, 20, 30, 50]" v-model="showingTransactionCount"></select-component>
</validation-wrapper-component>
</div>
</div>
<div v-show="!showingPreciseAmount">
<customer-transaction-component :deciamls="2" :wallet_id="wallet_id" :showingTransactionCount="showingTransactionCount"></customer-transaction-component>
</div>
<div v-show="showingPreciseAmount">
<customer-transaction-component :deciamls="5" :wallet_id="wallet_id" :showingTransactionCount="showingTransactionCount"></customer-transaction-component>
</div>
</div>
<div v-show="!showingPreciseAmount">
<customer-transaction-component :deciamls="2" :wallet_id="wallet_id" :showingTransactionCount="showingTransactionCount"></customer-transaction-component>
</div>
<div v-show="showingPreciseAmount">
<customer-transaction-component :deciamls="5" :wallet_id="wallet_id" :showingTransactionCount="showingTransactionCount"></customer-transaction-component>
</div>
</div>
<div class="col-3 m-l-15">
<div v-if="!isLoading">
<div class="col-3 m-l-15">
<wallet-component :data="company" :creditable=true></wallet-component>
<div class="row m-t-20">
<div class="col">
@@ -68,7 +69,6 @@
</div>
</div>
</div>
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
</div>
</div>
</div>
@@ -35,7 +35,7 @@
<div class="col-auto">
<div class="btn btn-xs p-l-15 p-r-20 b-rad-none font-heading btn-rounded bg-white" @click="reload = true"><i class="fa fa-plus fs-8 m-r-5"></i> Reload</div>
</div>
<div class="col-auto p-l-0" v-if="!mini">
<div class="col-auto p-l-0" v-if="!mini && data.wallet">
<a class="text-white fs-10" :href="route('wallet.details', data.reference)" target="_blank">Transaction History<i class="fa fa-angle-right p-l-5"></i></a>
</div>
</div>
@@ -52,9 +52,18 @@
<wallet-top-up-form-component :data="data" :amount="(!data.wallet ? amount : (Math.round((((amount - data.wallet.amount) < '0.00' ? '0.00' : (amount - data.wallet.amount)) + Number.EPSILON) * 100) / 100).toFixed(2))" :creditable="creditable"></wallet-top-up-form-component>
</div>
</div>
<div class="row m-t-20" v-if="$store.getters.isAdmin">
<div class="row m-t-10" v-if="$store.getters.isAdmin">
<div class="col">
<a :href="route('account.statment', data.reference)">View Account Statement<i class="fa fa-angle-right p-l-5"></i></a>
<div class="row m-t-10" v-if="$store.getters.isSuperAdmin">
<div class="col">
<a :href="route('account.statment', data.reference)">View Account Statement<i class="fa fa-angle-right p-l-5"></i></a>
</div>
</div>
<div class="row m-t-10">
<div class="col">
<a :href="downloadInvoiceUrl()">Download All Invoices<i class="fa fa-angle-right p-l-5"></i></a>
</div>
</div>
</div>
</div>
</div>
@@ -79,6 +88,11 @@
default: false
}
},
methods: {
downloadInvoiceUrl(){
return this.route('customers.invoices') + '?marking=' + this.data.reference;
},
},
data(){
return {
reload: false,
+29 -9
View File
@@ -13,19 +13,39 @@ export default {
let statusCode = response.status,
success = response.ok;
response.json().then(response => {
if (response.headers.get("content-type") === "application/zip") {
const fileName = response.headers.get('Content-Disposition').split('filename=')[1].replaceAll('"', '');
if(!success){
this.openModal();
errorNotification ? this.$store.dispatch('createNotification', {title: response.title, message: response.message, type: 'error'}): null;
this.errorHandler(response, statusCode); return;
}
response.blob().then(response => {
if (!success) {
this.openModal();
errorNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'error' }) : null;
this.errorHandler(response, statusCode); return;
}
successNotification ? this.$store.dispatch('createNotification', {title: response.title, message: response.message, type: 'success'}): null;
this.successHandler(response)
successNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'success' }) : null;
const link = document.createElement('a');
link.href = window.URL.createObjectURL(response);
link.download = fileName.trim();
link.click();
this.successHandler(response)
});
} else {
response.json().then(response => {
if (!success) {
this.openModal();
errorNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'error' }) : null;
this.errorHandler(response, statusCode); return;
}
successNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'success' }) : null;
this.successHandler(response)
});
});
}
}).catch((error) => {
console.log(error);
this.$store.dispatch('createNotification', {title: 'Unexpected Error', message: 'An unexpected error has occurred. Try again!', type: 'error'});
@@ -125,7 +125,7 @@
<div class="row justify-content-end">
<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="complete">
<div class="col p-t-20 p-b-20 bg-master-lighter tabButton" tab-name="ReportMapped">
<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"
@@ -312,6 +312,14 @@
</list-component>
</div>
</div>
{{-- tab for report & Analysis --}}
<div class="row tabsContainer tabContent" tab-name="ReportMapped">
<div class="col">
<report-transactions-mapped-component></report-transactions-mapped-component>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,8 @@
@extends('layouts.base_portal')
@section('inner_content')
<div class="row" v-if="$store.getters.isAdmin">
<div class="col">
<bulk-download-invoice-component section="BulkDownloadInvoiceComponent"></bulk-download-invoice-component>
</div>
</div>
@endsection
+1 -64
View File
@@ -1,67 +1,4 @@
@extends('layouts.base_portal')
@section('inner_content')
<div class="row">
<div class="col-12 col-md-6 no-padding">
<download-billing-with-dates-component section="paymentsReportSection" ></download-billing-with-dates-component>
</div>
</div>
<div class="row">
<div class="col bg-white p-t-15 p-b-15">
<div class="row no-margin">
<div class="col-12 col-md-4">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Complete Payments</small>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="2" section="paymentVerificationSection" :endpoint="route('api.transaction.list')" :options="{'type': 1, 'status': 3, 'payment_method_not_in': [4]}">
<template slot="list" slot-scope="{data}">
<payment-verification-component section="paymentVerificationSection" :data="data" :no_action="true"></payment-verification-component>
</template>
</list-component>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Complete Currency Orders</small>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="2" section="completeTransactionGroupsListSection" :options="{'per_page': 20, 'status': 3}" :endpoint="route('api.transaction.group.list')">
<template slot="list" slot-scope="{data}">
<transaction-group-component section="transactionGroupsListSection" :data="data"></transaction-group-component>
</template>
</list-component>
{{--<list-component key="2" section="currencyOrdersListSection" :options="{'per_page': 20, 'document_type_in': ['CURRENCY_VENDOR_ORDER'], 'status': 2, 'with_company': true}" :endpoint="route('api.document.list')">--}}
{{--<template slot="list" slot-scope="{data}">--}}
{{--<currency-order-component section="currencyOrdersListSection" :data="data"></currency-order-component>--}}
{{--</template>--}}
{{--</list-component>--}}
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="row m-b-15 p-b-10 b-b b-grey">
<div class="col">
<small class="all-caps muted fs-10">Open Currency Orders</small>
</div>
</div>
<div class="row">
<div class="col">
<list-component key="2" section="transactionGroupsListSection" :options="{'per_page': 20, 'status_in': [0, 1, 2]}" :endpoint="route('api.transaction.group.list')">
<template slot="list" slot-scope="{data}">
<transaction-group-component section="transactionGroupsListSection" :data="data"></transaction-group-component>
</template>
</list-component>
</div>
</div>
</div>
</div>
</div>
</div>
<payment-component></payment-component>
@endsection
@@ -69,109 +69,8 @@
<br>
<br>
<table class="line-table" style="overflow: wrap" autosize="1">
<!-- Table Header -->
<thead>
<tr>
<th width="5%">No</th>
<th class="stock-code" width="10%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 5) : "0";
$displayedSubtotal = "0";
$exactTotal = "0";
@endphp
@include('pages.pdfs.purchase_order_table')
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 5);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
// Round half to even for displayed item total
$displayedItemTotal = round(bcmul($exactUnitPrice, $transaction_detail->quantity, 2), 2, PHP_ROUND_HALF_EVEN);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
$exactTotal = bcadd($exactTotal, $displayedItemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($exactUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($itemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($subtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<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>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
// Calculate the totals with 5 decimal places
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
// Calculate the displayed totals with 2 decimal places
$displayedTotal = bcadd(bcadd(bcadd($displayedSubtotal, $transaction->service_charge, 2), $transaction->tax, 2), $voucherDiscount, 2);
// Calculate the discrepancy
$discrepancy = bcsub($expectedTotal, $displayedTotal, 5);
// Calculate the final total
$total = bcadd($expectedTotal, $discrepancy, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
<htmlpagefooter name="page-footer">
<table width="100%">
<tr>
+2 -91
View File
@@ -58,97 +58,8 @@
<br>
<!-- Invoice Table -->
<table class="line-table" style="overflow: wrap" autosize="1">
<!-- Table Header -->
<thead>
<tr>
<th width="5%">No</th>
<th class="stock-code" width="10%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 2) : "0";
$displayedSubtotal = 0;
@endphp
@include('pages.pdfs.purchase_order_table')
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 7);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
$displayedItemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 2);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($exactUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($itemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($subtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<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>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
$displayedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$discrepancy = bcsub($displayedTotal, $expectedTotal, 5);
$total = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
<br>
<div class="note">
<strong>Note:</strong> All items purchased are subject to our Terms & Conditions. Please refer to our official website for more information.
@@ -158,6 +69,6 @@
Please transfer the payment to:<br>
Bank: Maybank Berhad<br>
Account Name: CIEF Worldwide Sdn Bhd<br>
Account No: 564892103405<br>
Account No: 568603010762<br>
</div>
@endsection
@@ -76,109 +76,8 @@
<br>
<table class="line-table" style="overflow: wrap" autosize="1">
<!-- Table Header -->
<thead>
<tr>
<th width="5%">No</th>
<th class="stock-code" width="10%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 5) : "0";
$displayedSubtotal = "0";
$exactTotal = "0";
@endphp
@include('pages.pdfs.purchase_order_table')
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 5);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
// Round half to even for displayed item total
$displayedItemTotal = round(bcmul($exactUnitPrice, $transaction_detail->quantity, 2), 2, PHP_ROUND_HALF_EVEN);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
$exactTotal = bcadd($exactTotal, $displayedItemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($exactUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($itemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($subtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<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>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
// Calculate the totals with 5 decimal places
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
// Calculate the displayed totals with 2 decimal places
$displayedTotal = bcadd(bcadd(bcadd($displayedSubtotal, $transaction->service_charge, 2), $transaction->tax, 2), $voucherDiscount, 2);
// Calculate the discrepancy
$discrepancy = bcsub($expectedTotal, $displayedTotal, 5);
// Calculate the final total
$total = bcadd($expectedTotal, $discrepancy, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
<htmlpagefooter name="page-footer">
<table width="100%">
<tr>
@@ -0,0 +1,93 @@
<table class="line-table" style="overflow: wrap" autosize="1">
<!-- Table Header -->
<thead>
<tr>
<th width="5%">No</th>
<th class="stock-code" width="10%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 2) : "0";
$displayedSubtotal = 0;
$currency_id = $transaction->owner->fix_currency_id;
@endphp
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = ($currency_id) === 1 ? $transaction_detail->price : bcdiv($transaction_detail->price, $transaction->currency_rate, 7);
$displayUnitPrice = round($exactUnitPrice, 2);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
$displayedItemTotal = round(bcmul($displayUnitPrice, $transaction_detail->quantity, 7), 2);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($displayUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($displayedItemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($displayedSubtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<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>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
$displayedTotal = bcadd(bcadd(bcadd($displayedSubtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$discrepancy = bcsub($expectedTotal, $displayedTotal, 5);
$total = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
+4 -1
View File
@@ -10,10 +10,13 @@ Route::group(['prefix' => 'accounting', 'as' => 'accounting.', 'namespace' => 'A
Route::put('/details/update', 'BankStatementController@update')->name('details.update');
});
Route::post('bankStatement/{id}/details/{status}', 'ApproveDuplicateBankStatementDetailsStatusController@update')->where('status', 'approve|reject')->name('bankStatement.details.status.update');
Route::post('bankStatement/{id}/details/{status}', 'ApproveDuplicateBankStatementDetailsStatusController@update')->where('status', 'approve|reject|pending_verification')->name('bankStatement.details.status.update');
Route::group(['prefix' => 'statement_transaction', 'as' => 'statement_transaction.'], function () {
Route::post('/owner/group-approve', 'GroupApproveStatementTransactionController@approve')->name('owner.groupApprove');
Route::post('/{id}/owner/{status}', 'UpdateStatementTransactionStatusController@update')->where('status', 'approve|reject')->name('owner.status.update');
});
Route::get('history/imported', 'HistoryImportedTransactionMappedController@getImported')->name('history.imported');
});

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