Compare commits

...

92 Commits

Author SHA1 Message Date
Dillon Ngo 09383a45a6 UI Update (Payment and Billing page) on Admin view to see shipping invoices with storage invoices on Pending Payment tab and Paid Invoice tab 2024-02-25 15:44:03 +08:00
Dillon Ngo 141062a7bf Fix storage invoice not allow to be regenerated if not yet pay 2024-02-25 15:21:04 +08:00
Dillon Ngo 4179706e04 UI Update (Payment and Billing page) on Admin view to see shipping invoices with storage invoices on Pending Payment tab and Paid Invoice tab 2024-02-25 14:26:54 +08:00
Dillon Ngo eaa21beb83 UI Update (Payment and Billing page) on Admin view to see shipping invoices with storage invoices on Pending Payment tab and Paid Invoice tab 2024-02-25 14:04:19 +08:00
Dillon Ngo 1acc02eb93 UI Update (Payment and Billing page) on Admin view to see shipping invoices with storage invoices on Pending Payment tab and Paid Invoice tab 2024-02-25 13:52:22 +08:00
Dillon Ngo 1f2959a5d2 Merge branch 'dillon/46.7-warehouse-storage-charges-fixes' into dillon/46.6-warehouse-storage-charges-fixes 2024-02-25 13:15:49 +08:00
Dillon Ngo 9b7b7068e1 Merge branch 'dillon/46.5-warehouse-storage-charges-fixes' into dillon/46.6-warehouse-storage-charges-fixes 2024-02-25 13:15:35 +08:00
Dillon Ngo 41e1c346f6 Rename a component 2024-02-25 13:13:09 +08:00
Dillon Ngo f949877a7f Create a regenerate storage invoice feature to debug problem in reading and downloading storage invoice 2024-02-24 11:40:51 +08:00
Dillon Ngo 2dc037c8fd Create a regenerate storage invoice feature to debug problem in reading and downloading storage invoice 2024-02-24 11:08:19 +08:00
Dillon Ngo aaae8bcafa Create a regenerate storage invoice feature to debug problem in reading and downloading storage invoice 2024-02-24 10:48:26 +08:00
Dillon Ngo b66dfa8649 Implement authorization module for update customer credit term status 2024-02-24 10:08:24 +08:00
Dillon Ngo 2689296a2c Implement authorization module for update customer credit term status 2024-02-23 11:09:30 +08:00
Dillon Ngo 62191c2711 Undo a changes made on previous commit 2024-02-23 10:49:09 +08:00
Dillon Ngo 02cb21bd5a User interface to allow admin to update company whether is a credit term customer for warehoue storage charges 2024-02-18 10:10:57 +08:00
Dillon Ngo 78573db17d User interface to allow admin to update company whether is a credit term customer for warehoue storage charges 2024-02-18 10:00:35 +08:00
Dillon Ngo af1b6ac373 Merge branch 'dillon/46.4-warehouse-storage-charges' into 'master'
User interface to allow admin to waive warehouse storage invoice transaction

See merge request CIEFWorldwideSdnBhd/shipping-portal!200
2024-02-15 13:47:31 +00:00
Dillon Ngo d807e5d4fd User interface to allow admin to waive warehouse storage invoice transaction 2024-02-15 21:21:08 +08:00
Dillon Ngo c427ae42a0 User interface to allow admin to waive warehouse storage invoice transaction 2024-02-15 21:18:02 +08:00
Dillon Ngo 8a7a4ce38a User interface to allow admin to waive warehouse storage invoice transaction 2024-02-15 21:01:50 +08:00
Dillon Ngo 1b6fe7445e User interface to allow admin to waive warehouse storage invoice transaction 2024-02-15 20:45:32 +08:00
Dillon Ngo 7b2d0c4e2b Merge branch 'dillon/51.4-explore-multiple-queues' into 'master'
Vue Polling - Explore multiple laravel queue with priority settings

See merge request CIEFWorldwideSdnBhd/shipping-portal!199
2024-02-13 23:36:24 +00:00
Dillon Ngo 1bcad555a0 Second high priority queue through database 2024-02-13 22:06:38 +08:00
Dillon Ngo 2973547e76 Merge branch 'dillon/51.3-vue-polling-experimental' into 'master'
Vue Polling - Solve a problem where query of packing list with contaienrs and...

See merge request CIEFWorldwideSdnBhd/shipping-portal!198
2024-02-08 10:43:44 +00:00
Dillon Ngo 0b415e2e33 Vue Polling - Solve a problem where query of packing list with contaienrs and packages data is taking too long to complete + pagination problem 2024-02-08 18:41:57 +08:00
edmondlang 1110838e35 Merge branch 'master' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal into permits-reminder 2024-02-06 10:55:32 +08:00
edmondlang 9d87987488 code update - permits reminder - send email only at production 2024-02-06 10:55:19 +08:00
Omair Saleh 4c0984c9b8 fix container reschedule component 2024-02-05 15:08:51 +08:00
Omair Saleh 5517f16684 fix container reschedule component 2024-02-05 13:24:25 +08:00
Omair Saleh 4b967981dd fix container reschedule component 2024-02-05 12:56:30 +08:00
Omair Saleh e63b959714 fix container reschedule component 2024-02-05 12:52:07 +08:00
Omair Saleh a1751f274d Merge remote-tracking branch 'origin/master' 2024-02-05 12:48:54 +08:00
Omair Saleh ef6538feca fix container reschedule component 2024-02-05 12:47:51 +08:00
edmondlang de61d9a9fd add anitha email for premits reminder 2024-01-30 12:41:51 +08:00
edmondlang 2c1b857255 update permits reminder view 2024-01-30 12:37:34 +08:00
edmondlang 5b4a3507a3 add cron job to send permits reminder at daily 9.30 am 2024-01-30 00:48:23 +08:00
edmondlang 3d6543e467 add sample file 2024-01-30 00:42:21 +08:00
edmondlang 006a327a5f Merge branch 'master' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal into permits-reminder 2024-01-30 00:34:51 +08:00
edmondlang 295e6454b1 add cron job command to send permits reminder 2024-01-30 00:34:39 +08:00
edmondlang f389efa6af add permits edit 2024-01-30 00:07:31 +08:00
Dillon Ngo f83c89b5f3 Second high priority queue through database 2024-01-26 04:03:06 +08:00
Dillon Ngo 4ea3132334 Merge branch 'dillon/51.3-vue-polling-experimental' into 'master'
Vue Polling - Solve a problem where query of packing list with contaienrs and...

See merge request CIEFWorldwideSdnBhd/shipping-portal!197
2024-01-25 19:19:45 +00:00
Dillon Ngo 8fc3ac1b55 Vue Polling - Solve a problem where query of packing list with contaienrs and packages data is taking too long to complete 2024-01-26 03:18:58 +08:00
Dillon Ngo ed252e630b Merge branch 'dillon/51.3-vue-polling-experimental' into 'master'
Vue Polling - Solve a problem where query of packing list with contaienrs and...

See merge request CIEFWorldwideSdnBhd/shipping-portal!196
2024-01-25 18:21:17 +00:00
Dillon Ngo b6c9c8843d Vue Polling - Solve a problem where query of packing list with contaienrs and packages data is taking too long to complete 2024-01-26 02:19:31 +08:00
Dillon Ngo d80e996d02 Merge branch 'dillon/46.4-warehouse-storage-charges' into 'master'
Implement a solution to selectively waived a selected storage invoice

See merge request CIEFWorldwideSdnBhd/shipping-portal!195
2024-01-25 13:47:10 +00:00
Dillon Ngo 064ee3e41a Implement a solution to selectively waived a selected storage invoice 2024-01-24 06:50:18 +08:00
Dillon Ngo 3bda84dac5 Merge branch 'dillon/51.3-vue-polling-experimental' into 'master'
Vue Polling - Solve a problem where query of packing list with contaienrs and...

See merge request CIEFWorldwideSdnBhd/shipping-portal!194
2024-01-21 14:49:24 +00:00
Dillon Ngo e2b378e197 Vue Polling - Solve a problem where query of packing list with contaienrs and packages data is taking too long to complete 2024-01-21 22:48:48 +08:00
Dillon Ngo 0f2e1297e1 Merge branch 'dillon/51.3-vue-polling-experimental' into 'master'
Vue Polling - Solve a problem where query of packing list with contaienrs and...

See merge request CIEFWorldwideSdnBhd/shipping-portal!193
2024-01-21 14:35:45 +00:00
Dillon Ngo d034a285fc Vue Polling - Solve a problem where query of packing list with contaienrs and packages data is taking too long to complete 2024-01-21 22:31:16 +08:00
Dillon Ngo ee94943d00 Merge branch 'dillon/51.3-vue-polling-experimental' into 'master'
Experimental solution (Vue Polling) to solve an AWS API Gateway problem limitation for Laravel Vapor in the event of a emergency rollback to Google

See merge request CIEFWorldwideSdnBhd/shipping-portal!192
2024-01-21 02:54:47 +00:00
Dillon Ngo a9e89a45bd Experimental solution (Vue Polling) to solve an AWS API Gateway problem limitation for Laravel Vapor in the event of a emergency rollback to Google 2024-01-21 10:53:23 +08:00
Dillon Ngo f6b10b5763 Merge branch 'dillon/51.3-vue-polling-experimental' into 'master'
Experimental solution (Vue Polling) to solve an AWS API Gateway problem limitation for Laravel Vapor in the event of a emergency rollback to Google

See merge request CIEFWorldwideSdnBhd/shipping-portal!191
2024-01-21 02:32:55 +00:00
Dillon Ngo 9b9057ea81 Experimental solution (Vue Polling) to solve an AWS API Gateway problem limitation for Laravel Vapor in the event of a emergency rollback to Google 2024-01-21 10:31:39 +08:00
Dillon Ngo 33d2d02cb0 Merge branch 'dillon/51-vue-polling-experimental-2' into 'master'
Undo Experimental solution (Vue Polling)

See merge request CIEFWorldwideSdnBhd/shipping-portal!190
2024-01-20 19:16:33 +00:00
Dillon Ngo e5be6eaceb Undo Experimental solution (Vue Polling) 2024-01-21 03:15:46 +08:00
edmondlang a545e6bad6 add bill_no in transaction details page 2024-01-17 17:19:37 +08:00
edmondlang b64b25ff05 code update 2024-01-17 17:06:31 +08:00
edmondlang d1426c7f05 all api and ui to create permits reminder 2024-01-17 00:43:17 +08:00
edmondlang 3375934651 Merge branch 'master' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal into permits-reminder
# Conflicts:
#	routes/api.php
2024-01-16 21:16:11 +08:00
edmondlang c79f58d246 permits reminder first commit 2024-01-16 21:15:29 +08:00
Dillon Ngo ee17ac0b0c Merge branch 'dillon/51-vue-polling-experimental-2' into 'master'
Experimental solution (Vue Polling) to solve an AWS API Gateway problem limitation for Laravel Vapor in the event of a emergency rollback to Google

See merge request CIEFWorldwideSdnBhd/shipping-portal!189
2024-01-15 16:43:45 +00:00
Dillon Ngo 26742a7335 Merge branch 'dillon/46.3.1-warehouse-storage-charges-fixes' into 'master'
Fix a problem with manual payment options not available when paying for invoices with warehouse storage charges

See merge request CIEFWorldwideSdnBhd/shipping-portal!188
2024-01-15 16:28:47 +00:00
Dillon Ngo 0e822ff1dd UI/UX amendment for order page when paying with storage invoice 2024-01-15 08:38:13 +08:00
Dillon Ngo 085b8b349f UI/UX amendment for order page when paying with storage invoice 2024-01-15 06:41:41 +08:00
edmondlang c1164397e8 fix errors diplaying in console in settings - warehouse 2024-01-14 22:56:45 +08:00
Dillon Ngo 3c16253795 Fix manual payment not showing in single order page when storage invoice is present 2024-01-14 09:33:31 +08:00
edmondlang 18a201611e add bind key for postcode array 2024-01-11 17:50:43 +08:00
edmondlang 7e754a9c7b add search postcode 2024-01-11 17:37:01 +08:00
edmondlang beb200fd6a Merge branch 'steve/account-mapping' of https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal 2024-01-11 10:59:40 +08:00
Steve Ng f0cb7828c3 invoice date for shipping portal when export invoice to autocount 2024-01-10 19:10:21 +08:00
Dillon Ngo 1956a4095d Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2024-01-08 17:49:42 +08:00
Dillon Ngo 71559701f4 Merge branch 'dillon/46.3-warehouse-storage-charges-fixes' into 'master'
Fix attempt on storage invoice pdf not able to be downloaded by user

See merge request CIEFWorldwideSdnBhd/shipping-portal!187
2024-01-05 21:55:59 +00:00
Dillon Ngo 9ceba30680 Fix attempt on storage invoice pdf not able to be downloaded by user 2024-01-06 05:53:59 +08:00
Dillon Ngo 3b2d06ed8f Fix attempt on storage invoice pdf not able to be downloaded by user 2024-01-06 05:52:55 +08:00
Dillon Ngo 81e04e9cb0 Fix attempt on storage invoice pdf not able to be downloaded by user 2024-01-06 05:52:26 +08:00
Dillon Ngo 286ca64413 Merge branch 'dillon/46.3-warehouse-storage-charges-fixes' into 'master'
Fix attempt on storage invoice pdf not able to be downloaded by user

See merge request CIEFWorldwideSdnBhd/shipping-portal!186
2024-01-05 21:11:33 +00:00
Dillon Ngo 51a96812fd Fix attempt on storage invoice pdf not able to be downloaded by user 2024-01-06 05:10:50 +08:00
Dillon Ngo 07459b9a1b Merge branch 'dillon/46.3-warehouse-storage-charges-fixes' into 'master'
Fix attempt on storage invoice pdf not able to be downloaded by user

See merge request CIEFWorldwideSdnBhd/shipping-portal!185
2024-01-05 21:08:00 +00:00
Dillon Ngo ee69f9123c Fix attempt on storage invoice pdf not able to be downloaded by user 2024-01-06 05:05:59 +08:00
Steve Ng 7a2ed92147 change posting date to invoice date for shipping portal when export invoice to autocount 2024-01-05 11:10:42 +08:00
Dillon Ngo 894481591d Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2024-01-04 13:52:11 +08:00
Dillon Ngo e5580c2abd Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2024-01-04 02:50:55 +08:00
Dillon Ngo 47ee304da1 Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2024-01-04 02:10:04 +08:00
Steve Ng 6427e2996f change posting date to invoice date for shipping portal when export invoice to autocount 2024-01-03 09:05:07 +08:00
Dillon Ngo f54aae5dc9 Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2024-01-03 05:39:43 +08:00
Dillon Ngo f0ef9a5602 Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2023-12-30 19:13:40 +08:00
Dillon Ngo 4d682c2ac6 Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2023-12-26 04:21:46 +08:00
Dillon Ngo 8ea00e0383 Proof of concept - Vue Polling - Performance Improvement and tweaking for better user experience 2023-12-24 21:59:41 +08:00
Dillon Ngo 18df1c0c7b Proof of concept - Vue Polling a workaround for AWS API Gateway limitation 2023-12-20 14:47:56 +08:00
Dillon Ngo 359c103d01 Code sync from Exchange 2023-12-17 17:23:04 +08:00
120 changed files with 5287 additions and 113 deletions
@@ -0,0 +1,11 @@
<?php
namespace App\Classes\Exceptions;
use App\Classes\ValueObjects\Constants\HttpStatus;
final class JobResourceNotFoundException extends ServiceApiException {
public function __construct(?string $message = null) {
parent::__construct($message ?? 'Unable to find the requested resource', HttpStatus::RESOURCE_NOT_FOUND);
}
}
@@ -15,6 +15,8 @@ use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\Facades\DB;
use App\Classes\Exceptions\JobResourceNotFoundException;
use Illuminate\Support\Facades\Log;
abstract class AbstractControllerLogic
{
@@ -62,6 +64,19 @@ abstract class AbstractControllerLogic
return $response;
} catch (ErrorException|GeneralExceptions|TypeError $exception){
if ($exception instanceof JobResourceNotFoundException) {
Log::error(sprintf(
"Uncaught exception '%s' with message '%s' in %s:%d",
get_class($exception),
$exception->getMessage(),
$exception->getTrace()[0]['file'],
$exception->getTrace()[0]['line']
));
}
else{
Log::error($exception);
}
return (new ApiResponseObject($this->getNotificationTitle().' failed', $exception->getMessage(),
!in_array($exception->getCode(), [0, 42000]) ? $exception->getCode() : HttpStatus::SERVER_ERROR))->handler();
@@ -6,6 +6,8 @@ namespace App\Classes\General\Eloquent;
use App\Classes\Exceptions\ResourceNotFoundException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use App\Classes\Exceptions\JobResourceNotFoundException;
abstract class AbstractFetchRecord extends AbstractGetRecord
{
@@ -26,12 +28,18 @@ abstract class AbstractFetchRecord extends AbstractGetRecord
* @return Model
* @throws ResourceNotFoundException
*/
public function getResults(Builder $query): Model {
public function getResults(Builder $query, array $param = []): Model {
if(!$query->exists()){
throw new ResourceNotFoundException('Unable to find any record based on the criteria provided');
$table = $query->getModel()->getTable();
if($table ==='job_results'){
throw new JobResourceNotFoundException('Unable to find any job based on the criteria provided');
}
else{
throw new ResourceNotFoundException('Unable to find any record based on the criteria provided');
}
}
return $query->first();
}
}
}
@@ -50,9 +50,9 @@ abstract class AbstractGetRecord
* @param array $filters
* @return mixed
*/
public function handler(array $filters){
public function handler(array $filters, array $params = []){
$this->filters = collect($filters);
return $this->getResults($this->applyFiltersToQuery());
return $this->getResults($this->applyFiltersToQuery(), $params);
}
@@ -65,6 +65,6 @@ abstract class AbstractGetRecord
* @param Builder $query
* @return mixed
*/
abstract function getResults(Builder $query);
abstract function getResults(Builder $query, array $params = []);
}
}
@@ -16,11 +16,10 @@ abstract class AbstractListRecord extends AbstractGetRecord
* @return mixed
* @throws MalformedRequestException
*/
public function execute(array $filters = []){
public function execute(array $filters = [], array $param = []){
try{
return $this->handler($filters);
return $this->handler($filters, $param);
} catch (QueryException $exception){
throw new MalformedRequestException('Unable to fetch the list of records due to unexpected error');
@@ -32,7 +31,7 @@ abstract class AbstractListRecord extends AbstractGetRecord
* @param Builder $query
* @return mixed
*/
public function getResults(Builder $query) {
public function getResults(Builder $query, array $param = []) {
$filters = $this->getDecorationFilters();
if($filters->has('order_by')){
@@ -44,8 +43,12 @@ abstract class AbstractListRecord extends AbstractGetRecord
}
//dd($query->toSql());
return $filters->has('per_page') ? $query->paginate($filters->get('per_page')) : $query->get();
if(!empty($param)){
return $filters->has('per_page') ? $query->paginate($filters->get('per_page'), ['*'], 'page', $param['page']) : $query->get(); //page data from query parameters e.g ?page=1
}
else{
return $filters->has('per_page') ? $query->paginate($filters->get('per_page')) : $query->get();
}
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class JobId implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('job_id', $value);
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class PostcodeLike implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('postcode', 'LIKE', '%'.$value.'%');
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class RequestSignature implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->where('request_signature', $value);
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
class ResultNotNull implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return Builder|mixed
*/
public static function apply(Builder $builder, $value)
{
return $builder->whereNotNull('result');
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Classes\General\Eloquent\Filters;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Log;
class WithContainersPackages implements Filter
{
/**
* @param Builder $builder
* @param $value
* @return mixed
*/
public static function apply(Builder $builder, $value)
{
// Log::info('WithContainersPackages: '.$value);
// return $builder->with(['containers', 'packages']);
return null;
}
}
+9
View File
@@ -2,6 +2,7 @@
namespace App\Classes\General;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
@@ -40,4 +41,12 @@ class Helper
}
}
}
/**
* @param ResourceCollection $collection
* @return array
*/
static function collectionResponse(ResourceCollection $collection){
return json_decode($collection->response()->getContent(), true);
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace App\Classes\Jobs;
use App\Classes\Modules\PackingLists\Processors\ListPackingListsJobProcessor;
use App\Classes\Modules\Jobs\DataTransferObjects\ListGenericJobObject;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ListPackingListsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 900;
/** @var ListGenericJobObject */
private $listGenericJobObject;
/**
* ListPackingListsJob constructor.
* @param ListGenericJobObject $listGenericJobObject
*/
public function __construct(ListGenericJobObject $listGenericJobObject)
{
$this->listGenericJobObject = $listGenericJobObject;
}
public function handle()
{
$rawPayload = $this->job->payload();
if(isset($rawPayload['data']['commandName'])){
$this->listGenericJobObject->setJobCommandName($rawPayload['data']['commandName']);
}
if(isset($rawPayload['data']['command'])){
$this->listGenericJobObject->setJobCommand($rawPayload['data']['command']);
}
$result = (App()->make(ListPackingListsJobProcessor::class))->execute($this->listGenericJobObject);
}
}
@@ -0,0 +1,76 @@
<?php
namespace App\Classes\Modules\Companies\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Companies\Services\FetchesCompany;
use App\Classes\Modules\Companies\Services\UpdatesCompanyModuleIsCreditTerm;
use App\Classes\Modules\Companies\Standards\Rules\CanUpdateCompanyModule;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class UpdateCompanyCreditTermStatusLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Update Company Credit Term Status',
'message' => 'You have successfully updated Company Credit Term Status'
];
}
/** @var FetchesCompany */
private $fetchesCompany;
/** @var UpdatesCompanyModuleIsCreditTerm */
private $updatesCompanyModuleIsCreditTerm;
/** @var CanUpdateCompanyModule */
private $canUpdateCompanyModule;
/**
* UpdateCompanyCreditTermStatusLogic constructor.
* @param FetchesCompany $fetchesCompany
* @param UpdatesCompanyModuleIsCreditTerm $updatesCompanyModuleIsCreditTerm
* @param CanUpdateCompanyModule $canUpdateCompanyModule
*/
public function __construct(FetchesCompany $fetchesCompany, UpdatesCompanyModuleIsCreditTerm $updatesCompanyModuleIsCreditTerm, CanUpdateCompanyModule $canUpdateCompanyModule)
{
$this->fetchesCompany = $fetchesCompany;
$this->updatesCompanyModuleIsCreditTerm = $updatesCompanyModuleIsCreditTerm;
$this->canUpdateCompanyModule = $canUpdateCompanyModule;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request) : JsonResponse
{
$this->canUpdateCompanyModule->passes();
$company = $this->fetchesCompany->execute(['id' => $request->route('id')]);
$isCreditTerm = $company->companyModules()->first()->connections()->first()->is_credit_term;
if ($isCreditTerm == 1) {
$isCreditTerm = 0;
} else {
$isCreditTerm = 1;
}
$this->updatesCompanyModuleIsCreditTerm->execute($company->companyModules()->first()->connections()->first(), $isCreditTerm);
Log::info(Auth::user()->email." updated credit term status for customer with id: " .$request->route('id'). " to status " . $isCreditTerm);
$result = ['is_credit_term' => $isCreditTerm];
return $this->response(['data' => $result]);
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Classes\Modules\Companies\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Models\CompanyConnection;
class UpdatesCompanyModuleIsCreditTerm extends AbstractUpdateRecord
{
/**
* @param CompanyConnection $model
* @param int $isCreditTerm
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(CompanyConnection $model, int $isCreditTerm)
{
$model->is_credit_term = $isCreditTerm;
return $this->handler($model);
}
}
@@ -0,0 +1,53 @@
<?php
namespace App\Classes\Modules\Companies\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Companies\DataTransferObjects\CompanyObject;
use App\Classes\ValueObjects\Constants\RoleTypes;
class CanUpdateCompanyModule extends AbstractRule
{
/**
* CanUpdateCompanyModule constructor.
*/
public function __construct()
{
}
/**
* @return bool
*/
protected function authorized($object): bool
{
$user = Auth()->user();
if($user){
$roleToCheck = Auth()->user()->type;
if (in_array($roleToCheck, RoleTypes::ADMIN_ROLES)) {
return true;
}
}
return false;
}
/**
* @param $object
* @return bool
* @throws \App\Classes\Exceptions\RequestValidationException
*/
protected function validators($object): bool
{
return true;
}
/**
* @param CompanyObject $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Classes\Modules\Imports\Services;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
class GenericImport implements ToCollection, WithHeadingRow
{
function headingRow(): int { return 1; }
public $rows;
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$this->rows = $collection;
}
}
@@ -0,0 +1,52 @@
<?php
namespace App\Classes\Modules\Jobs\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Jobs\Processors\FetchesJobResultProcessor;
use App\Http\Resources\JobResultResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchJobResultLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Retrieved Data',
'message' => 'You have successfully retrieved data'
];
}
/** @var FetchesJobResultProcessor */
private $fetchesJobResultProcessor;
/**
* FetchJobResultLogic constructor.
* @param FetchesJobResultProcessor $fetchesJobResultProcessor
*/
public function __construct(FetchesJobResultProcessor $fetchesJobResultProcessor)
{
$this->fetchesJobResultProcessor = $fetchesJobResultProcessor;
}
/**
* @param Request $request
* @return JsonResponse
* @throws \App\Classes\Exceptions\AccessForbiddenException
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\RequestValidationException
*/
public function logic(Request $request) : JsonResponse
{
$query = $this->fetchesJobResultProcessor->execute($request);
return $this->resourceResponse(new JobResultResource($query));
}
}
@@ -0,0 +1,118 @@
<?php
namespace App\Classes\Modules\Jobs\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class ListGenericJobObject implements DataTransferObject
{
/** @var string */
private $name;
/** @var array */
private $payload;
/** @var string */
private $jobId;
/** @var string */
private $requestSignature;
/** @var string */
private $resultSignature;
/** @var object */
private $userInfo;
/** @var string */
private $jobCommandName;
/** @var string */
private $jobCommand;
public function __construct(string $name, array $payload, string $requestSignature, ?string $resultSignature, string $jobId, object $userInfo = null)
{
$this->name = $name;
$this->payload = $payload;
$this->jobId = $jobId;
$this->requestSignature = $requestSignature;
$this->resultSignature = $resultSignature;
$this->userInfo = $userInfo;
}
/**
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* @return array
*/
public function getPayload(): array
{
return $this->payload;
}
/**
* @return string
*/
public function getJobId(): string
{
return $this->jobId;
}
/**
* @return string
*/
public function getRequestSignature(): string
{
return $this->requestSignature;
}
/**
* @return string
*/
public function getResultSignature(): ?string
{
return $this->resultSignature;
}
/**
* @return object
*/
public function getUserInfo(): object
{
return $this->userInfo;
}
/**
* @return string
*/
public function getJobCommandName(): string
{
return $this->jobCommandName;
}
/**
* @return string
*/
public function getJobCommand(): string
{
return $this->jobCommand;
}
public function setJobCommandName(string $jobCommandName)
{
$this->jobCommandName = $jobCommandName;
}
public function setJobCommand(string $jobCommand)
{
$this->jobCommand = $jobCommand;
}
}
@@ -0,0 +1,60 @@
<?php
namespace App\Classes\Modules\Jobs\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class UpdateJobResultObject implements DataTransferObject
{
/** @var string */
private $result;
/** @var string */
private $resultSignature;
/** @var string */
private $jobCommandName;
/** @var string */
private $jobCommand;
public function __construct(string $result, string $resultSignature, string $jobCommandName, string $jobCommand)
{
$this->result = $result;
$this->resultSignature = $resultSignature;
$this->jobCommandName = $jobCommandName;
$this->jobCommand = $jobCommand;
}
/**
* @return string
*/
public function getResult(): string
{
return $this->result;
}
/**
* @return array
*/
public function getResultSignature(): string
{
return $this->resultSignature;
}
/**
* @return string
*/
public function getJobCommandName(): string
{
return $this->jobCommandName;
}
/**
* @return string
*/
public function getJobCommand(): string
{
return $this->jobCommand;
}
}
@@ -0,0 +1,47 @@
<?php
namespace App\Classes\Modules\Jobs\Processors;
use App\Classes\Exceptions\JobResourceNotFoundException;
use App\Classes\Modules\Jobs\Services\FetchesJobResult;
use Illuminate\Http\Request;
class FetchesJobResultProcessor
{
/** @var FetchesJobResult */
private $fetchesJobResult;
/**
* FetchesJobResultProcessor constructor.
* @param FetchesJobResult $fetchesJobResult
*/
public function __construct(FetchesJobResult $fetchesJobResult)
{
$this->fetchesJobResult = $fetchesJobResult;
}
/**
* @param Request $request
* @return Model
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\JobResourceNotFoundException
* @throws \App\Classes\Exceptions\ResourceNotFoundException
*/
public function execute(Request $request){
$res1 = $this->fetchesJobResult->execute(['job_id' => $request->route('job_id')]);
if($request->route('is_last')){
$res2 = $this->fetchesJobResult->execute(['request_signature' => $res1->request_signature, 'result_not_null' => true, 'order_by_id_desc' => true]);
return $res2;
}
if(!$res1->result){
throw new JobResourceNotFoundException('Unable to find any job based on the criteria provided');
}
return $res1;
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Classes\Modules\Jobs\Processors;
use App\Classes\Modules\Jobs\Services\UpdatesJobResult;
use App\Classes\Modules\Jobs\Services\FetchesJobResult;
use App\Classes\Exceptions\JobResourceNotFoundException;
use App\Classes\Modules\Jobs\DataTransferObjects\ListGenericJobObject;
use App\Classes\Modules\Jobs\DataTransferObjects\UpdateJobResultObject;
class UpdateJobResultProcessor
{
/** @var FetchesJobResult */
private $fetchesJobResult;
/** @var UpdatesJobResult */
private $updatesJobResult;
/**
* UpdateJobResultProcessor constructor.
* @param FetchesJobResult $fetchesJobResult
* @param UpdatesJobResult $updatesJobResult
*/
public function __construct(FetchesJobResult $fetchesJobResult, UpdatesJobResult $updatesJobResult)
{
$this->fetchesJobResult = $fetchesJobResult;
$this->updatesJobResult = $updatesJobResult;
}
/**
* @param ListGenericJobObject $listGenericJobObject
* @param array $resultCurrent
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\JobResourceNotFoundException
*/
public function execute(ListGenericJobObject $listGenericJobObject, $resultCurrent) {
$jobResultCurrent = $this->fetchesJobResult->execute(['job_id' => $listGenericJobObject->getJobId()]);
$resultCurrentJson = json_encode($resultCurrent);
$resultSignatureCurrent = md5($resultCurrentJson);
try{
$jobResultExisting = $this->fetchesJobResult->execute(['request_signature' => $jobResultCurrent->request_signature, 'result_not_null' => true, 'order_by_id_desc' => true]);
$resultSignatureExisting = $jobResultExisting->result_signature;
//if($resultSignatureExisting != $resultSignatureCurrent){
$this->updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $listGenericJobObject->getJobCommandName(), $listGenericJobObject->getJobCommand());
//}
} catch (JobResourceNotFoundException $exception){
$this->updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $listGenericJobObject->getJobCommandName(), $listGenericJobObject->getJobCommand());
}
}
private function updateJobResult($jobResultCurrent, $resultCurrentJson, $resultSignatureCurrent, $jobCommandName, $jobCommand){
$updateJobResultObject = new UpdateJobResultObject(
$resultCurrentJson,
$resultSignatureCurrent,
$jobCommandName,
$jobCommand
);
$create = $this->updatesJobResult->execute($jobResultCurrent, $updateJobResultObject);
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Classes\Modules\Jobs\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Models\JobResult;
use App\Classes\Modules\Jobs\DataTransferObjects\ListGenericJobObject;
class CreatesJobResult extends AbstractUpdateRecord
{
/**
* @param ListGenericJobObject $listGenericJobObject
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(ListGenericJobObject $listGenericJobObject)
{
$model = new JobResult();
$model->job_id = $listGenericJobObject->getJobId();
$model->request_signature = $listGenericJobObject->getRequestSignature();
$model->result_signature = $listGenericJobObject->getResultSignature();
$model->url = $listGenericJobObject->getName();
return $this->handler($model);
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Jobs\Services;
use App\Classes\General\Eloquent\AbstractFetchRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\JobResult;
class FetchesJobResult extends AbstractFetchRecord
{
/** @var JobResult */
private $repository;
/**
* FetchesJobResult constructor.
* @param JobResult $repository
*/
public function __construct(JobResult $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
public function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Classes\Modules\Jobs\Services;
use App\Classes\General\Eloquent\AbstractListRecord;
use Illuminate\Database\Eloquent\Builder;
use App\Models\JobResult;
class ListsJobResult extends AbstractListRecord
{
/** @var JobResult */
private $repository;
/**
* ListsJobResult constructor.
* @param JobResult $repository
*/
public function __construct(JobResult $repository)
{
$this->repository = $repository;
}
/**
* @return Builder
*/
function getRepository(): Builder
{
return $this->repository->newQuery();
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Classes\Modules\Jobs\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Classes\Modules\Jobs\DataTransferObjects\UpdateJobResultObject;
use App\Models\JobResult;
class UpdatesJobResult extends AbstractUpdateRecord
{
/**
* @param JobResult $model
* @param UpdateJobResultObject $updateJobResultObject
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(JobResult $model, UpdateJobResultObject $updateJobResultObject) {
$model->result = $updateJobResultObject->getResult();
$model->result_signature = $updateJobResultObject->getResultSignature();
$model->job_command_name = $updateJobResultObject->getJobCommandName();
$model->job_command = $updateJobResultObject->getJobCommand();
return $this->handler($model);
}
}
@@ -0,0 +1,78 @@
<?php
namespace App\Classes\Modules\PackingLists\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Jobs\ListPackingListsJob;
use App\Classes\Modules\Jobs\DataTransferObjects\ListGenericJobObject;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Classes\Modules\Jobs\Services\CreatesJobResult;
class ListPackingListsJobLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Job Retrieving PackingLists',
'message' => 'You have successfully submit a job to retrieve a list of PackingLists'
];
}
/** @var CreatesJobResult */
private $createsJobResult;
/**
* ListPackingListsJobLogic constructor.
* @param CreatesJobResult $createsJobResult
*/
public function __construct(CreatesJobResult $createsJobResult)
{
$this->createsJobResult = $createsJobResult;
}
/**
* @param Request $request
* @return JsonResponse
*/
public function logic(Request $request) : JsonResponse
{
$jobId = uniqid();
$user = Auth::user();
$userInfo = (object) [
// 'email' => $user->email,
'type' => $user->type,
];
$userInfoJson = json_encode($userInfo);
$requestSignature = md5($userInfoJson . $request->fullUrl());
$listGenericJobObject = new ListGenericJobObject(
$request->fullUrl(),
$request->all(),
$requestSignature,
null,
$jobId,
$userInfo
);
ListPackingListsJob::dispatch($listGenericJobObject)->onQueue('high_priority');
$result = [];
$result['job_id'] = $jobId;
$this->createsJobResult->execute($listGenericJobObject);
return $this->response(['data' => $result]);
}
}
@@ -6,8 +6,8 @@ namespace App\Classes\Modules\PackingLists\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\PackingLists\Services\ListsPackingLists;
use App\Classes\Modules\PackingLists\Standards\Rules\CanListPackingLists;
use App\Http\Resources\PackingListNullOrderResource;
use App\Http\Resources\PackingListResource;
use App\Http\Resources\PackingListWithStorageResource;
use ErrorException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -54,7 +54,14 @@ class ListPackingListsLogic extends AbstractControllerLogic
$query = $this->listsPackingLists->execute($this->listsPackingLists->deserializeFilters($request->input('filters')));
return $this->collectionResponse(PackingListResource::collection($query));
if($request->input('storages')){
foreach ($query->items() as $item) {
$item['storages'] = $request->input('storages');
}
return $this->collectionResponse(PackingListWithStorageResource::collection($query));
}
else{
return $this->collectionResponse(PackingListResource::collection($query));
}
}
}
@@ -0,0 +1,58 @@
<?php
namespace App\Classes\Modules\PackingLists\Processors;
use App\Classes\Modules\PackingLists\Services\ListsPackingLists;
use App\Classes\Modules\Jobs\Processors\UpdateJobResultProcessor;
use App\Classes\General\Helper;
use App\Classes\Modules\Jobs\DataTransferObjects\ListGenericJobObject;
use App\Http\Resources\ListPackingListJobResource;
use App\Http\Resources\ListPackingListDetailsJobResource;
class ListPackingListsJobProcessor
{
/** @var ListsPackingLists */
private $listsPackingLists;
/** @var UpdateJobResultProcessor */
private $updateJobResultProcessor;
/**
* ListPackingListsJobProcessor constructor.
* @param ListsPackingLists $listsPackingLists
* @param UpdateJobResultProcessor $updateJobResultProcessor
*/
public function __construct(ListsPackingLists $listsPackingLists, UpdateJobResultProcessor $updateJobResultProcessor)
{
$this->listsPackingLists = $listsPackingLists;
$this->updateJobResultProcessor = $updateJobResultProcessor;
}
/**
* @param ListGenericJobObject $listGenericJobObject
* @return void
* @throws \App\Classes\Exceptions\MalformedRequestException
* @throws \App\Classes\Exceptions\JobResourceNotFoundException
*/
public function execute(ListGenericJobObject $listGenericJobObject) {
// $query = $this->listsPackingLists->execute($this->listsPackingLists->deserializeFilters($listGenericJobObject->getPayload()['filters']), ['page' => $listGenericJobObject->getPayload()['page'], 'with_containers_packages' => true]);
$query = $this->listsPackingLists->execute($this->listsPackingLists->deserializeFilters($listGenericJobObject->getPayload()['filters']), ['page' => $listGenericJobObject->getPayload()['page']]);
// $query = $this->listsPackingLists->execute(array_merge($this->listsPackingLists->deserializeFilters($listGenericJobObject->getPayload()['filters']), ['page' => $listGenericJobObject->getPayload()['page'], 'with_containers_packages' => true]));
foreach ($query->items() as &$item) {
$item['userInfo'] = $listGenericJobObject->getUserInfo();
}
$filtersArray = json_decode($listGenericJobObject->getPayload()['filters'], true);
if(isset($filtersArray['does_not_have_transaction_type'])){ //For Pending Invoice query
$resultCurrent = Helper::collectionResponse(ListPackingListDetailsJobResource::collection($query));
$this->updateJobResultProcessor->execute($listGenericJobObject, $resultCurrent);
}
else{
$resultCurrent = Helper::collectionResponse(ListPackingListJobResource::collection($query));
$this->updateJobResultProcessor->execute($listGenericJobObject, $resultCurrent);
}
}
}
@@ -30,7 +30,7 @@ class DeleteTransactionLogic extends AbstractControllerLogic
/**
* SuspendTransactionLogic constructor.
* DeleteTransactionLogic constructor.
* @param FetchesTransaction $fetchesTransaction
* @param UpdatesTransactionStatus $updatesTransactionStatus
*/
@@ -5,6 +5,7 @@ namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\Services\ListsTransactions;
use App\Http\Resources\TransactionWithStorageResource;
use App\Http\Resources\TransactionResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -42,15 +43,18 @@ class ListTransactionsLogic extends AbstractControllerLogic
$query = $this->listsTransactions->execute($this->listsTransactions->deserializeFilters($request->input('filters')));
if($request->input('storages')){
foreach ($query->items() as &$item) {
foreach ($query->items() as $item) {
$transactionId = $item['id'];
$filteredStorages = array_filter($request->input('storages'), function ($storage) use ($transactionId) {
return isset($storage['parentInvoiceId']) && $storage['parentInvoiceId'] == $transactionId;
});
$item['storages'] = $filteredStorages;
}
//return $this->collectionResponse(TransactionWithStorageResource::collection($query));
}
return $this->collectionResponse(TransactionResource::collection($query));
// else{
// return $this->collectionResponse(TransactionResource::collection($query));
//
return $this->collectionResponse(TransactionWithStorageResource::collection($query));
}
}
@@ -0,0 +1,67 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Processors\CreateStorageInvoiceDocTransactionFixProcessor;
use App\Classes\Modules\Transactions\Standards\Rules\CanRegenerateStorageInvoice;
use App\Classes\ValueObjects\Constants\TransactionType;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class RegenerateSingleStorageInvoiceTransactionLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Regenerate Storage Invoice',
'message' => 'You have successfully regenerated storage invoice'
];
}
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var CreateStorageInvoiceDocTransactionFixProcessor */
private $createStorageInvoiceDocTransactionProcessor;
/** @var CanRegenerateStorageInvoice */
private $canRegenerateStorageInvoice;
/**
* @param CreatesDocument $createsDocument
*/
public function __construct(FetchesTransaction $fetchesTransaction, CreateStorageInvoiceDocTransactionFixProcessor $createStorageInvoiceDocTransactionProcessor, CanRegenerateStorageInvoice $canRegenerateStorageInvoice)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->createStorageInvoiceDocTransactionProcessor = $createStorageInvoiceDocTransactionProcessor;
$this->canRegenerateStorageInvoice = $canRegenerateStorageInvoice;
}
public function logic(Request $request) : JsonResponse
{
$this->canRegenerateStorageInvoice->passes();
$invoice = $this->fetchesTransaction->execute(['id' => $request->route('invoice_id')]);
$packingList = $invoice->owner;
$order = $packingList->owner;
$invoices = $order->transactions()->where('transactions.type', TransactionType::STORAGE_INVOICE)->get();
foreach ($invoices as $invoice){
$invoice->documents()->delete();
}
$this->createStorageInvoiceDocTransactionProcessor->execute($packingList, true);
Log::info(Auth::user()->email." regenerate storage invoice for transaction with id ".$request->route('invoice_id'));
return $this->response([]);
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Transactions\DataTransferObjects\WaiveTransactionObject;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionIsWaived;
use App\Classes\Modules\Transactions\Standards\Rules\CanWaiveTransaction;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class WaiveTransactionLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Waive Transaction',
'message' => 'You have successfully waived the transaction'
];
}
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var UpdatesTransactionIsWaived */
private $updatesTransactionIsWaived;
/** @var CanWaiveTransaction */
private $canWaiveTransaction;
/**
* WaiveTransactionLogic constructor.
* @param FetchesTransaction $fetchesTransaction
* @param UpdatesTransactionIsWaived $updatesTransactionIsWaived
* @param CanWaiveTransaction $canWaiveTransaction
*/
public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionIsWaived $updatesTransactionIsWaived, CanWaiveTransaction $canWaiveTransaction)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->updatesTransactionIsWaived = $updatesTransactionIsWaived;
$this->canWaiveTransaction = $canWaiveTransaction;
}
public function logic(Request $request) : JsonResponse
{
$transaction = $this->fetchesTransaction->execute(['id' => $request->route('id')]);
$object = new WaiveTransactionObject($transaction->id, $transaction->bill_no, $transaction->type, $transaction->status);
$this->canWaiveTransaction->passes($object);
$this->updatesTransactionIsWaived->execute($transaction);
Log::info(Auth::user()->email." waive storage invoice charges with bill_no: ".$transaction->bill_no);
return $this->response([]);
}
}
@@ -0,0 +1,61 @@
<?php
namespace App\Classes\Modules\Transactions\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class WaiveTransactionObject implements DataTransferObject
{
/** @var int */
private $id;
/** @var string */
private $billNo;
/** @var int */
private $type;
/** @var int */
private $status;
public function __construct(int $id, string $billNo, int $type, int $status)
{
$this->id = $id;
$this->billNo = $billNo;
$this->type = $type;
$this->status = $status;
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @return string
*/
public function getBillNo(): string
{
return $this->billNo;
}
/**
* @return int
*/
public function getType(): int
{
return $this->type;
}
/**
* @return int
*/
public function getStatus(): int
{
return $this->status;
}
}
@@ -24,7 +24,7 @@ use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PackageType;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransactionDetailType;
use App\Http\Resources\TransactionResource;
use App\Http\Resources\TransactionWithStorageResource;
use App\Models\Order;
use App\Models\PackingList;
use App\Models\Transaction;
@@ -200,6 +200,11 @@ class CheckStorageInvoiceTransactionProcessor
$this->createStorageInvoiceTransactionDetails($storageInvoice, $destinationWarehousePackage, $cbm, $pricePerCBM, $resultNumberOfDaysExceeded);
}
else if($storageInvoice){
//Additional handling for giving selected storage invoice a waiver
if(isset($storageInvoice->is_waived) && $storageInvoice->is_waived){
$pricePerCBM = 0;
$price_cbm = $pricePerCBM * $cbm * $resultNumberOfDaysExceeded;
}
//Additional handling for calculation of numberOfDaysExceeded in the event of the storage already paid
$paymentStorageTransaction = $storageInvoice->transactions()->where('transactions.type', TransactionType::PAYMENT)->where('transactions.status', ApprovalStatus::APPROVED)->first();
@@ -216,10 +221,12 @@ class CheckStorageInvoiceTransactionProcessor
Log::channel('storage_invoices')->info('Update $transaction->id: '.$transaction->id);
Log::channel('storage_invoices')->info('$storageInvoice->status: '.$storageInvoice->status);
Log::channel('storage_invoices')->info('price_cbm: '.$price_cbm."-".gettype($price_cbm));
Log::channel('storage_invoices')->info('amount: '.$amount);
$proceedToUpdate = false;
if(abs($price_cbm - $amount) > $epsilon && $storageInvoice->status !== ApprovalStatus::COMPLETED){
$paymentTransactions = $storageInvoice->transactions()->where('transactions.type', TransactionType::PAYMENT)->where('transactions.status', ApprovalStatus::PENDING_SUBMISSION)->get();
if(!$isBackDoorCheck){
if(count($paymentTransactions) > 0){
$this->updatePaymentTransactionViaNonGroupPayment($paymentTransactions);
@@ -229,6 +236,11 @@ class CheckStorageInvoiceTransactionProcessor
}
}
$proceedToUpdate = true;
}
if($proceedToUpdate || ($pricePerCBM == 0 && $amount != 0)){
Log::channel('storage_invoices')->info('proceedToUpdate');
$storageInvoice = $this->updateStorageInvoiceTransaction($storageInvoice, $price_cbm);
$invoiceTransactionDetails = $storageInvoice->transactionDetails()->first();
$this->updateStorageInvoiceTransactionDetails($invoiceTransactionDetails, $destinationWarehousePackage, $cbm, $pricePerCBM, $resultNumberOfDaysExceeded);
@@ -247,7 +259,7 @@ class CheckStorageInvoiceTransactionProcessor
'currentDate' => $resultCurrentDate,
'cbm' => $cbm,
'pricePerCBM' => $pricePerCBM,
'storageInvoice' => new TransactionResource($storageInvoice)
'storageInvoice' => new TransactionWithStorageResource($storageInvoice)
];
return $result;
}
@@ -0,0 +1,79 @@
<?php
namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Notifications\InvoiceIssuedEmail;
use App\Models\Document;
use App\Models\PackingList;
use App\Models\Transaction;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
class CreateStorageInvoiceDocTransactionFixProcessor
{
/** @var UpdatesTransactionStatus */
private $updatesTransactionStatus;
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFiles;
/**
* @param UpdatesTransactionStatus $updatesTransactionStatus
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFiles
*/
public function __construct(UpdatesTransactionStatus $updatesTransactionStatus, CreatesDocument $createsDocument, CreatesFiles $createsFiles)
{
$this->updatesTransactionStatus = $updatesTransactionStatus;
$this->createsDocument = $createsDocument;
$this->createsFiles = $createsFiles;
}
/**
* @throws MalformedRequestException
*/
public function execute(PackingList $packingList)
{
/** @var Transaction $invoice_transaction */
$invoice_transaction = $packingList->transactions()->where('transactions.type', TransactionType::STORAGE_INVOICE)->whereIn('status', [ApprovalStatus::COMPLETED])->first();
// $this->updatesTransactionStatus->execute($invoice_transaction, ApprovalStatus::APPROVED);
$transaction_invoice_pdf = LaravelMpdf::loadView('pages.pdfs.shipping_invoice', ['invoice_transaction' => $invoice_transaction]);
$document_object = new DocumentObject(
DocumentType::STORAGE_INVOICE,
[chunk_split('data:application/pdf;base64,'.base64_encode($transaction_invoice_pdf->output()))],
'',
ApprovalStatus::COMPLETED,
'storage_invoice'
);
// $invouce_transaction_document = $invoice_transaction->documents()->where('document_type', DocumentType::STORAGE_INVOICE)->first();
// if(!$invouce_transaction_document){
/** @var Document $document */
$document = $this->createsDocument->execute($invoice_transaction, $document_object);
$this->createsFiles->execute($document, $document_object);
// $user = $packingList->owner->companyModule->employees()->first();
// if(app()->environment(['production'])) {
// $user->notify(new InvoiceIssuedEmail($user, $packingList));
// }
// }
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Classes\Modules\Transactions\Services;
use App\Classes\General\Eloquent\AbstractUpdateRecord;
use App\Models\Transaction;
class UpdatesTransactionIsWaived extends AbstractUpdateRecord
{
/**
* @param Transaction $model
* @return \Illuminate\Database\Eloquent\Model
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function execute(Transaction $model)
{
$model->is_waived = 1;
return $this->handler($model);
}
}
@@ -0,0 +1,47 @@
<?php
namespace App\Classes\Modules\Transactions\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\ValueObjects\Constants\RoleTypes;
class CanRegenerateStorageInvoice extends AbstractRule
{
/**
* @return bool
*/
protected function authorized($object): bool
{
$user = Auth()->user();
if($user){
$roleToCheck = Auth()->user()->type;
if (in_array($roleToCheck, RoleTypes::ADMIN_ROLES)) {
return true;
}
}
return false;
}
/**
* @param $object
* @return bool
* @throws \App\Classes\Exceptions\RequestValidationException
*/
protected function validators($object): bool
{
return true;
}
/**
* @param $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,58 @@
<?php
namespace App\Classes\Modules\Transactions\Standards\Rules;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Transactions\DataTransferObjects\WaiveTransactionObject;
use App\Classes\Modules\Transactions\Standards\Validators\WaiveTransactionValidation;
use App\Classes\ValueObjects\Constants\RoleTypes;
class CanWaiveTransaction extends AbstractRule
{
/** @var WaiveTransactionValidation */
private $waiveTransactionValidation;
public function __construct(WaiveTransactionValidation $waiveTransactionValidation)
{
$this->waiveTransactionValidation = $waiveTransactionValidation;
}
/**
* @return bool
*/
protected function authorized($object): bool
{
$user = Auth()->user();
if($user){
$roleToCheck = Auth()->user()->type;
if (in_array($roleToCheck, RoleTypes::ADMIN_ROLES)) {
return true;
}
}
return false;
}
/**
* @param WaiveTransactionObject $object
* @return bool
* @throws \App\Classes\Exceptions\RequestValidationException
*/
protected function validators($object): bool
{
return $this->waiveTransactionValidation->validate($object);
}
/**
* @param $object
* @return bool
*/
protected function criteria($object): bool
{
return true;
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\Classes\Modules\Transactions\Standards\Validators;
use App\Classes\General\Abstracts\AbstractValidation;
use App\Classes\Modules\Transactions\DataTransferObjects\WaiveTransactionObject;
class WaiveTransactionValidation extends AbstractValidation
{
/**
* @param WaiveTransactionObject $object
* @return array
*/
protected function data($object): array {
return [
'id' => $object->getId(),
'billNo' => $object->getBillNo(),
'type' => $object->getType(),
'status' => $object->getStatus(),
];
}
/**
* @return array
*/
protected function rules(): array {
return [
'id' => 'required',
'type' => ['required', 'numeric', 'in:16'], //Only storage invoice (type 16) can be waived
'status' => ['required', 'numeric', 'in:2'], //Approve storage invoice only, paid invoice stricly forbidden
// 'billNo' => 'required',
];
}
/**
* @return array
*/
protected function messages(): array {
return [];
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Classes\Notifications;
use App\Models\User;
use Illuminate\Notifications\Messages\MailMessage;
class PermitsReminderEmail extends AbstractEmail
{
/** @var User */
private $user;
private $reminders;
/**
* SendResetPasswordEmail constructor.
* @param User $user
* @param PasswordReset $attempt
*/
public function __construct(User $user, $reminders)
{
$this->user = $user;
$this->reminders = $reminders;
}
public function toMail()
{
return (new MailMessage)
->subject('Permits Renew Remidner Email')
->view('emails.reminder.permits_reminder', ['user' => $this->user, 'reminders' => $this->reminders]);
}
}
+22 -10
View File
@@ -4,7 +4,9 @@ namespace App\Console\Commands;
use App\Classes\Modules\Transactions\Services\FetchesGroup;
use App\Classes\Modules\Orders\Services\FetchesOrder;
use App\Classes\Modules\Transactions\Processors\ReleaseGoodsToCustomerProcessor;
use App\Classes\Modules\Transactions\Processors\CreateStorageInvoiceDocTransactionFixProcessor;
use Carbon\Carbon;
use Illuminate\Console\Command;
@@ -30,17 +32,24 @@ class FixGroupPaymentProblem extends Command
/** @var ReleaseGoodsToCustomerProcessor */
private $releaseGoodsToCustomerProcessor;
/** @var FetchesOrder */
private $fetchesOrder;
/** @var CreateStorageInvoiceDocTransactionFixProcessor */
private $createStorageInvoiceDocTransactionProcessor;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(FetchesGroup $fetchesGroup, ReleaseGoodsToCustomerProcessor $releaseGoodsToCustomerProcessor)
public function __construct(FetchesGroup $fetchesGroup, ReleaseGoodsToCustomerProcessor $releaseGoodsToCustomerProcessor, FetchesOrder $fetchesOrder, CreateStorageInvoiceDocTransactionFixProcessor $createStorageInvoiceDocTransactionProcessor)
{
parent::__construct();
$this->fetchesGroup = $fetchesGroup;
$this->releaseGoodsToCustomerProcessor = $releaseGoodsToCustomerProcessor;
$this->fetchesOrder = $fetchesOrder;
$this->createStorageInvoiceDocTransactionProcessor = $createStorageInvoiceDocTransactionProcessor;
}
@@ -56,22 +65,25 @@ class FixGroupPaymentProblem extends Command
$this->info(Carbon::now() . ': Start data patch.');
$start = new Carbon();
/*
$group = $this->fetchesGroup->execute(['id' => 325]);
$this->info('FixGroupPaymentProblem group: '.json_encode($group));
if ($group) {
foreach ($group->groupTransactions as $groupTransaction) {
$invoice = $groupTransaction->transaction;
$this->info('FixGroupPaymentProblem group: '.json_encode($invoice));
//$paymentTransaction = $this->createPaymentTransactionProcessor->execute($invoice, PaymentMethodType::WALLET, null);
//if($paymentTransaction && $paymentTransaction->status == ApprovalStatus::APPROVED){
$pL = $invoice->owner;
$this->releaseGoodsToCustomerProcessor->execute($pL, $invoice);
//}
$pL = $invoice->owner;
$this->releaseGoodsToCustomerProcessor->execute($pL, $invoice);
}
// $group->status = $status;
// $group->save();
}
*/
$order = $this->fetchesOrder->execute(['reference' => '729251424']);
$packingLists = $order->destinationWarehousePackages;
foreach ($packingLists as $packingList){
$this->createStorageInvoiceDocTransactionProcessor->execute($packingList, true);
dd(json_encode($packingList));
}
@@ -0,0 +1,74 @@
<?php
namespace App\Console\Commands;
use App\Classes\Notifications\PermitsReminderEmail;
use App\Models\PermitsReminder;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
class SendPermitsReminderEmails extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'permitsReminder:send';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send Permits Reminders Email';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$now = now();
$startRange = $now->subDays(3)->startOfDay();
$endRange = $now->copy()->addDays(6)->endOfDay();
$reminders = PermitsReminder::whereDate('reminder_date', '>=', $startRange)
->whereDate('reminder_date', '<=', $endRange)
->pluck('model');
$this->info($this->getTimeStamp() . json_encode($reminders));
$users = User::whereIn(
'email',
[
// 'edmond.wuiming2021@gmail.com',
'anithagurl96@gmail.com'
]
)->get();
foreach ($users as $user) {
$this->info($this->getTimeStamp() . 'Reminder email sent to ' . $user->email);
if (app()->environment('production')) {
$user->notify(new PermitsReminderEmail($user, $reminders));
}
}
}
public function getTimeStamp()
{
return '[' . Carbon::now()->format('Y-m-d H:i:s') . '] - ';
}
}
+5
View File
@@ -67,6 +67,11 @@ class Kernel extends ConsoleKernel
->dailyAt('0:01')
->withoutOverlapping()
->appendOutputTo(storage_path().'/logs/check_storage_invoices.log');
$schedule->command('permitsReminder:send')
->dailyAt('09:30')
->withoutOverlapping()
->appendOutputTo(storage_path().'/logs/permits-reminder-send.log');
}
/**
@@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers\Companies;
use App\Classes\Modules\Companies\ControllersLogic\UpdateCompanyCreditTermStatusLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UpdateCompanyCreditTermStatusController
{
/**
* @param Request $request
* @param UpdateCompanyCreditTermStatusLogic $logic
* @return JsonResponse
*/
public function update(Request $request, UpdateCompanyCreditTermStatusLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Imports;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Imports\Services\GenericImport;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Models\PermitsReminder;
use Carbon\Carbon;
use DateTime;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel;
use PhpOffice\PhpSpreadsheet\Shared\Date;
class ImportPermitsReminderController
{
/**
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function import(Request $request)
{
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
$file = json_decode($object->getFiles()[0])->file_info->original->file;
$file = storage_path('app/documents/' . $file);
$import = new GenericImport();
Excel::import($import, $file);
$excelRows = $import->rows->toArray();
$successRows = [];
$returnArray = []; // Initialize the return array
foreach ($excelRows as $row) {
if (is_null($row['expiry_date']) && is_null($row['reminder_date'])) {
continue;
}
$row['expiry_date'] = $this->changeExcelDate($row['expiry_date']);
$row['reminder_date'] = $this->changeExcelDate($row['reminder_date']);
$validator = Validator::make($row, [
'model' => 'required|unique:permits_reminders,model',
'expiry_date' => 'required|date|after_or_equal:today',
'reminder_date' => 'required|date|after_or_equal:today',
]);
if ($validator->fails()) {
$row['status'] = 'failed';
$row['message'] = $validator->errors()->all();
$returnArray[] = $row;
} else {
$row['created_at'] = Carbon::now();
$row['updated_at'] = Carbon::now();
$successRows[] = $row;
}
}
if (!empty($successRows)) {
PermitsReminder::insert($successRows); // Insert only if there are successful rows
}
$successCount = count($successRows);
return response()->json([
'completedRows' => $successCount,
'failedRows' => count($returnArray),
'remark' => "Successful Imported {$successCount} data.",
'data' => $returnArray
]);
}
public function changeExcelDate($date)
{
$formatedDate = DateTime::createFromFormat('d/m/Y', $date);
if ($formatedDate instanceof DateTime) {
return $formatedDate->format('Y-m-d');
}
$unixTime = (($date - 25569) * 86400);
return (new DateTime("@$unixTime"))->format('Y-m-d');
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Jobs;
use App\Classes\Modules\Jobs\ControllersLogic\FetchJobResultLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class FetchJobResultController
{
/**
* @param Request $request
* @param FetchJobResultLogic $logic
* @return JsonResponse
*/
public function fetch(Request $request, FetchJobResultLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\PackingLists;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\PackingLists\ControllersLogic\ListPackingListsJobLogic;
class ListPackingListsJobController
{
/**
* @param Request $request
* @param ListPackingListsJobLogic $logic
* @return JsonResponse
*/
public function list(Request $request, ListPackingListsJobLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,59 @@
<?php
namespace App\Http\Controllers\PermitsReminder;
use App\Classes\Exceptions\RequestValidationException;
use App\Models\PermitsReminder;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class CreatePermitsReminderController
{
/**
* @param Request $request
* @return JsonResponse
*/
public function create(Request $request): JsonResponse
{
// Define validation rules
$rules = [
'model' => 'required|unique:permits_reminders,model',
'expiry_date' => 'required|date|after_or_equal:today',
'reminder_date' => 'required|date|after_or_equal:today',
];
// Perform validation
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
$validationErrors = $validator->errors()->all();
$validationError = implode("<br>", $validationErrors);
// Return validation error response
return response()->json([
'title' => 'Create Permits Reminder Failed',
'message' => $validationError,
"payload" => []
], 422); // 422 Unprocessable Entity status code for validation errors
}
// If validation passes, create the record
$record = PermitsReminder::create([
'expiry_date' => Carbon::parse($request->input('expiry_date'))->format('Y-m-d'),
'reminder_date' => Carbon::parse($request->input('reminder_date'))->format('Y-m-d'),
'model' => $request->input('model'),
]);
$response = [
"title" => "Create Permits Reminder Successful",
"message" => "You have successfully created a Permits Reminder",
"payload" => [
"data" => $record
]
];
return response()->json($response);
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\PermitsReminder;
use App\Models\PermitsReminder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class DeletePermitsReminderController
{
/**
* @param Request $request
* @return JsonResponse
*/
public function delete(Request $request): JsonResponse
{
$record = PermitsReminder::find($request->route('id'));
$record->delete();
$response = [
"title" => "Delete Permits Reminder Successful",
"message" => "You have successfully deleted a Permits Reminder",
"payload" => []
];
return response()->json($response);
}
}
@@ -0,0 +1,55 @@
<?php
namespace App\Http\Controllers\PermitsReminder;
use App\Models\PermitsReminder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ListPermitsReminderController
{
/**
* @param Request $request
* @return JsonResponse
*/
public function list(Request $request): JsonResponse
{
$perPage = 10; // Number of items per page
$page = request('page', 1); // Get the current page from the request
// Paginate the PermitsReminder model
$query = PermitsReminder::orderBy('id', 'desc');
$paginator = $query->paginate($perPage, ['*'], 'page', $page);
// Convert paginated data to an array
$data = $paginator->items();
// Create pagination links
$links = [
"first" => $paginator->url(1),
"last" => $paginator->url($paginator->lastPage()),
"prev" => $paginator->previousPageUrl(),
"next" => $paginator->nextPageUrl(),
];
$response = [
"title" => "List Permits Reminder",
"message" => "You have successfully retrieved a list of Permits Reminder",
"payload" => [
'data' => $data,
"links" => $links,
"meta" => [
"current_page" => $paginator->currentPage(),
"from" => $paginator->firstItem(),
"last_page" => $paginator->lastPage(),
"path" => $request->path(),
"per_page" => $paginator->perPage(),
"to" => $paginator->lastItem(),
"total" => $paginator->total(),
]
]
];
return response()->json($response);
}
}
@@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers\PermitsReminder;
use App\Models\PermitsReminder;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class UpdatePermitsReminderController
{
/**
* @param Request $request
* @return JsonResponse
*/
public function update(Request $request): JsonResponse
{
$rules = [
'model' => 'required|unique:permits_reminders,model,' . $request->route('id'),
'expiry_date' => 'required|date|after_or_equal:today',
'reminder_date' => 'required|date|after_or_equal:today',
];
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
$validationErrors = $validator->errors()->all();
$validationError = implode("<br>", $validationErrors);
return response()->json([
'title' => 'Update Permits Reminder Failed',
'message' => $validationError,
"payload" => []
], 422);
}
$record = PermitsReminder::findOrFail($request->route('id'));
$record->model = $request->input('model');
$record->expiry_date = Carbon::parse($request->input('expiry_date'))->format('Y-m-d');
$record->reminder_date = Carbon::parse($request->input('reminder_date'))->format('Y-m-d');
$record->save();
return response()->json([
'title' => 'Permits Reminder Updated',
'message' => 'The permits reminder has been successfully updated.',
"payload" => $record
]);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\RegenerateSingleStorageInvoiceTransactionLogic;
class RegenerateSingleStorageInvoiceTransactionController
{
/**
* @param Request $request
* @param RegenerateShippingInvoiceTransactionLogic $logic
* @return JsonResponse
*/
public function regenerate(Request $request, RegenerateSingleStorageInvoiceTransactionLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\WaiveTransactionLogic;
class WaiveTransactionController
{
/**
* @param Request $request
* @param WaiveTransactionLogic $logic
* @return JsonResponse
*/
public function waive(Request $request, WaiveTransactionLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
+1
View File
@@ -73,5 +73,6 @@ class Kernel extends HttpKernel
'storage.invoice.check.byorder' => \App\Http\Middleware\CheckForStorageInvoiceByOrderId::class,
'storage.invoice.check.bytransactions' => \App\Http\Middleware\CheckForStorageInvoiceByTransactions::class,
'storage.invoice.check.bygroup' => \App\Http\Middleware\CheckForStorageInvoiceByGroup::class,
'storage.invoice.check.bypackinglists' => \App\Http\Middleware\CheckForStorageInvoiceByPackingLists::class,
];
}
@@ -0,0 +1,69 @@
<?php
namespace App\Http\Middleware;
use Closure;
use App\Classes\Modules\Transactions\Processors\CheckStorageInvoiceTransactionProcessor;
use App\Classes\Modules\PackingLists\Services\ListsPackingLists;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\CompanyConnection;
use App\Models\Order;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class CheckForStorageInvoiceByPackingLists
{
/** @var CheckStorageInvoiceTransactionProcessor */
private $storageInvoiceTransactionProcessor;
/** @var ListsPackingLists */
private $listsPackingLists;
public function __construct(CheckStorageInvoiceTransactionProcessor $storageInvoiceTransactionProcessor, ListsPackingLists $listsPackingLists)
{
$this->storageInvoiceTransactionProcessor = $storageInvoiceTransactionProcessor;
$this->listsPackingLists = $listsPackingLists;
}
/**
* Handle an incoming request.
*
* @param Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$results = [];
$packinglists = null;
$filters = json_decode($request->input('filters'), true);
// $filters['type_in'] = [TransactionType::SHIPPING_INVOICE];
// if(json_encode($filters['order_by']) == '{"column":"id","DESC":true}'){
// $filters['order_by_id_desc'] = true;
// }
if(isset($filters['check_for_storage_invoice'])){
unset($filters['order_by']);
$packinglists = $this->listsPackingLists->execute($filters);
foreach($packinglists as $packingList){
if($packingList){
$order = $packingList->owner()->first();
if($order instanceof Order){
$storages = $this->storageInvoiceTransactionProcessor->executeOrder($order);
if($storages){
$results = array_merge($results, $storages);
}
}
}
}
}
$request->merge(['storages' => $results]);
return $next($request);
}
}
+2 -1
View File
@@ -21,7 +21,7 @@ class CompanyResource extends JsonResource
{
$companyModule = $this->companyModules()->first();
$companyTypeArray = CompanyType::COMPANY_TYPE_ID;
return [
'id' => $this->id,
'hash_id' => Crypt::encryptString($this->id),
@@ -33,6 +33,7 @@ class CompanyResource extends JsonResource
'business_type' => (int) $this->business_type,
'status' => (int) $this->status,
'segments' => SegmentResource::collection($companyModule->connections()->first()->segments),
'is_credit_term' => $companyModule->connections()->first()->is_credit_term,
'contact' => new ContactResource ($this->when($this->has('contacts'), $this->contacts->first())),
'employee' => new UserResource($companyModule->employees()->first()),
'company_module' => new CompanyModuleResource($companyModule),
@@ -4,6 +4,7 @@ namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Models\Transaction;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -17,6 +18,8 @@ class GroupForOrderV2Resource extends JsonResource
*/
public function toArray($request)
{
$payment_transaction = Transaction::where('payment_reference', $this->reference)->first();
return [
'id' => $this->id,
'original_amount' => (float) $this->original_amount,
@@ -33,7 +36,12 @@ class GroupForOrderV2Resource extends JsonResource
'payment_method' => (int)$this->payment_method,
'payment_method_name' => ucwords(PaymentMethodType::PAYMENT_METHODS_ID[$this->payment_method]),
'payment_reference' => $this->reference,
'transactions_ids' => GroupTransactionsForOrderV2Resource::collection($this->groupTransactions)
'transactions_ids' => GroupTransactionsForOrderV2Resource::collection($this->groupTransactions),
'payment_transaction' => $payment_transaction ? [
'id' => $payment_transaction->id,
'status' => $payment_transaction->status,
'status_name' => ApprovalStatus::APPROVAL_STATUS_ID[$payment_transaction->status]
] : []
];
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class JobResultResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'job_id' => $this->job_id,
'result' => $this->result,
];
}
}
@@ -0,0 +1,47 @@
<?php
namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Models\Order;
use App\Models\PackingList;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Http\Resources\V2\PackingListV2Resource;
class ListPackingListDetailsJobResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$exceptionUsers = in_array($this->userInfo ? $this->userInfo->type : Auth()->user()->type, [RoleTypes::SHADOW_ADMIN, RoleTypes::SUPER_ADMIN]);
$packages = !$this->packingLists()->exists() || $exceptionUsers ? $this->packages : $this->packingLists->first()->packages;
return [
'id' => $this->id,
'claimant_id' => $this->claimant_id,
'reference' => $this->reference,
'status' => $this->status,
'transport' => new TransportResource($this->transports()->first()),
'type' => $this->type,
'receive_packing_list' => $this->when($this->type === PackingListType::SHIPPING_PACKING_LIST, new PackingListV2Resource(PackingList::where('reference', $this->reference)->where('type', PackingListType::WAREHOUSE_RECEIVE_LIST)->first(), $this->userInfo)),
// 'packages' => PackageResource::collection($packages),
// $this->mergeWhen($this->owner instanceof Order, [
// 'order' => New OrderResource($this->owner)
// ]),
'container' => new ContainerResource($this->containers->first()),
'packages' => PackageWithoutOrderResource::collection($packages),
$this->mergeWhen($this->owner instanceof Order, [ 'order' => New OrderResource($this->owner) ]),
'shipping_transaction' => new TransactionResource($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereNotIn('status', [ApprovalStatus::SUSPENDED, ApprovalStatus::EXPIRED])->first()),
'suspended_invoice' => new TransactionResource($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::SUSPENDED])->first()),
// 'suspended_invoices' => TransactionResource::collection($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::SUSPENDED])->get()),
];
}
}
@@ -0,0 +1,47 @@
<?php
namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Models\Order;
use App\Models\PackingList;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Http\Resources\V2\PackingListV2Resource;
class ListPackingListJobResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$exceptionUsers = in_array($this->userInfo ? $this->userInfo->type : Auth()->user()->type, [RoleTypes::SHADOW_ADMIN, RoleTypes::SUPER_ADMIN]);
$packages = !$this->packingLists()->exists() || $exceptionUsers ? $this->packages : $this->packingLists->first()->packages;
return [
'id' => $this->id,
'claimant_id' => $this->claimant_id,
'reference' => $this->reference,
'status' => $this->status,
'transport' => new TransportResource($this->transports()->first()),
'type' => $this->type,
// 'receive_packing_list' => $this->when($this->type === PackingListType::SHIPPING_PACKING_LIST, new PackingListV2Resource(PackingList::where('reference', $this->reference)->where('type', PackingListType::WAREHOUSE_RECEIVE_LIST)->first(), $this->userInfo)),
// 'packages' => PackageResource::collection($packages),
// $this->mergeWhen($this->owner instanceof Order, [
// 'order' => New OrderResource($this->owner)
// ]),
'container' => new ContainerResource($this->containers->first()),
'packages' => PackageWithoutOrderResource::collection($packages),
$this->mergeWhen($this->owner instanceof Order, [ 'order' => New OrderResource($this->owner) ]),
'shipping_transaction' => new TransactionResource($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereNotIn('status', [ApprovalStatus::SUSPENDED, ApprovalStatus::EXPIRED])->first()),
'suspended_invoice' => new TransactionResource($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::SUSPENDED])->first()),
// 'suspended_invoices' => TransactionResource::collection($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::SUSPENDED])->get()),
];
}
}
@@ -37,6 +37,7 @@ class MappableTransactionResource extends JsonResource
'owner_type' => Transaction::class,
'owner_id'=> $this->id,
'owner_reference'=> $reference,
'created_at'=> $this->created_at, // invoice date
];
}
}
@@ -27,16 +27,19 @@ class MappableTransactionWithDetailsResource extends JsonResource
'order_reference' => null,
'bill_no' => null,
'marking' => null,
'created_at' => null
];
if ($this->type === TransactionType::PAYMENT) {
$order = $this->owner->owner->owner;
$data['order_reference'] = $order->reference;
$data['debtor_code'] = $order->companyModule->company->debtor;
$data['created_at'] = $this->owner->created_at; // invoice date
} elseif (in_array($this->type, [TransactionType::GROUP_PAYMENT, TransactionType::TOP_UP])) {
$connection = $this->owner->owner->inviters()->withPivot('invitee_reference')->first();
$data['marking'] = $connection ? $connection->pivot->invitee_reference : '';
$data['bill_no'] = $this->bill_no;
$data['created_at'] = $this->created_at;
} else {
$payments = $this->transactions()
->payments()->where('status', ApprovalStatus::APPROVED)
@@ -54,6 +57,7 @@ class MappableTransactionWithDetailsResource extends JsonResource
'type' => $payment->type,
'marking' => null,
'amount' => $payment->amount,
'created_at' => $this->created_at
];
})->toArray();
} else {
@@ -61,6 +65,7 @@ class MappableTransactionWithDetailsResource extends JsonResource
$data['amount'] = 'Transaction Type not allowed';
$data['transaction_type'] = $this->type;
$data['payment_transactions'] = null;
$data['created_at'] = null;
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ class OrderV2Resource extends JsonResource
'address' => new AddressResource($this->addresses()->where('status', '=', ApprovalStatus::APPROVED)->first()),
'address_change_request' => new AddressResource($this->addressesPendingVerification()->first()),
'invoices' => $this->whenLoaded('packingLists', function() {
return TransactionResource::collection($this->transactions()->whereNotIn('transactions.status', [0, 1])->whereIn('transactions.type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get());
return TransactionWithStorageResource::collection($this->transactions()->whereNotIn('transactions.status', [0, 1])->whereIn('transactions.type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get());
}),
'storages' => $this->storages ? $this->storages : null, //from middleware
'remarks' => RemarkResource::collection($this->remarks),
@@ -0,0 +1,38 @@
<?php
namespace App\Http\Resources;
use App\Models\Order;
use App\Models\PackingList;
use Illuminate\Http\Resources\Json\JsonResource;
class PackageWithoutOrderResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'type' => $this->type,
'description' => $this->description,
'width' => floatval($this->width),
'height' => floatval($this->height),
'length' => floatval($this->length),
'weight' => $this->weight,
'quantity' => $this->quantity,
'cbm' => (($this->width / 100) * ($this->height / 100) * ($this->length / 100)) * $this->quantity,
'reference' => $this->packingList->reference,
'status' => $this->status,
// $this->mergeWhen($originalPackingList->owner instanceof Order, [
// 'order' => New OrderResource($originalPackingList->owner)
// ]),
// 'container' => new ContainerResource($originalPackingList->containers()->first()),
// 'transport' => new TransportResource($originalPackingList->transports()->first())
];
}
}
@@ -0,0 +1,56 @@
<?php
namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Models\Order;
use App\Models\PackingList;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Log;
class PackingListWithStorageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$exceptionUsers = in_array(Auth()->user()->type, [RoleTypes::SHADOW_ADMIN, RoleTypes::SUPER_ADMIN]);
$packages = !$this->packingLists()->exists() || $exceptionUsers ? $this->packages : $this->packingLists->first()->packages;
$shippingTransaction = $this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereNotIn('status', [ApprovalStatus::SUSPENDED, ApprovalStatus::EXPIRED])->first();
$transactions = $this->transactions()->whereNotIn('transactions.status', [0, 1])->whereIn('transactions.type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get();
$storages = [];
if($this->storages){
$transactionId = $shippingTransaction->id;
$filteredStorages = array_filter($this->storages, function ($storage) use ($transactionId) {
return isset($storage['parentInvoiceId']) && $storage['parentInvoiceId'] == $transactionId;
});
$storages = $filteredStorages;
}
return [
'id' => $this->id,
'claimant_id' => $this->claimant_id,
'reference' => $this->reference,
'status' => $this->status,
'transport' => new TransportResource($this->transports()->first()),
'type' => $this->type,
'receive_packing_list' => $this->when($this->type === PackingListType::SHIPPING_PACKING_LIST, new PackingListResource(PackingList::where('reference', $this->reference)->where('type', PackingListType::WAREHOUSE_RECEIVE_LIST)->first())),
'packages' => PackageResource::collection($packages),
$this->mergeWhen($this->owner instanceof Order, [
'order' => New OrderResource($this->owner)
]),
'shipping_transaction' => count($storages) == 0 ? new TransactionWithStorageResource($shippingTransaction): null,
'storages' => $storages,
'invoices' => TransactionWithStorageResource::collection($transactions),
'suspended_invoice' => new TransactionResource($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::SUSPENDED])->first()),
];
}
}
+3 -18
View File
@@ -3,14 +3,15 @@
namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Group;
use App\Models\Order;
use App\Models\Transaction;
use App\Models\Wallet;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
class TransactionResource extends JsonResource
{
/**
@@ -23,8 +24,6 @@ class TransactionResource extends JsonResource
{
$order = null;
$groupTransactions = null;
$group_payment_attempts = null;
$group_payment_expired = null;
if ($this->owner instanceof Transaction) {
if ($this->owner) {
@@ -36,12 +35,6 @@ class TransactionResource extends JsonResource
if ($this->owner) {
$order = new OrderResource($this->owner->owner);
}
if($this->groups){
$group_payment_attempts = GroupForOrderV2Resource::collection($this->groups->whereNotIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]));
$group_payment_expired = GroupForOrderV2Resource::collection($this->groupsWithTrashed->whereIn('status', [ApprovalStatus::EXPIRED]));
}
} else {
$group = Group::where('reference', $this->payment_reference)->first();
if ($group) {
@@ -55,9 +48,7 @@ class TransactionResource extends JsonResource
'owner_type' => $this->owner_type,
'order' => $order,
'group_transactions' => $groupTransactions,
'group_reference' => $groupTransactions ? ($group ? $group->reference : null ) : null,
'groups_payment_attempts' => $group_payment_attempts,
'groups_payment_expired' => $group_payment_expired,
'group_reference' => $groupTransactions ? ($group ? $group->reference : null ) : null,
'documents' => $groupTransactions ? DocumentResource::collection($this->documents->where('status', ApprovalStatus::PENDING_VERIFICATION)) : DocumentResource::collection($this->documents),
'type' => (int) $this->type,
'bill_no' => $this->bill_no,
@@ -80,11 +71,6 @@ class TransactionResource extends JsonResource
->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)
->get()
),
'payments_expired' => TransactionResource::collection(
$this->transactions()
->payments()->where('status', ApprovalStatus::EXPIRED)
->get()
),
'payment_history' => TransactionResource::collection(
$this->transactions()
->payments()
@@ -92,7 +78,6 @@ class TransactionResource extends JsonResource
->get()
),
'remarks' => RemarkResource::collection($this->remarks),
'storages' => $this->storages ? $this->storages : null, //from middleware
'expires_on' => Carbon::parse($this->expires_on)->format('d-m-Y h:s:i'),
'updated_at' => Carbon::parse($this->updated_at)->format('d-m-Y'),
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y')
@@ -0,0 +1,129 @@
<?php
namespace App\Http\Resources;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Group;
use App\Models\Transaction;
use App\Models\Wallet;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Log;
class TransactionWithStorageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$order = null;
$groupTransactions = null;
$group_payment_attempts = null;
$groupPaymentAttemptsFiltered = [];
$group_payment_expired = null;
$group_payment_history = null;
$groupTotalAmount = 0;
if ($this->owner instanceof Transaction) {
if ($this->owner) {
if ($this->owner->owner) {
$order = new OrderResource($this->owner->owner->owner);
}
}
} else if (!($this->owner instanceof Transaction) && !($this->owner instanceof Wallet)) {
if ($this->owner) {
$order = new OrderResource($this->owner->owner);
}
if($this->groups){
$group_payment_attempts = GroupForOrderV2Resource::collection($this->groups->whereNotIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]));
$group_payment_expired = GroupForOrderV2Resource::collection($this->groupsWithTrashed->whereIn('status', [ApprovalStatus::EXPIRED]));
$group_payment_history = GroupForOrderV2Resource::collection($this->groups->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::REJECTED]));
}
} else {
$group = Group::where('reference', $this->payment_reference)->first();
if ($group) {
$groupTransactions = GroupTransactionResource::collection($group->groupTransactions);
}
}
$ts = $this->groups->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION])->last();
if ($ts) {
$paymentTransaction = Transaction::where('payment_reference', $ts->reference)->whereIn('status', [ApprovalStatus::PENDING_SUBMISSION])->first();
if($paymentTransaction){
$groupTotalAmount = (double) $this->amount;
foreach ($group_payment_history as $key => $value) {
if ($value->id === $ts->id && $value->reference === $ts->reference) {
unset($group_payment_history[$key]);
}
}
foreach ($group_payment_attempts as $key => $value) {
if ($value->id === $ts->id && $value->reference == $ts->reference) {
$groupPaymentAttemptsFiltered[$key] = $value;
}
}
}
}
return [
'id' => $this->id,
'owner_type' => $this->owner_type,
'order' => $order,
'group_transactions' => $groupTransactions,
'group_reference' => $groupTransactions ? ($group ? $group->reference : null ) : null,
'groups_payment_attempts' => $groupPaymentAttemptsFiltered,
'groups_payment_expired' => $group_payment_expired,
'groups_payment_history' => $group_payment_history,
'documents' => $groupTransactions ? DocumentResource::collection($this->documents->where('status', ApprovalStatus::PENDING_VERIFICATION)) : DocumentResource::collection($this->documents),
'type' => (int) $this->type,
'bill_no' => $this->bill_no,
'amount' => (double) $this->amount,
'payment_method' => (int) $this->payment_method,
'payment_reference' => $this->payment_reference,
'outstanding' => (double) $this->amount - ($this->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->sum('amount')),
'floating' => $group_payment_attempts ? $groupTotalAmount : (double) $this->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::PENDING_SUBMISSION, ApprovalStatus::PENDING_VERIFICATION])->sum('amount'),
'service_charge' => (double) $this->service_charge,
'tax' => (double) $this->tax,
'original_amount' => (double) $this->original_amount,
'currency' => new CurrencyResource($this->currency),
'original_currency' => new CurrencyResource($this->original_currency),
'currency_rate' => (double) $this->currency_rate,
'status' => (int) $this->status,
'details' => TransactionDetailResource::collection($this->transactionDetails),
'transactions' => TransactionResource::collection($this->transactions),
'payment_attempts' => TransactionResource::collection(
$this->transactions()
->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)
->get()
),
'payments_expired' => TransactionResource::collection(
$this->transactions()
->payments()->where('status', ApprovalStatus::EXPIRED)
->get()
),
'payment_history' => TransactionResource::collection(
$this->transactions()
->payments()
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED])
->get()
),
'remarks' => RemarkResource::collection($this->remarks),
'storages' => $this->storages ? $this->storages : null, //from middleware
'is_waived' => (int) $this->is_waived,
'expires_on' => Carbon::parse($this->expires_on)->format('d-m-Y h:s:i'),
'updated_at' => Carbon::parse($this->updated_at)->format('d-m-Y'),
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y')
];
}
}
@@ -0,0 +1,54 @@
<?php
namespace App\Http\Resources\V2;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\RoleTypes;
use App\Models\Order;
use App\Models\PackingList;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Http\Resources as V1;
class PackingListV2Resource extends JsonResource
{
private $userInfo;
public function __construct($resource, $userInfo = null)
{
parent::__construct($resource);
$this->userInfo = $userInfo;
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$user = $this->userInfo ? $this->userInfo : Auth()->user();
$exceptionUsers = in_array($user->type, [RoleTypes::SHADOW_ADMIN, RoleTypes::SUPER_ADMIN]);
$packages = !$this->packingLists()->exists() || $exceptionUsers ? $this->packages : $this->packingLists->first()->packages;
return [
'id' => $this->id,
'claimant_id' => $this->claimant_id,
'reference' => $this->reference,
'status' => $this->status,
'transport' => new V1\TransportResource($this->transports()->first()),
'type' => $this->type,
'receive_packing_list' => $this->when($this->type === PackingListType::SHIPPING_PACKING_LIST, new PackingListV2Resource(PackingList::where('reference', $this->reference)->where('type', PackingListType::WAREHOUSE_RECEIVE_LIST)->first(), $user)),
'packages' => V1\PackageResource::collection($packages),
$this->mergeWhen($this->owner instanceof Order, [
'order' => New V1\OrderResource($this->owner)
]),
'shipping_transaction' => new V1\TransactionResource($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereNotIn('status', [ApprovalStatus::SUSPENDED, ApprovalStatus::EXPIRED])->first()),
'suspended_invoice' => new V1\TransactionResource($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::SUSPENDED])->first()),
// 'suspended_invoices' => V1\TransactionResource::collection($this->transactions()->where('type', TransactionType::SHIPPING_INVOICE)->whereIn('status', [ApprovalStatus::SUSPENDED])->get()),
];
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Models;
class JobResult extends AbstractModel
{
protected $table = 'job_results';
public $fillable = [
'job_id',
'result'
];
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
class PermitsReminder extends Model
{
use SoftDeletes;
protected $table = 'permits_reminders';
protected $dates = ['deleted_at'];
protected $fillable = ['model', 'expiry_date', 'reminder_date'];
public function getExpiryDateAttribute($value)
{
return $this->attributes['expiry_date'] === null ? NULL : Carbon::parse($this->attributes['expiry_date'])->format('d-m-Y');
}
public function getReminderDateAttribute($value)
{
return $this->attributes['reminder_date'] === null ? NULL : Carbon::parse($this->attributes['reminder_date'])->format('d-m-Y');
}
public function getCreatedAtAttribute($value)
{
return $this->attributes['created_at'] === null ? NULL : Carbon::parse($this->attributes['created_at'])->format('d-m-Y');
}
}
+8
View File
@@ -34,6 +34,14 @@ return [
'driver' => 'sync',
],
'high_priority' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'high_priority',
'retry_after' => 3600,
'after_commit' => false,
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateJobResultsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('job_results', function (Blueprint $table) {
$table->id();
$table->string('job_id', 50);
$table->longText('result')->nullable();
$table->timestamps();
// $table->foreign('job_id')->references('id')->on('jobs')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('job_results');
}
}
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNewColumnToJobResultsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('job_results', function (Blueprint $table) {
$table->longText('url')->after('result')->nullable();
$table->string('job_command_name')->after('url')->nullable();
$table->longText('job_command')->after('job_command_name')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('job_results', function (Blueprint $table) {
$table->dropColumn('url');
$table->dropColumn('job_command_name');
$table->dropColumn('job_command');
});
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNewColumn2ToJobResultsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('job_results', function (Blueprint $table) {
$table->string('request_signature')->after('job_id')->nullable();
$table->string('result_signature')->after('request_signature')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('job_results', function (Blueprint $table) {
$table->dropColumn('request_signature');
$table->dropColumn('result_signature');
});
}
}
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePermitsRemindersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('permits_reminders', function (Blueprint $table) {
$table->id();
$table->string('model')->nullable();
$table->timestamp('expiry_date')->nullable();
$table->timestamp('reminder_date')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('permits_reminders');
}
}
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsWaivedToTransactionsLogsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('transaction_logs', function (Blueprint $table) {
$table->boolean('is_waived')->nullable()->after('status')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('transaction_logs', function (Blueprint $table) {
$table->dropColumn('is_waived');
});
}
}
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsWaivedToTransactionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('transactions', function (Blueprint $table) {
$table->boolean('is_waived')->nullable()->after('status')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('transactions', function (Blueprint $table) {
$table->dropColumn('is_waived');
});
}
}
Vendored
+2 -1
View File
@@ -37,6 +37,7 @@ gulp.task('vendorCss', () => {
.pipe(plugins.cleanCss())
.pipe(plugins.replace('../../../font-awesome/', './'))
.pipe(plugins.replace('../../jquery-ui-dist/', ''))
//.pipe(plugins.replace(/background-image:url\(\.\.\/\.\./g, `background-image:url(`)) //cief todo: gulp
.pipe(gulp.dest(pkg.paths.build.css));
});
@@ -115,4 +116,4 @@ gulp.task('watch', (done) => {
gulp.watch(pkg.globs.sourceJs, gulp.series('sourceJs'));
gulp.watch(pkg.globs.sourceJs, gulp.series('sourceFonts'));
done();
});
});
@@ -5,7 +5,7 @@
<div class="col-auto">
<div class="row">
<div class="col">
<h6 class="no-margin fs-12 text-black">{{item.street_one}} {{item.street_two}}, {{item.district.name}}, {{item.post_code}} {{item.state.name}}, {{item.country.name}}</h6>
<h6 v-if="item" class="no-margin fs-12 text-black">{{item.street_one}} {{item.street_two}}, {{item.district.name}}, {{item.post_code}} {{item.state.name}}, {{item.country.name}}</h6>
</div>
</div>
</div>
@@ -17,7 +17,7 @@
<change-customer-name-form-component :section="'customerProfileSection'" :data="item"></change-customer-name-form-component>
</modal-component>
<div class="font-heading fs-20 light">
{{item.name}}
{{item.name}}
<div class="btn btn-xs b-rad-none pointer requestModal d-inline rounded no-border hover-primary bg-transparent" data-type="changeCustomerName" >
<i class="fa fa-edit pointer fa-fw fs-15 m-l-5"></i>
</div>
@@ -35,6 +35,26 @@
</div>
</div>
</div>
<div class="row" v-if="$store.getters.isAdmin">
<div class="col">
<div class="row align-items-center">
<div class="col-auto">
<div v-if="item.is_credit_term === 1" class="font-heading fs-11 light">
Credit term customer: YES
</div>
<div v-else class="font-heading fs-11 light">
Credit term customer: NO
</div>
</div>
<div class="btn btn-xs b-rad-none pointer requestModal d-inline rounded no-border hover-primary bg-transparent" data-type="changeCreditTermCustomerStatus">
<i class="fa fa-edit pointer fa-fw fs-11 m-l-5"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="changeCreditTermCustomerStatus">
<update-credit-term-status-form-component :section="'customerProfileSection'" :data="item" v-on:update="item.is_credit_term = $event.is_credit_term"></update-credit-term-status-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -95,13 +115,13 @@
</div>
<!-- <div class="row align-items-center">
<div class="col">
<h6 class="no-margin fs-12">WeChat ID</h6>
<h6 class="no-margin fs-12">WeChat ID</h6>
<h6 class="no-margin">{{item.contact.wechat_id}}</h6>
</div>
</div> -->
<div class="row align-items-center">
<div class="col">
<h6 class="no-margin fs-12">Company Type </h6>
<h6 class="no-margin fs-12">Company Type </h6>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="changeBusinessType">
<change-business-type-form-component :section="'customerProfileSection'" :data="item"></change-business-type-form-component>
</modal-component>
@@ -216,4 +236,4 @@
},
mixins: [componentHandler]
}
</script>
</script>
@@ -0,0 +1,39 @@
<template>
<div class="row" @keyup.enter="submitForm">
<div class="col bg-white padding-40 b-rad-lg">
<loading-component style="height: 50px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
<div class="row" v-show="!$store.getters.isLoading(section)">
<div class="col text-center">
<div class="row m-b-20">
<div class="col">
<h5 class="all-caps">Update Credit Term Customer</h5>
<div class="fs-11">Are you sure you want to update the credit term status for this company to <b>{{ data.is_credit_term === 1 ? 'NO' : 'YES' }}</b>?</div>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div data-dismiss="modal" class="btn btn-sm btn-default bg-master-lighter btn-block b-rad-none">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-danger w-100 btn-sm" @click="submit(route('api.company.update.creditterm.status', data.id), 'post', section, true, true)">Confirm</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import modalFormHandler from '../../../general/mixins/modalFormHandler';
export default {
methods: {
successHandler(response){
this.$emit('update', response.payload.data);
this.closeModal();
this.formHandler();
},
},
mixins: [modalFormHandler]
}
</script>
@@ -3,7 +3,7 @@
<div class="col bg-white padding-40 b-rad-lg">
<div class="row m-b-10">
<div class="col text-center">
<p>Plese set the approximate dates of below.</p>
<p>Please set the approximate dates of below.</p>
</div>
</div>
<div class="row">
@@ -12,13 +12,13 @@
<div class="col p-l-0 p-r-10">
<validation-wrapper-component class="m-b-15" :validator="$v.parameters.etd">
<label class="text-primary">Etd</label>
<date-picker-component :parameters="parameters" :value="'etd'"></date-picker-component>
<date-picker-component v-model.lazy="parameters.etd"></date-picker-component>
</validation-wrapper-component>
</div>
<div class="col p-r-0 p-l-10">
<validation-wrapper-component class="m-b-15" :validator="$v.parameters.eta">
<label class="text-primary">Eta</label>
<date-picker-component :parameters="parameters" :value="'eta'"></date-picker-component>
<date-picker-component v-model.lazy="parameters.eta"></date-picker-component>
</validation-wrapper-component>
</div>
</div>
@@ -43,8 +43,8 @@
data() {
return {
parameters: {
etd: this.etd,
eta: this.eta,
etd: this.data.transport.current_schedule.etd,
eta: this.data.transport.current_schedule.eta,
}
};
},
@@ -57,4 +57,4 @@
mixins: [modalFormHandler]
}
</script>
</script>
@@ -0,0 +1,207 @@
<template>
<transition-component group enter-class="animate__animated animate__fadeInUp animate__delay-1 animate__faster p-r-30" leave-class="animate__animated animate__fadeOutDown animate__faster p-r-30" style="min-height: 300px;width:100%">
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div class="row" key="2" v-show="!$store.getters.isLoading(section)">
<div class="col">
<div class="row">
<div class="col">
<div class="row align-items-center justify-content-center p-t-50 p-b-50" v-show="!$store.getters.getListData(section).length && !isLoading && emptyListSection">
<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" style="letter-spacing: 2px;">Nothing To Show Here</p>
</div>
</div>
<div class="row m-t-5 align-items-center justify-content-center">
<div class="col">
<small class="fs-9 muted all-caps font-lato" style="letter-spacing: 2px">There is no results found, Try adjusting your filters to find what you are looking for.</small>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" v-show="!isLoading">
<div class="col">
<div class="list disable-text-selection" data-check-all="checkAll">
<div class="row" ref="list" v-for="item in $store.getters.getListData(section)" v-bind:key="item.id" :data="item">
<div class="col">
<slot name="list" :data="item"></slot>
</div>
</div>
</div>
</div>
</div>
<div class="row" v-show="!isLoading">
<div class="col">
<pagination-component :section="section" class="mb-5" ref="pagination"></pagination-component>
</div>
</div>
</div>
</div>
</div>
</div>
</transition-component>
</template>
<script>
import requestV2 from '../../../general/mixins/aws/requestV2'
export default {
props: {
section:{
type: String,
required: true
},
endpoint: {
type: String,
required: true
},
options: {
default () {
return {}
}
},
emptyListSection: {
type: Boolean,
default: true,
}
},
data(){
return {
filters: this.options,
pollingInterval: null,
isPolling: false,
isFetchingResult: false,
isLoading: false,
}
},
created(){
this.setDecoratorDefault();
this.$store.dispatch('updateListQueue', {'name': this.section, 'page': 1, 'filters': this.filters}); //cief todo: Uncaught (in promise) null
},
computed: {
pendingList () {
return this.$store.getters.isInCompleteQueue(this.section);
}
},
watch: {
pendingList(inComplete){
if(inComplete){
this.fetchList();
}
}
},
methods: {
fetchList(){
let listDecorators = this.$store.getters.getListDetails(this.section);
let url = this.endpoint + '?page=' + listDecorators.page + '&filters=' + JSON.stringify(listDecorators.filters);
this.isLoading = true;
this.submitJob(url);
},
successHandler(response){
let result = JSON.parse(response.payload.data.result);
result.meta = {
current_page: result.meta.current_page,
first_page_url: result.meta.first_page_url,
from: result.meta.from,
last_page: result.meta.last_page,
last_page_url: result.meta.last_page_url,
next_page_url: result.meta.next_page_url,
path: result.meta.path,
per_page: result.meta.per_page,
prev_page_url: result.meta.prev_page_url,
to: result.meta.to,
total: result.meta.total
};
this.stopPolling();
this.$store.dispatch('completeList', {'name': this.section, 'data': result.data});
this.$refs.pagination.makePagination(result.meta, result.links);
this.isLoading = false;
},
errorHandler(error){
this.isFetchingResult = false;
this.isPolling = false;
},
startPolling(jobId, maxAttempts = 8) {
let attempts = 0;
let interval = 10000; // Initial interval
const resetPollingInterval = (customInterval) => {
this.pollingInterval = setInterval(pollJobResult, customInterval);
};
const pollJobResult = () => {
if (this.isPolling || this.isFetchingResult) {
return;
}
this.isPolling = true;
attempts++;
if(attempts === 1){
this.stopPolling();
resetPollingInterval(5000);
}
if (attempts > maxAttempts) {
this.stopPolling();
this.isLoading = false;
console.log(`Reached maximum attempts (${maxAttempts}). Polling stopped.`);
return;
}
if(attempts === maxAttempts){
this.fetchJobResult(jobId, true);
}
else{
this.fetchJobResult(jobId);
}
};
// pollJobResult(); // Initial call
this.pollingInterval = setInterval(pollJobResult, interval);
},
stopPolling() {
clearInterval(this.pollingInterval);
this.pollingInterval = null;
this.isPolling = false;
this.isFetchingResult = false;
},
submitJob(url){
try {
this.$store.dispatch('crudRequestV2', {endpoint: url, method: 'get'}).then(response => {
let success = response.ok;
response.json().then(response => {
if(!success){return;}
let jobId = response.payload.data.job_id;
if(jobId){
this.startPolling(jobId);
}
});
})
} catch (error) {
console.error('Error submitJob', error);
}
},
fetchJobResult(jobId, isLastAttempt = false) {
// console.log('fetchJobResult: ', jobId);
this.isFetchingResult = true;
try {
let anotherEndpoint = route('api.job.fetch', jobId);
if(isLastAttempt){
anotherEndpoint = route('api.job.fetch.last.attempt', jobId, isLastAttempt);
}
this.poll(anotherEndpoint, 'get', this.section, false, false); //cief todo: Uncaught (in promise) null
} catch (error) {
console.error('Error fetchJobResult', error);
}
}
},
mixins: [requestV2]
}
</script>
@@ -149,7 +149,7 @@
</div>
<div class="row bg-master-lightest p-t-15" v-for="invoice in order.invoices" v-if="invoice.type === 1">
<div class="col" v-if="getMergedInvoices(invoice).length > 1">
<customer-payments-billing-variant-2-component :data="getMergedInvoices(invoice)" :storage="getStorageInfo(invoice)" invoice_status="Pending Payment" :section="section"></customer-payments-billing-variant-2-component>
<customer-payments-billing-with-storage-component :data="getMergedInvoices(invoice)" :storage="getStorageInfo(invoice)" invoice_status="Pending Payment" :section="section"></customer-payments-billing-with-storage-component>
</div>
<div class="col" v-else>
<customer-payments-billing-component :data="invoice" invoice_status="Pending Payment" :section="section"></customer-payments-billing-component>
@@ -34,11 +34,18 @@
</div>
</div>
</div>
<!-- CIEF TODO: For easy revert to old code -->
<list-component :key="currentKey" :section="section" :endpoint="route('api.packing_list.list')" :options="options">
<template slot="list" slot-scope="{data}">
<single-parent-admin-payments-billing-component :data="data" :invoice_status="invoice_status" :section="section" ></single-parent-admin-payments-billing-component>
<!-- <admin-payments-billing-component :data="data" :invoice_status="invoice_status" :section="section" ></admin-payments-billing-component> -->
</template>
</list-component>
<!-- <list-polling-component :key="currentKey" :section="section" :endpoint="route('api.packing_list.list.job')" :options="options">
<template slot="list" slot-scope="{data}">
<admin-payments-billing-component :data="data" :invoice_status="invoice_status" :section="section" ></admin-payments-billing-component>
</template>
</list-component>
</list-polling-component> -->
</div>
</div>
</template>
@@ -0,0 +1,90 @@
<template>
<div class="row m-t-20" @keyup.enter="search">
<div class="col">
<div class="row">
<div class="col-12 col-md">
<div class="row m-b-15">
<div class="col">
<div class="row">
<div class="col p-r-0">
<div class="form-group no-margin form-group-default b-rad-none">
<label class="text-primary">Order Number Like</label>
<input type="text" class="form-control" v-model="marking" />
</div>
</div>
</div>
</div>
<div class="col-auto p-l-0 p-r-0">
<div class="btn btn-primary b-rad-none" @click="search">
<i class="fa fa-search lh-40"></i>
</div>
</div>
<div class="col-auto p-l-0">
<div class="btn btn-secondary b-rad-none" @click="reset">
<i class="fa fa-remove lh-40"></i>
</div>
</div>
</div>
</div>
<div class="col-12 col-md">
<div class="row" v-if="with_export">
<div class="col">
<download-billing-with-dates-component :section="section"></download-billing-with-dates-component>
</div>
</div>
</div>
</div>
<!-- CIEF TODO: For easy revert to old code -->
<!-- <list-component :key="currentKey" :section="section" :endpoint="route('api.packing_list.list')" :options="options">
<template slot="list" slot-scope="{data}">
<admin-payments-billing-component :data="data" :invoice_status="invoice_status" :section="section" ></admin-payments-billing-component>
</template>
</list-component> -->
<list-polling-component :key="currentKey" :section="section" :endpoint="route('api.packing_list.list.job')" :options="options">
<template slot="list" slot-scope="{data}">
<single-admin-payments-billing-component :data="data" :invoice_status="invoice_status" :section="section" ></single-admin-payments-billing-component>
</template>
</list-polling-component>
</div>
</div>
</template>
<script>
export default {
props: {
invoice_status: {
type: String,
required: true
},
options: {
default () {
return {}
}
},
section:{
type: String,
required: true
},
with_export:{
type: Boolean,
default: false
}
},
data(){
return {
marking: '',
currentKey: 1,
}
},
methods: {
search() {
this.options['order_marking_in'] = this.marking;
this.currentKey+=1;
},
reset() {
this.marking = '';
delete(this.options['order_marking_in']);
this.currentKey+=1;
},
},
}
</script>
@@ -41,7 +41,21 @@
</div>
</div>
</div>
<div class="col-2 p-l-0 p-r-0 d-flex justify-content-center align-items-center">
<div class="col-2 p-l-0 p-r-0 d-flex justify-content-center align-items-center" v-if="$store.getters.isAdmin">
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" v-if="item.type === 16 && item.is_waived === 0 && item.status === 2" :data-type="'waiveInvoice-' + item.id">
Waive Transaction
</span>
<span class="d-inline-block m-r-15 text-primary bold" v-else-if="item.type === 16 && item.is_waived === 1">
Transaction Waived
</span>
<span class="d-inline-block m-r-15 text-primary bold" v-else>
N/A
</span>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" v-if="item.type === 16 && item.is_waived === 0 && item.status === 2" :type="'waiveInvoice-' + item.id">
<waive-invoice-form-component :data="item" :section="section"></waive-invoice-form-component>
</modal-component>
</div>
<div class="col-auto p-l-0 p-r-0 d-flex justify-content-center align-items-center">
<div v-if="item.type === 1" :class="[{'invisible': [5, 6, 3].includes(item.status)}]">
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" data-type="billingRemark">Billing Question?</span>
</div>
@@ -68,11 +82,48 @@
</div>
</div>
</div>
<div class="col-1 p-l-0 p-r-0 d-flex justify-content-center align-items-center" v-if="$store.getters.isSuperAdmin">
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" data-type="deleteInvoice">
<div class="col-auto p-l-0 p-r-0 d-flex justify-content-center align-items-center" v-if="$store.getters.isSuperAdmin">
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" style="min-width:15px;" :data-type="'regenerateStorageInvoice-' + item.id" v-if="item.type === 16 && item.status === 3">
<i class="fa fa-repeat"></i>
</span>
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" style="min-width:15px;" v-if="item.type === 16 && item.status !== 3">
<i class="fa fa-ban"></i>
</span>
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" style="min-width:15px;" :data-type="'regenerateShippingInvoice-' + item.id" v-if="item.type === 1">
<i class="fa fa-repeat"></i>
</span>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" :type="'regenerateStorageInvoice-' + item.id" v-if="item.type === 16">
<general-confirmation-form-component
contentText="Are you sure you want to regenerate this storage Invoice?"
modalType="delete"
buttonText="Regenerate"
class="text-center"
:apiRoute="route('api.transaction.storage.invoice.regenerate', item.id)"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" :type="'regenerateShippingInvoice-' + item.id" v-else>
<general-confirmation-form-component
contentText="Are you sure you want to regenerate this Invoice?"
modalType="delete"
buttonText="Regenerate"
class="text-center"
:apiRoute="route('api.transaction.invoice.regenerate', item.id)"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" :data-type="'deleteInvoice-' + item.id">
<i class="fa fa-close"></i>
</span>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteInvoice">
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" :type="'deleteInvoice-' + item.id">
<delete-invoice-form-component :data="item" :section="section"></delete-invoice-form-component>
</modal-component>
</div>
@@ -102,7 +153,7 @@
<div class="col-12 col-md-7 padding-20">
<div class="row bg-master-lightest">
<div class="col">
<div v-for="(groupPaymentAttemptItem, index) in items" class="row bg-master-lightest" v-if="groupPaymentAttemptItem.groups_payment_attempts.length && groupPaymentAttemptItem.type == 1 && groupTransactionsExist(items, groupPaymentAttemptItem.groups_payment_attempts)">
<div v-for="(groupItem, index) in items" class="row bg-master-lightest" v-if="groupItem.groups_payment_attempts.length && groupItem.type == 1 && groupTransactionsExist(items, groupItem.groups_payment_attempts)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
@@ -111,12 +162,12 @@
</div>
<div class="row">
<div class="col">
<shipping-transaction-component v-for="group in groupPaymentAttemptItem.groups_payment_attempts" v-bind:key="group.id" :data="group" :section="section"></shipping-transaction-component>
<single-group-payment-transaction-details-component v-for="group in groupItem.groups_payment_attempts" v-bind:key="group.id" :data="group" :section="section"></single-group-payment-transaction-details-component>
</div>
</div>
</div>
</div>
<div v-for="(groupPaymentAttemptItem, index) in items" class="row bg-master-lightest" v-if="groupPaymentAttemptItem.groups_payment_expired.length && groupPaymentAttemptItem.type == 1 && groupTransactionsExist(items, groupPaymentAttemptItem.groups_payment_expired)">
<div v-for="(groupItem, index) in items" class="row bg-master-lightest" v-if="groupItem.groups_payment_expired.length && groupItem.type == 1 && groupTransactionsExist(items, groupItem.groups_payment_expired)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
@@ -125,7 +176,21 @@
</div>
<div class="row">
<div class="col">
<payment-expired-component v-for="group in groupPaymentAttemptItem.groups_payment_expired" v-bind:key="group.id" :data="group" :section="section"></payment-expired-component>
<payment-expired-component v-for="group in groupItem.groups_payment_expired" v-bind:key="group.id" :data="group" :section="section"></payment-expired-component>
</div>
</div>
</div>
</div>
<div v-for="(groupItem, index) in items" class="row bg-master-lightest" v-if="groupItem.groups_payment_history.length && groupItem.type == 1 && groupTransactionsExist(items, groupItem.groups_payment_history)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment History</div>
</div>
</div>
<div class="row">
<div class="col">
<single-group-payment-history-transaction-details-component v-for="group in groupItem.groups_payment_history" v-bind:key="group.id" :data="group" :section="section"></single-group-payment-history-transaction-details-component>
</div>
</div>
</div>
@@ -216,7 +281,7 @@
<div class="col">
<div class="btn btn-sm all-caps b-rad-none btn-success btn-block requestModal" data-type="makePayment">Make Payment</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="makePayment">
<group-payment-form-component :section="section" :selectedIds="selectedIds" :sumAmount="totalOutstanding.toFixed(2)"></group-payment-form-component>
<group-payment-form-component :section="section" :selectedIds="selectedIds" :sumAmount="totalOutstanding.toFixed(2)" :showManualPayment="true"></group-payment-form-component>
</modal-component>
</div>
</div>
@@ -0,0 +1,322 @@
<template>
<div class="row m-b-15 m-l-5 m-r-10 parentContainer">
<div class="col bg-white rounded">
<div class="row">
<div class="col padding-20">
<div class="row align-items-center">
<div class="col">
<p class="no-margin fs-10 all-caps">Marking</p>
<div class="no-margin">
<div v-if="item.order">
<a :href="route('customer.profile', item.order.company_module.marking)">{{item.order.company_module.marking}}</a>/<a :href="route('order.show', item.order.reference)">{{item.order.reference}}</a>
<p>{{item.loading_days_ago}}</p>
</div>
<div v-else class="text-danger">Unclaimed Packing List</div>
</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Container</p>
<div v-if="item.container"> {{item.container.container_reference}}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Invoice Date</p>
<div v-if="!item.shipping_transaction">n/a</div>
<div v-if="item.shipping_transaction"> {{ item.shipping_transaction.updated_at }}</div>
</div>
<div class="col">
<div v-if="item.order">
<p class="no-margin fs-10 all-caps">Address</p>
<div>{{ item.order.address.post_code_area}}</div>
<div>{{ item.order.address.district.name +' '+item.order.address.postcode+' '+item.order.address.state.name }}</div>
</div>
<div v-else class="text-danger">Unclaimed</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Status</p>
<div class="all-caps">{{ invoice_status }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Amount</p>
<div v-if="!item.shipping_transaction">n/a</div>
<div v-if="item.shipping_transaction">MYR {{ item.shipping_transaction.amount.toFixed(2) }}</div>
</div>
<div class="col-auto p-l-0 p-r-0" v-if="['Pending Payment', 'Paid Invoice', 'Suspended Invoice'].includes(invoice_status)">
<div v-if="item.shipping_transaction">
<div v-if="item.shipping_transaction.documents.length">
<div v-for="file in item.shipping_transaction.documents[0].files" v-bind:key="file.id" class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<div class="btn bg-grey no-border muted">
<i class="fa fa-file-pdf-o"></i>
</div>
</template>
</document-file-viewer-component>
</div>
</div>
<div v-else>
<div class="btn bg-grey no-border muted invisible">
<i class="fa fa-file-pdf-o"></i>
</div>
</div>
</div>
</div>
<div class="col-auto">
<div class="row text-center parentContainer m-b-10" v-if="['Pending Invoice'].includes(invoice_status) && !item.receive_packing_list" >
<div class="col">
<div class="requestModal pointer btn btn-primary btn-xs" data-type="addWarehouseTransport">
Add Warehouse Transport
</div>
<modal-component class="animate_animated animatefast animate_fadeIn" styleType="fill-in" type="addWarehouseTransport">
<create-warehouse-transport-form-component :packinglistId="item.id" :section="section"></create-warehouse-transport-form-component>
</modal-component>
</div>
</div>
<div class="row">
<div class="col">
<div class="btn bg-grey no-border" @click="expanded = !expanded" v-if="['Pending Approval', 'Pending Payment'].includes(invoice_status)">
<i class="fa" :class="{'fa-angle-down': !expanded, 'fa-angle-up': expanded}" ></i>
</div>
<div v-if="!item.shipping_transaction">
<div v-if="item.order">
<div v-if="!item.order.company_module.billingAddress">
<div class="btn btn-primary btn-xs pointer requestModal btn-block" data-type="billingAddressComponent">Add Billing Address</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" size="extra-large" styleType="fill-in" type="billingAddressComponent">
<address-form-component :id="item.order.company_module.id" :section="section" :type=1></address-form-component>
</modal-component>
</div>
<div class="row" v-if="item.order.company_module.billingAddress">
<div class="col">
<div class="col-auto requestModal pointer" data-type="editBillingAddress">
<i class="fa fa-edit pointer fa-fw m-l-5"></i> Edit billing Address
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" size="extra-large" styleType="fill-in" type="editBillingAddress">
<div class="row">
<div class="col bg-white">
<address-form-component :id="item.order.company_module.id" :data="item.order.company_module.billingAddress" section="editBillingAddress"></address-form-component>
</div>
</div>
</modal-component>
</div>
</div>
<div v-if="!item.order.address.post_code_area">
<div class="btn btn-xs btn-primary pointer m-t-10 requestModal btn-block" data-type="defineLocation">Define Location</div>
<modal-component class="animate_animated animatefast animate_fadeIn" styleType="fill-in" type="defineLocation">
<declare-postcode-area-form-component :data="item.order.address" :section="section"></declare-postcode-area-form-component>
</modal-component>
</div>
<div v-if="item.order.company_module.billingAddress && item.order.address.post_code_area" class="btn btn-outline-primary btn-lg pointer" @click="generateInvoice()">Generate Invoice</div>
</div>
<div v-else>
<div class="btn btn-outline-primary btn-lg pointer invisible">Generate Invoice</div>
</div>
</div>
<div class="btn bg-grey no-border muted requestModal" v-if="item.shipping_transaction && invoice_status == 'Pending Approval'" data-type="confirmInvoice">
<i class="fa fa-check fs-12"></i>
</div>
<div class="btn bg-grey no-border muted requestModal" v-if="item.shipping_transaction && invoice_status == 'Pending Approval'" data-type="deleteInoive">
<i class="fa fa-close fs-12"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="confirmInvoice">
<approve-shipping-invoice-form-component :section="section" :data="data"></approve-shipping-invoice-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteInoive">
<delete-shipping-invoice-form-component :section="section" :data="data"></delete-shipping-invoice-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded" v-if="item.shipping_transaction && ['Pending Approval', 'Suspended Invoice'].includes(invoice_status)">
<div class="col p-b-10">
<div class="row">
<div class="col p-l-20 p-r-20 p-t-10 p-b-10">
<div class="row align-items-center">
<div class="col">
<p class="no-margin fs-10 all-caps">Container Reference</p>
<div v-if="item.packages.length">{{ item.container ? item.container.container_reference : 'n/a' }}</div>
<div v-else>n/a</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Due Date</p>
<div v-if="item.packages.length">
<div v-if="item.container">
<div v-if="item.container.transport">{{ item.container.transport.drop_date == null ? item.container.transport.current_schedule.etd : item.container.transport.drop_date }}</div>
<div v-else>n/a</div>
</div>
<div v-else>n/a</div>
</div>
<div v-else>n/a</div>
</div>
<!-- <div class="col">
<p class="no-margin fs-10 all-caps">Paid Date</p>
<div>n/a</div>
</div> -->
<div class="col">
<p class="no-margin fs-10 all-caps">Total CBM</p>
<div>{{ (parseFloat(cbm) + parseFloat(overweight)).toFixed(3) }}</div>
</div>
</div>
</div>
</div>
<h5 class="text-underline text-center">Invoice Details</h5>
<invoice-items-form-component :data="item.shipping_transaction" :section="section"></invoice-items-form-component>
<div class="row">
<div class="col">
<p class="no-margin fs-10 all-caps">Subtotal</p>
<div>{{ item.shipping_transaction.amount - item.shipping_transaction.service_charge - item.shipping_transaction.tax }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Service Charges</p>
<div>{{ item.shipping_transaction.service_charge }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Tax</p>
<div>{{ item.shipping_transaction.tax }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Total</p>
<div>{{ item.shipping_transaction.amount }}</div>
</div>
</div>
</div>
</div>
<div class="row b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded" v-if="item.shipping_transaction && invoice_status == 'Pending Payment'">
<div class="col-12 col-md-7 padding-20">
<div class="row bg-master-lightest h-100">
<div class="col">
<div class="row bg-master-lightest" v-if="item.shipping_transaction.payment_attempts.length">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="font-head fs-10 all-caps">Payment Attempt</div>
</div>
</div>
<div class="row">
<div class="col">
<shipping-transaction-component v-for="item in item.shipping_transaction.payment_attempts" v-bind:key="item.id" :data="item" ></shipping-transaction-component>
</div>
</div>
</div>
</div>
<div class="row bg-master-lightest" v-if="item.shipping_transaction.payment_history.length">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="font-head fs-10 all-caps">Payment History</div>
</div>
</div>
<div class="row">
<div class="col">
<payment-history-component v-for="item in item.shipping_transaction.payment_history" v-bind:key="item.id" :data="item" ></payment-history-component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-5 padding-20 parentContainer">
<div class="row bg-master-lightest h-100">
<div class="col">
<div class="row padding-10">
<div class="col">
<div class="row align-items-end m-b-10 text-complete">
<div class="col">
<div class="font-heading all-caps fs-12">Total Amount:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-success hide">
<div class="col">
<div class="font-heading all-caps fs-12">Paid Total:</div>
</div>
<div class="col-auto text-right">
<!-- <div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.paid_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div> -->
<div class="font-heading fs-12">Paid Total</div>
</div>
</div>
<div class="row align-items-end m-b-10 hide">
<div class="col">
<div class="font-heading all-caps fs-12">Floating Amount:</div>
</div>
<div class="col-auto text-right">
<!-- <div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.floating_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div> -->
<div class="font-heading fs-12">Floating Amount</div>
</div>
</div>
<div class="row align-items-end bold text-danger">
<div class="col">
<div class="font-heading all-caps fs-12">OutStanding Total:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.outstanding + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
</div>
</div>
<div class="row m-t-20" v-if="item.shipping_transaction.outstanding > 0">
<div class="col">
<div class="btn btn-sm all-caps b-rad-none btn-success btn-block requestModal" data-type="makePayment">Make Payment</div>
</div>
</div>
</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="makePayment">
<payment-form-component :data="item.shipping_transaction" :section="section"></payment-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
invoice_status: {
type: String,
required: true
},
section:{
type: String,
required: true
}
},
data(){
return {
parameters: {
packing_list_id: null,
transaction_details: [],
},
expanded: false,
}
},
computed: {
cbm () {
return (Math.ceil((this.item.packages.reduce((total, obj) => (obj.type === 2 ? 0 : obj.cbm) + total, 0)) * 1000) / 1000).toFixed(3)
},
overweight(){
return (Math.ceil((this.item.packages.reduce((total, obj) => (obj.type === 2 ? obj.cbm : 0) + total, 0)) * 1000) / 1000).toFixed(3)
}
},
created(){
this.parameters.packing_list_id = this.data.id;
},
methods: {
generateInvoice() {
this.submit(this.route('api.transaction.invoice.create'), 'post', this.section, true, true);
},
successHandler(response){
this.item = response.payload.data;
this.updateList();
}
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,293 @@
<template>
<div class="row m-b-15 m-l-5 m-r-10 parentContainer">
<div class="col bg-white rounded">
<div v-for="singleInvoice in invoices" class="row">
<div class="col padding-20">
<div class="row align-items-center">
<div class="col">
<p class="no-margin fs-10 all-caps">Marking</p>
<div class="no-margin">
<div v-if="item.order">
<a :href="route('customer.profile', item.order.company_module.marking)">{{item.order.company_module.marking}}</a>/<a :href="route('order.show', item.order.reference)">{{item.order.reference}}</a>
<p>{{item.loading_days_ago}}</p>
</div>
<div v-else class="text-danger">Unclaimed Packing List</div>
</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Container</p>
<div v-if="item.packages[0].container"> {{item.packages[0].container.container_reference}}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Invoice Date</p>
<div v-if="singleInvoice">({{ singleInvoice.bill_no }})</div>
<div v-if="!singleInvoice">n/a</div>
<div v-if="singleInvoice"> {{ singleInvoice.updated_at }}</div>
</div>
<div class="col">
<div v-if="item.order">
<p class="no-margin fs-10 all-caps">Address</p>
<div>{{ item.order.address.post_code_area}}</div>
<div>{{ item.order.address.district.name +' '+item.order.address.postcode+' '+item.order.address.state.name }}</div>
</div>
<div v-else class="text-danger">Unclaimed</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Status</p>
<div class="all-caps">{{ invoice_status }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Amount</p>
<div v-if="!singleInvoice">n/a</div>
<div v-if="singleInvoice">MYR {{ singleInvoice.amount.toFixed(2) }}</div>
</div>
<div class="col-auto p-l-0 p-r-0" v-if="['Pending Payment', 'Paid Invoice', 'Suspended Invoice'].includes(invoice_status)">
<div v-if="singleInvoice">
<div v-if="singleInvoice.documents.length">
<div v-for="file in singleInvoice.documents[0].files" v-bind:key="file.id" class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<div class="btn bg-grey no-border muted">
<i class="fa fa-file-pdf-o"></i>
</div>
</template>
</document-file-viewer-component>
</div>
</div>
<div v-else>
<div class="btn bg-grey no-border muted invisible">
<i class="fa fa-file-pdf-o"></i>
</div>
</div>
</div>
</div>
<div class="col-auto">
<div class="row text-center parentContainer m-b-10" v-if="['Pending Invoice'].includes(invoice_status) && !item.receive_packing_list" >
<div class="col">
<div class="requestModal pointer btn btn-primary btn-xs" data-type="addWarehouseTransport">
Add Warehouse Transport
</div>
<modal-component class="animate_animated animatefast animate_fadeIn" styleType="fill-in" type="addWarehouseTransport">
<create-warehouse-transport-form-component :packinglistId="item.id" :section="section"></create-warehouse-transport-form-component>
</modal-component>
</div>
</div>
<div class="row">
<div class="col">
<div class="btn bg-grey no-border" @click="expanded = !expanded" v-if="['Pending Approval', 'Pending Payment'].includes(invoice_status)">
<i class="fa" :class="{'fa-angle-down': !expanded, 'fa-angle-up': expanded}" ></i>
</div>
<div class="btn bg-grey no-border muted requestModal" v-if="singleInvoice && invoice_status == 'Pending Approval'" data-type="confirmInvoice">
<i class="fa fa-check fs-12"></i>
</div>
<div class="btn bg-grey no-border muted requestModal" v-if="singleInvoice && invoice_status == 'Pending Approval'" data-type="deleteInoive">
<i class="fa fa-close fs-12"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="confirmInvoice">
<approve-shipping-invoice-form-component :section="section" :data="data"></approve-shipping-invoice-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteInoive">
<delete-shipping-invoice-form-component :section="section" :data="data"></delete-shipping-invoice-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded" v-if="invoice_status == 'Pending Payment'">
<div class="col-12 col-md-7 padding-20">
<div class="row bg-master-lightest h-100">
<div class="col">
<div v-for="(groupItem, index) in invoices" class="row bg-master-lightest" v-if="groupItem.groups_payment_attempts.length && groupItem.type == 1 && groupTransactionsExist(invoices, groupItem.groups_payment_attempts)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Attempt</div>
</div>
</div>
<div class="row">
<div class="col">
<single-group-payment-transaction-details-component v-for="group in groupItem.groups_payment_attempts" v-bind:key="group.id" :data="group" :section="section"></single-group-payment-transaction-details-component>
</div>
</div>
</div>
</div>
<div v-for="(groupItem, index) in invoices" class="row bg-master-lightest" v-if="groupItem.groups_payment_expired.length && groupItem.type == 1 && groupTransactionsExist(invoices, groupItem.groups_payment_expired)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Expired</div>
</div>
</div>
<div class="row">
<div class="col">
<payment-expired-component v-for="group in groupItem.groups_payment_expired" v-bind:key="group.id" :data="group" :section="section"></payment-expired-component>
</div>
</div>
</div>
</div>
<div v-for="(groupItem, index) in invoices" class="row bg-master-lightest" v-if="groupItem.groups_payment_history.length && groupItem.type == 1 && groupTransactionsExist(invoices, groupItem.groups_payment_history)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment History</div>
</div>
</div>
<div class="row">
<div class="col">
<single-group-payment-history-transaction-details-component v-for="group in groupItem.groups_payment_history" v-bind:key="group.id" :data="group" :section="section"></single-group-payment-history-transaction-details-component>
</div>
</div>
</div>
</div>
<div v-for="(paymentAttemptItem, index) in invoices" class="row bg-master-lightest" v-if="invoices.some(x => x.payment_attempts && x.payment_attempts.length > 0)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Attempt</div>
</div>
</div>
<div class="row">
<div class="col">
<shipping-transaction-component v-for="i in paymentAttemptItem.payment_attempts" v-bind:key="i.id" :data="i" :section="section"></shipping-transaction-component>
</div>
</div>
</div>
</div>
<div v-for="(paymentExpiredItem, index) in invoices" class="row bg-master-lightest" v-if="invoices.some(x => x.payments_expired && x.payments_expired.length > 0)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Expired</div>
</div>
</div>
<div class="row">
<div class="col">
<payment-expired-component v-for="i in paymentExpiredItem.payments_expired" v-bind:key="i.id" :data="i" ></payment-expired-component>
</div>
</div>
</div>
</div>
<div v-for="(paymentHistoryItem, index) in invoices" class="row bg-master-lightest" v-if="invoices.some(x => x.payment_history && x.payment_history.length > 0)">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment History</div>
</div>
</div>
<div class="row">
<div class="col">
<payment-history-component v-for="i in paymentHistoryItem.payment_history" v-bind:key="i.id" :data="i" ></payment-history-component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-5 padding-20 parentContainer">
<div class="row bg-master-lightest h-100">
<div class="col">
<div class="row padding-10">
<div class="col">
<div class="row align-items-end m-b-10 text-complete">
<div class="col">
<div class="font-heading all-caps fs-12">Total Amount:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">MYR {{(Math.round((totalAmount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
</div>
</div>
<div class="row align-items-end bold text-danger">
<div class="col">
<div class="font-heading all-caps fs-12">OutStanding Total:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">MYR {{(Math.round((totalOutstanding + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
</div>
</div>
<div class="row m-t-20" v-if="totalOutstanding - totalFloating > 0.009">
<div class="col">
<div class="btn btn-sm all-caps b-rad-none btn-success btn-block requestModal" data-type="makePayment">Make Payment</div>
</div>
</div>
</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="makePayment">
<group-payment-form-component :section="section" :selectedIds="selectedIds" :sumAmount="totalOutstanding.toFixed(2)" :showManualPayment="true"></group-payment-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
invoices: {
type: Array,
},
invoice_status: {
type: String,
required: true
},
section:{
type: String,
required: true
},
storage:{
type: Object
},
},
data(){
return {
parameters: {
packing_list_id: null,
transaction_details: [],
},
expanded: false,
}
},
computed: {
totalAmount() {
return this.invoices.reduce((sum, item) => sum + item.amount, 0);
},
totalOutstanding() {
return this.invoices.reduce((sum, item) => sum + item.outstanding, 0);
},
totalFloating() {
return this.invoices.reduce((sum, item) => sum + item.floating, 0);
},
selectedIds() {
const ids = [];
this.invoices.forEach(item => {
ids.push(item.id);
});
return ids;
},
},
created(){
this.parameters.packing_list_id = this.data.id;
},
methods: {
successHandler(response){
this.item = response.payload.data;
this.updateList();
},
groupTransactionsExist(items, groups) {
return groups.some(group =>
group.transactions_ids.length === items.length &&
group.transactions_ids.every(transaction =>
items.some(item => item.id === transaction.transaction_id)
)
);
}
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,324 @@
<template>
<div class="row m-b-15 m-l-5 m-r-10 parentContainer">
<div class="col bg-white rounded">
<div class="row">
<div class="col padding-20">
<div class="row align-items-center">
<div class="col">
<p class="no-margin fs-10 all-caps">Marking</p>
<div class="no-margin">
<div v-if="item.order">
<a :href="route('customer.profile', item.order.company_module.marking)">{{item.order.company_module.marking}}</a>/<a :href="route('order.show', item.order.reference)">{{item.order.reference}}</a>
<p>{{item.loading_days_ago}}</p>
</div>
<div v-else class="text-danger">Unclaimed Packing List</div>
</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Container</p>
<div v-if="item.packages[0].container"> {{item.packages[0].container.container_reference}}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Invoice Date</p>
<div v-if="item.shipping_transaction">({{ item.shipping_transaction.bill_no }})</div>
<div v-if="!item.shipping_transaction">n/a</div>
<div v-if="item.shipping_transaction">{{ item.shipping_transaction.updated_at }}</div>
</div>
<div class="col">
<div v-if="item.order">
<p class="no-margin fs-10 all-caps">Address</p>
<div>{{ item.order.address.post_code_area}}</div>
<div>{{ item.order.address.district.name +' '+item.order.address.postcode+' '+item.order.address.state.name }}</div>
</div>
<div v-else class="text-danger">Unclaimed</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Status</p>
<div class="all-caps">{{ invoice_status }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Amount</p>
<div v-if="!item.shipping_transaction">n/a</div>
<div v-if="item.shipping_transaction">MYR {{ item.shipping_transaction.amount.toFixed(2) }}</div>
</div>
<div class="col-auto p-l-0 p-r-0" v-if="['Pending Payment', 'Paid Invoice', 'Suspended Invoice'].includes(invoice_status)">
<div v-if="item.shipping_transaction">
<div v-if="item.shipping_transaction.documents.length">
<div v-for="file in item.shipping_transaction.documents[0].files" v-bind:key="file.id" class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
<div class="btn bg-grey no-border muted">
<i class="fa fa-file-pdf-o"></i>
</div>
</template>
</document-file-viewer-component>
</div>
</div>
<div v-else>
<div class="btn bg-grey no-border muted invisible">
<i class="fa fa-file-pdf-o"></i>
</div>
</div>
</div>
</div>
<div class="col-auto">
<div class="row text-center parentContainer m-b-10" v-if="['Pending Invoice'].includes(invoice_status) && !item.receive_packing_list" >
<div class="col">
<div class="requestModal pointer btn btn-primary btn-xs" data-type="addWarehouseTransport">
Add Warehouse Transport
</div>
<modal-component class="animate_animated animatefast animate_fadeIn" styleType="fill-in" type="addWarehouseTransport">
<create-warehouse-transport-form-component :packinglistId="item.id" :section="section"></create-warehouse-transport-form-component>
</modal-component>
</div>
</div>
<div class="row">
<div class="col">
<div class="btn bg-grey no-border" @click="expanded = !expanded" v-if="['Pending Approval', 'Pending Payment'].includes(invoice_status)">
<i class="fa" :class="{'fa-angle-down': !expanded, 'fa-angle-up': expanded}" ></i>
</div>
<div v-if="!item.shipping_transaction">
<div v-if="item.order">
<div v-if="!item.order.company_module.billingAddress">
<div class="btn btn-primary btn-xs pointer requestModal btn-block" data-type="billingAddressComponent">Add Billing Address</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" size="extra-large" styleType="fill-in" type="billingAddressComponent">
<address-form-component :id="item.order.company_module.id" :section="section" :type=1></address-form-component>
</modal-component>
</div>
<div class="row" v-if="item.order.company_module.billingAddress">
<div class="col">
<div class="col-auto requestModal pointer" data-type="editBillingAddress">
<i class="fa fa-edit pointer fa-fw m-l-5"></i> Edit billing Address
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" size="extra-large" styleType="fill-in" type="editBillingAddress">
<div class="row">
<div class="col bg-white">
<address-form-component :id="item.order.company_module.id" :data="item.order.company_module.billingAddress" section="editBillingAddress"></address-form-component>
</div>
</div>
</modal-component>
</div>
</div>
<div v-if="!item.order.address.post_code_area">
<div class="btn btn-xs btn-primary pointer m-t-10 requestModal btn-block" data-type="defineLocation">Define Location</div>
<modal-component class="animate_animated animatefast animate_fadeIn" styleType="fill-in" type="defineLocation">
<declare-postcode-area-form-component :data="item.order.address" :section="section"></declare-postcode-area-form-component>
</modal-component>
</div>
<div v-if="item.order.company_module.billingAddress && item.order.address.post_code_area" class="btn btn-outline-primary btn-lg pointer" @click="generateInvoice()">Generate Invoice</div>
</div>
<div v-else>
<div class="btn btn-outline-primary btn-lg pointer invisible">Generate Invoice</div>
</div>
</div>
<div class="btn bg-grey no-border muted requestModal" v-if="item.shipping_transaction && invoice_status == 'Pending Approval'" data-type="confirmInvoice">
<i class="fa fa-check fs-12"></i>
</div>
<div class="btn bg-grey no-border muted requestModal" v-if="item.shipping_transaction && invoice_status == 'Pending Approval'" data-type="deleteInoive">
<i class="fa fa-close fs-12"></i>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="confirmInvoice">
<approve-shipping-invoice-form-component :section="section" :data="data"></approve-shipping-invoice-form-component>
</modal-component>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteInoive">
<delete-shipping-invoice-form-component :section="section" :data="data"></delete-shipping-invoice-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded" v-if="item.shipping_transaction && ['Pending Approval', 'Suspended Invoice'].includes(invoice_status)">
<div class="col p-b-10">
<div class="row">
<div class="col p-l-20 p-r-20 p-t-10 p-b-10">
<div class="row align-items-center">
<div class="col">
<p class="no-margin fs-10 all-caps">Container Reference</p>
<div v-if="item.packages.length">{{ item.packages[0].container ? item.packages[0].container.container_reference : 'n/a' }}</div>
<div v-else>n/a</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Due Date</p>
<div v-if="item.packages.length">
<div v-if="item.packages[0].container">
<div v-if="item.packages[0].container.transport">{{ item.packages[0].container.transport.drop_date == null ? item.packages[0].container.transport.current_schedule.etd : item.packages[0].container.transport.drop_date }}</div>
<div v-else>n/a</div>
</div>
<div v-else>n/a</div>
</div>
<div v-else>n/a</div>
</div>
<!-- <div class="col">
<p class="no-margin fs-10 all-caps">Paid Date</p>
<div>n/a</div>
</div> -->
<div class="col">
<p class="no-margin fs-10 all-caps">Total CBM</p>
<div>{{ (parseFloat(cbm) + parseFloat(overweight)).toFixed(3) }}</div>
</div>
</div>
</div>
</div>
<h5 class="text-underline text-center">Invoice Details</h5>
<invoice-items-form-component :data="item.shipping_transaction" :section="section"></invoice-items-form-component>
<div class="row">
<div class="col">
<p class="no-margin fs-10 all-caps">Subtotal</p>
<div>{{ item.shipping_transaction.amount - item.shipping_transaction.service_charge - item.shipping_transaction.tax }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Service Charges</p>
<div>{{ item.shipping_transaction.service_charge }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Tax</p>
<div>{{ item.shipping_transaction.tax }}</div>
</div>
<div class="col">
<p class="no-margin fs-10 all-caps">Total</p>
<div>{{ item.shipping_transaction.amount }}</div>
</div>
</div>
</div>
</div>
<div class="row b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded" v-if="item.shipping_transaction && invoice_status == 'Pending Payment'">
<div class="col-12 col-md-7 padding-20">
<div class="row bg-master-lightest h-100">
<div class="col">
<div class="row bg-master-lightest" v-if="item.shipping_transaction.payment_attempts.length">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="font-head fs-10 all-caps">Payment Attempt</div>
</div>
</div>
<div class="row">
<div class="col">
<shipping-transaction-component v-for="item in item.shipping_transaction.payment_attempts" v-bind:key="item.id" :data="item" ></shipping-transaction-component>
</div>
</div>
</div>
</div>
<div class="row bg-master-lightest" v-if="item.shipping_transaction.payment_history.length">
<div class="col">
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="font-head fs-10 all-caps">Payment History</div>
</div>
</div>
<div class="row">
<div class="col">
<payment-history-component v-for="item in item.shipping_transaction.payment_history" v-bind:key="item.id" :data="item" ></payment-history-component>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-5 padding-20 parentContainer">
<div class="row bg-master-lightest h-100">
<div class="col">
<div class="row padding-10">
<div class="col">
<div class="row align-items-end m-b-10 text-complete">
<div class="col">
<div class="font-heading all-caps fs-12">Total Amount:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
</div>
</div>
<div class="row align-items-end m-b-10 text-success hide">
<div class="col">
<div class="font-heading all-caps fs-12">Paid Total:</div>
</div>
<div class="col-auto text-right">
<!-- <div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.paid_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div> -->
<div class="font-heading fs-12">Paid Total</div>
</div>
</div>
<div class="row align-items-end m-b-10 hide">
<div class="col">
<div class="font-heading all-caps fs-12">Floating Amount:</div>
</div>
<div class="col-auto text-right">
<!-- <div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.floating_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div> -->
<div class="font-heading fs-12">Floating Amount</div>
</div>
</div>
<div class="row align-items-end bold text-danger">
<div class="col">
<div class="font-heading all-caps fs-12">OutStanding Total:</div>
</div>
<div class="col-auto text-right">
<div class="font-heading fs-12">MYR {{(Math.round((item.shipping_transaction.outstanding + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
</div>
</div>
<div class="row m-t-20" v-if="item.shipping_transaction.outstanding > 0">
<div class="col">
<div class="btn btn-sm all-caps b-rad-none btn-success btn-block requestModal" data-type="makePayment">Make Payment</div>
</div>
</div>
</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="makePayment">
<payment-form-component :data="item.shipping_transaction" :section="section"></payment-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
invoice_status: {
type: String,
required: true
},
section:{
type: String,
required: true
}
},
data(){
return {
parameters: {
packing_list_id: null,
transaction_details: [],
},
expanded: false,
}
},
computed: {
cbm () {
return (Math.ceil((this.item.packages.reduce((total, obj) => (obj.type === 2 ? 0 : obj.cbm) + total, 0)) * 1000) / 1000).toFixed(3)
},
overweight(){
return (Math.ceil((this.item.packages.reduce((total, obj) => (obj.type === 2 ? obj.cbm : 0) + total, 0)) * 1000) / 1000).toFixed(3)
}
},
created(){
this.parameters.packing_list_id = this.data.id;
},
methods: {
generateInvoice() {
this.submit(this.route('api.transaction.invoice.create'), 'post', this.section, true, true);
},
successHandler(response){
this.item = response.payload.data;
this.updateList();
}
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,73 @@
<template>
<div class="row m-l-0 m-b-10 m-r-0 parentContainer">
<div class="col-auto bg-master-lighter requestModal pointer" data-type="deleteAttempt">
<div class="row align-items-center h-100">
<div class="col">
<i class="fa fa-times muted"></i>
</div>
</div>
</div>
<div class="col bg-white p-t-10 p-b-10 p-r-0">
<div class="row m-b-5">
<div class="col-auto">
<div class="font-heading fs-8 muted all-caps">Payment Amount</div>
<div class="font-heading fs-10 bold">
MYR {{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
</div>
</div>
</div>
</div>
<div class="col-auto p-l-5 p-r-5 bg-success pointer" v-if="item.payment_method === 5">
<a :href="route('billplz.bill', item.payment_reference)">
<div class="row align-items-center h-100">
<div class="col">
<i class="fa fa-repeat fs-20 text-white p-l-10 p-r-10"></i>
</div>
</div>
</a>
</div>
<div class="col-auto p-l-5 p-r-5 bg-success requestModal pointer" v-if="item.payment_method !== 5" data-type="paymentProofModal">
<div @click="selectedID(item.payment_transaction.id)" class="row align-items-center h-100">
<div class="col">
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="30" height="30" viewBox="0 0 172 172" style=" fill:#000000;"><defs><linearGradient x1="86" y1="70.76994" x2="86" y2="116.46013" gradientUnits="userSpaceOnUse" id="color-1_52139_gr1"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient><linearGradient x1="61.8125" y1="34.48869" x2="61.8125" y2="144.97181" gradientUnits="userSpaceOnUse" id="color-2_52139_gr2"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient><linearGradient x1="130.34375" y1="34.48869" x2="130.34375" y2="144.97181" gradientUnits="userSpaceOnUse" id="color-3_52139_gr3"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient><linearGradient x1="86" y1="32.25" x2="86" y2="148.71013" gradientUnits="userSpaceOnUse" id="color-4_52139_gr4"><stop offset="0" stop-color="#ffffff"></stop><stop offset="1" stop-color="#ffffff"></stop></linearGradient></defs><g fill="none" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><path d="M0,172v-172h172v172z" fill="none"></path><g><path d="M102.45825,99.43213h-5.70825c-1.4835,0 -2.6875,1.16637 -2.6875,2.65256v8.10013c0,1.48081 -1.19862,2.68481 -2.67944,2.68481h-10.76612c-1.48081,0 -2.67944,-1.204 -2.67944,-2.68481v-8.10013c0,-1.48619 -1.204,-2.65256 -2.6875,-2.65256h-5.70825c-1.93769,0 -3.04225,-2.39188 -1.88125,-4.05275l14.577,-20.855c1.8275,-2.61494 5.69481,-2.61763 7.52231,-0.00538l14.577,20.86306c1.16369,1.66088 0.05644,4.05006 -1.87856,4.05006z" fill="url(#color-1_52139_gr1)"></path><path d="M51.0625,67.1875h5.375c0,-8.0625 7.23206,-16.12231 16.125,-16.12231v-5.375c-11.85456,0 -21.5,10.74731 -21.5,21.49731z" fill="url(#color-2_52139_gr2)"></path><path d="M139.75,80.625c0,-10.75 -8.44144,-18.80981 -18.8125,-18.80981v5.375c7.40944,0 13.4375,5.37231 13.4375,13.43481z" fill="url(#color-3_52139_gr3)"></path><path d="M148.09738,92.27263c1.59369,-3.68188 2.40263,-7.59219 2.40263,-11.64494c0,-16.29969 -13.26281,-29.5625 -29.5625,-29.5625c-6.5145,0 -12.68769,2.08819 -17.78588,5.96088c-4.30269,-13.03438 -16.52006,-22.08587 -30.58912,-22.08587c-17.78319,0 -32.25,14.46681 -32.25,32.25c0,4.14681 0.16662,8.05981 1.42437,10.74731h-1.42437c-13.33806,0 -24.1875,10.84944 -24.1875,24.1875c0,11.56431 8.16194,21.24737 19.0275,23.62044c1.02394,6.39894 6.53869,11.31706 13.2225,11.31706h56.4375h16.125h5.375c6.54944,0 12.00238,-4.71388 13.18488,-10.92469c9.2235,-1.20131 16.37762,-9.08913 16.37762,-18.63513c0,-6.09256 -2.924,-11.72019 -7.77762,-15.23006zM126.3125,131.6875h-5.375h-16.125h-56.4375c-3.49912,0 -6.45538,-2.6875 -7.568,-5.375h93.0735c-1.11263,2.6875 -4.06888,5.375 -7.568,5.375zM137.0625,120.9375h-96.75c-10.37106,0 -18.8125,-8.44144 -18.8125,-18.8125c0,-10.37106 8.44144,-18.8125 18.8125,-18.8125h9.51375l-1.68506,-3.78131c-2.23063,-5.01488 -2.45369,-7.48737 -2.45369,-12.341c0,-14.81888 12.05613,-26.875 26.875,-26.875c13.01825,0 24.13106,9.29875 26.42619,22.11275l0.92719,5.17075l3.64962,-3.77325c4.60638,-4.76225 10.77687,-7.38525 17.372,-7.38525c13.33806,0 24.1875,10.84944 24.1875,24.1875c0,3.6765 -0.81431,7.21056 -2.37575,10.41944l-1.763,3.32713l2.37037,1.26044c4.40481,2.34081 7.14338,6.88806 7.14338,11.868c0,7.40944 -6.02806,13.43481 -13.4375,13.43481z" fill="url(#color-4_52139_gr4)"></path></g></g></svg>
</div>
</div>
</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteAttempt">
<delete-payment-attempt-form-component :data="item" :section="section" class="text-center"></delete-payment-attempt-form-component>
</modal-component>
<modal-component type="paymentProofModal">
<payment-verification-form-component v-if="selected_id === item.payment_transaction.id" :section="section" :data="item"></payment-verification-form-component>
</modal-component>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
data(){
return {
expandPaymentDetails: false,
amount: (Math.round(1000 * 100) / 100).toFixed(2),
selected_id: '',
parameters: {
amount: (Math.round(1000 * 100) / 100).toFixed(2),
bank_id: 1
},
section: 'bookingDetailSection',
}
},
mounted() {
this.item.id = this.item.payment_transaction.id;
},
methods: {
clickExpand(){
this.expandPaymentDetails = !this.expandPaymentDetails;
},
selectedID(id){
this.selected_id = id;
}
},
mixins: [componentHandler]
}
</script>
@@ -0,0 +1,77 @@
<template>
<div class="row m-b-15 m-l-5 m-r-10 parentContainer">
<div class="col bg-white rounded" v-if="item.storages && item.storages.length > 0">
<div class="row" v-for="invoice in item.invoices" v-if="invoice.type === 1">
<div class="col" v-if="getMergedInvoices(invoice).length > 1">
<single-admin-payments-billing-with-storage-component :data="item" :invoices="getMergedInvoices(invoice)" :storage="getStorageInfo(invoice)" :invoice_status="invoice_status" :section="section" ></single-admin-payments-billing-with-storage-component>
</div>
</div>
</div>
<div class="col bg-white rounded" v-else>
<div class="row">
<div class="col">
<single-admin-payments-billing-without-storage-component :data="item" :invoice_status="invoice_status" :section="section" ></single-admin-payments-billing-without-storage-component>
</div>
</div>
</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
invoice_status: {
type: String,
required: true
},
section:{
type: String,
required: true
}
},
computed: {
orderStorageMap() {
// Create a map to link parent invoice IDs to storageInvoiceIds
const storageMap = {};
if(this.item.storages){
for (const storage of this.item.storages) {
storageMap[storage.parentInvoiceId] = storage.storageInvoiceId;
}
}
return storageMap;
}
},
methods: {
getStorageInfo(invoice) {
const storageObject = this.findStorageInfoObject(invoice.id);
return storageObject;
},
findStorageInfoObject(shippingInvoiceId) {
if(this.item.storages){
return this.item.storages.find(storage => storage.parentInvoiceId === shippingInvoiceId);
}
return null;
},
getMergedInvoices(invoice){
const storageInvoice = this.getStorageInvoice(invoice);
const mergedArray = [
...(invoice ? [invoice] : []),
...(storageInvoice ? [storageInvoice] : []),
];
return mergedArray;
},
getStorageInvoice(invoice) {
// Retrieve the storageInvoiceId for the current shipping invoice from storage info (order.storages)
const storageInvoiceId = this.orderStorageMap[invoice.id];
// Find and return the storage invoice (type 16) from order.invoices
const storageInvoice = this.item.invoices.find((invoice) => {
return invoice.type === 16 && invoice.id === storageInvoiceId;
});
return storageInvoice;
},
},
mixins: [componentHandler]
}
</script>
@@ -55,7 +55,7 @@
</div>
</div>
</div>
<div v-if="$store.getters.isSuperAdmin">
<div v-if="$store.getters.isSuperAdmin | showManualPayment">
<div class="row m-t-10 m-b-10">
<div class="col text-center"><div id="expend-method" class="padding-10 bg-master-lightest pointer" @click="$store.dispatch('toggleSection', {name: 'otherPaymentMethods', status: !$store.getters.isShowing('otherPaymentMethods')})"><i class="fa m-r-10" :class="[{'fa-angle-up': $store.getters.isShowing('otherPaymentMethods')}, {'fa-angle-down': !$store.getters.isShowing('otherPaymentMethods')}]"></i>{{$store.getters.isShowing('otherPaymentMethods') ? 'Hide' : 'Show'}} Alternative Methods <i class="fa m-l-10" :class="[{'fa-angle-up': $store.getters.isShowing('otherPaymentMethods')}, {'fa-angle-down': !$store.getters.isShowing('otherPaymentMethods')}]"></i></div></div>
</div>
@@ -170,6 +170,10 @@
type: Array,
required: true,
},
showManualPayment: {
type: Boolean,
default: false
},
},
data(){
return {
@@ -0,0 +1,30 @@
<template>
<div class="row" @keyup.enter="submitForm">
<div class="col bg-white padding-40 b-rad-lg">
<div class="row">
<div class="col text-center">
<div class="row m-b-20">
<div class="col">
<h5 class="all-caps">Waive Invoice</h5>
<div class="fs-11">This action cannot be undone. Are you sure you want to waive this invoice (MYR {{(Math.round((data.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}})?</div>
</div>
</div>
<div class="row">
<div class="col p-r-5">
<div data-dismiss="modal" class="btn btn-sm btn-default bg-master-lighter btn-block b-rad-none">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-danger w-100 btn-sm" @click="submit(route('api.transaction.waive', data.id), 'post', section, true, true)">Confirm</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import modalFormHandler from '../../../general/mixins/modalFormHandler';
export default {
mixins: [modalFormHandler]
}
</script>
File diff suppressed because one or more lines are too long
@@ -6,7 +6,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 m-r-5 bg-master-lighter tabButton" :class="{'active': item ? item.status === 2 : true }" tab-name="pendingInvoice">
<div class="col p-t-20 p-b-20 m-r-5 bg-master-lighter tabButton" :class="{'active': item ? item.status === 2 : true }" tab-name="pendingInvoice" @click="setActiveTab($event)">
<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"
@@ -29,7 +29,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 m-r-5 bg-master-lighter tabButton" tab-name="pendingApproval">
<div class="col p-t-20 p-b-20 m-r-5 bg-master-lighter tabButton" tab-name="pendingApproval" @click="setActiveTab($event)">
<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"
@@ -52,7 +52,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 m-r-5 bg-master-lighter tabButton" tab-name="pendingPayment">
<div class="col p-t-20 p-b-20 m-r-5 bg-master-lighter tabButton" tab-name="pendingPayment" @click="setActiveTab($event)">
<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"
@@ -78,7 +78,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="paidInvoice">
<div class="col p-t-20 p-b-20 bg-master-lighter tabButton" tab-name="paidInvoice" @click="setActiveTab($event)">
<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"
@@ -100,25 +100,25 @@
</div>
<div class="row no-margin">
<div class="col bg-master-lightest p-1 p-sm-4">
<div class="row tabsContainer tabContent" tab-name="pendingInvoice">
<div class="row tabsContainer tabContent" tab-name="pendingInvoice" v-if="isActiveTab('pendingInvoice') || showTabContent('pendingInvoice')">
<div class="col">
<admin-payments-billing-with-search-component :options="{does_not_have_transaction_type: 1, type: 2, per_page: 5}" section="pendingInvoiceSection" invoice_status="Pending Invoice"></admin-payments-billing-with-search-component>
</div>
</div>
<div class="row tabsContainer tabContent hide" tab-name="pendingApproval">
<div class="row tabsContainer tabContent hide" tab-name="pendingApproval" v-if="isActiveTab('pendingApproval') || showTabContent('pendingApproval')">
<div class="col">
<admin-payments-billing-with-search-component :options="{has_invoice_status_in: [0, 1], per_page: 5}" section="pendingApprovalSection" invoice_status="Pending Approval"></admin-payments-billing-with-search-component>
</div>
</div>
<div class="row tabsContainer tabContent hide" tab-name="pendingPayment">
<div class="row tabsContainer tabContent hide" tab-name="pendingPayment" v-if="isActiveTab('pendingPayment') || showTabContent('pendingPayment')">
<div class="col">
<payment-filter-component :endpoint="route('api.packing_list.list')" :data="data"></payment-filter-component>
<admin-payments-billing-with-search-component :options="{has_invoice_status_in: [2], packing_list_ordered_by_invoice_date: true, per_page: 5}" section="pendingPaymentSection" invoice_status="Pending Payment" :with_export="true"></admin-payments-billing-with-search-component>
<admin-payments-billing-with-search-component :options="{has_invoice_status_in: [2], packing_list_ordered_by_invoice_date: true, per_page: 5, check_for_storage_invoice: 1}" section="pendingPaymentSection" invoice_status="Pending Payment" :with_export="true"></admin-payments-billing-with-search-component>
</div>
</div>
<div class="row tabsContainer tabContent hide" tab-name="paidInvoice">
<div class="row tabsContainer tabContent hide" tab-name="paidInvoice" v-if="isActiveTab('paidInvoice') || showTabContent('paidInvoice')">
<div class="col">
<admin-payments-billing-with-search-component :options="{has_invoice_status_in: [3], per_page: 5}" section="paidInvoiceSection" invoice_status="Paid Invoice" :with_export="true"></admin-payments-billing-with-search-component>
<admin-payments-billing-with-search-component :options="{has_invoice_status_in: [3], per_page: 5, check_for_storage_invoice: 1}" section="paidInvoiceSection" invoice_status="Paid Invoice" :with_export="true"></admin-payments-billing-with-search-component>
</div>
</div>
</div>
@@ -128,7 +128,14 @@
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
import tabHandler from '../../../general/mixins/tabHandler';
export default {
mixins: [componentHandler]
data() {
return {
activeTab: 'pendingInvoice',
displayedTabs: ['pendingInvoice'],
};
},
mixins: [componentHandler, tabHandler]
}
</script>
@@ -1,5 +1,5 @@
<template>
<div class="row m-b-15 parentContainer">
<div class="row m-b-15 parentContainer" :class="{'d-none': searchValue != data.postcode && searchValue != ''}">
<div class="col">
<div class="row align-items-center">
<div class="col-auto p-r-0">
@@ -55,6 +55,10 @@
type: Object,
required: false
},
searchValue: {
type: String,
default: null
},
section: {
default: ''
},
@@ -0,0 +1,138 @@
<template>
<div class="row parentContainer">
<div class="col">
<div class="row" v-if="!returnData">
<div class="col">
<div class="row m-b-20">
<div class="col">
<div class="btn btn-sm btn-primary rounded b-rad-none"
@click="isUploadingCsv = !isUploadingCsv">{{ isUploadingCsv ? "Cancel" : "Upload Excel" }}
</div>
<div class="btn btn-sm btn-primary rounded b-rad-none requestModal"
data-type="addPermitsReminder" v-if="!isUploadingCsv"><i class="fa fa-plus m-r-5"></i>Add
Permits</div>
<modal-component class="animate__animated animate__fast animate__fadeIn"
type="addPermitsReminder">
<add-permits-reminder-form-component
:section="section"></add-permits-reminder-form-component>
</modal-component>
<div class="btn btn-sm btn-default rounded b-rad-none" @click="refreshData"><i class="fa fa-refresh"></i></div>
</div>
</div>
<div class="row m-b-50" v-if="isUploadingCsv">
<div class="col-12 col-md-6">
<div class="row bg-white">
<div class="col">
<file-input-component :validator="$v.files" v-model="files">
<template slot="label">
<div class="font-heading fs-11 all-caps">Permits Reminder Csv</div>
<div class="fs-8 all-caps d-block m-t-10"><a :href="excelFileUrl" download>Click
HERE to download the template</a></div>
</template>
</file-input-component>
</div>
</div>
<div class="row m-t-20">
<div class="col">
<div class="row">
<div class="col">
<button type="button"
class="btn btn-block p-t-10 p-b-10 p-r-35 p-l-35 btn-success b-rad-none"
@click="submitForm">Upload</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else>
<div class="font-heading fs-16 all-caps bold m-b-15">Permits Reminder</div>
<list-component :key="key" :section="section" :endpoint="route('api.permits_reminder.list')">
<template slot="list" slot-scope="{data}">
<permits-reminder-item-component :data="data"
:section="section"></permits-reminder-item-component>
</template>
</list-component>
</div>
</div>
</div>
<div v-if="returnData">
<div class="font-heading fs-16 all-caps bold m-b-15">Upload Summary</div>
<div class="font-heading fs-12 all-caps bold">Total Completed Rows: {{ returnData.completedRows }}</div>
<div class="font-heading fs-12 all-caps bold m-b-15">Total Failed Rows: {{ returnData.failedRows }}</div>
<div v-if="returnData.failedRows">
<div class="font-heading fs-12 all-caps m-b-15">Please copy error below into a excel file and try again
after it has been corrected.</div>
<table class="w-100 table table-bordered">
<tr>
<th class="text-capitalize" v-for="key in returnDataKeys"> {{ key }}</th>
</tr>
<tr v-for="row in returnData.data">
<td v-for="col in row" :class="[{ 'text-danger': row['status'] == 'failed' }]">
{{ col }}
</td>
</tr>
</table>
</div>
<div class="fs-12 font-arial m-b-0 pointer bold m-t-20 text-primary" @click="refreshData()">Click here to
refresh</div>
</div>
</div>
</div>
</template>
<script>
import { required } from "vuelidate/lib/validators";
export default {
props: {
section: {
type: String,
required: true
}
},
data() {
return {
files: [],
parameters: {},
returnData: null,
data: null,
key: 1,
isUploadingCsv: false,
}
},
computed: {
returnDataKeys() {
return this.returnData ? Object.keys(Object.assign({}, this.returnData.data[0])) : 0;
},
excelFileUrl() {
return `${window.location.origin}/` + 'excel-templates/permits_reminder_template.xlsx';
}
},
validations: {
files: {
required
},
},
methods: {
submitForm() {
this.parameters = {
files: this.files,
};
this.submit(this.route('api.permits_reminder.upload'), 'post', this.section, true, true);
},
errorHandler(error) {
console.log(error);
},
successHandler(response) {
console.log(response);
this.returnData = response;
},
refreshData() {
this.returnData = null;
this.isUploadingCsv = false;
this.key++;
}
},
}
</script>

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