diff --git a/app/Classes/General/Eloquent/Filters/DoesNotHaveSegments.php b/app/Classes/General/Eloquent/Filters/DoesNotHaveSegments.php new file mode 100644 index 00000000..88a1ab3d --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/DoesNotHaveSegments.php @@ -0,0 +1,21 @@ +whereDoesntHave('segments', function ($segment) use ($value) { + $segment->whereIn('id', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/IssuerNot.php b/app/Classes/General/Eloquent/Filters/IssuerNot.php new file mode 100644 index 00000000..77423b5d --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/IssuerNot.php @@ -0,0 +1,20 @@ +where('issuer', '!=', $value); + } + +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/SegmentsIn.php b/app/Classes/General/Eloquent/Filters/SegmentsIn.php new file mode 100644 index 00000000..3f756810 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/SegmentsIn.php @@ -0,0 +1,21 @@ +whereHas('segments', function ($segment) use ($value) { + $segment->whereIn('id', $value); + }); + } +} diff --git a/app/Classes/General/Eloquent/Filters/WithOutTransactions.php b/app/Classes/General/Eloquent/Filters/WithOutTransactions.php new file mode 100644 index 00000000..3b551f65 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WithOutTransactions.php @@ -0,0 +1,20 @@ +whereDoesntHave('transactions'); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/WithTotalPayments.php b/app/Classes/General/Eloquent/Filters/WithTotalPayments.php new file mode 100644 index 00000000..d93e55e2 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WithTotalPayments.php @@ -0,0 +1,25 @@ +leftJoin('bookings', 'companies.id', '=', 'bookings.company_id')->rightJoin('transactions', function ($join) { + $join->on('bookings.id', '=', 'transactions.booking_id')->where('transactions.type', TransactionType::PAYMENT)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + })->addSelect(['companies.*', DB::raw('SUM(transactions.amount) as total_payments')])->groupBy(['companies.id']); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/WithoutConfirmedPayments.php b/app/Classes/General/Eloquent/Filters/WithoutConfirmedPayments.php new file mode 100644 index 00000000..e17ed5da --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WithoutConfirmedPayments.php @@ -0,0 +1,24 @@ +whereDoesntHave('transactions', function($transaction){ + return $transaction->where('transactions.type', TransactionType::PAYMENT)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); + }); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index 528eb618..940d6565 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -79,7 +79,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $outstanding = $this->calculatesBookingOutstanding->execute($booking); - if($conversionObject->getAmount() > $outstanding) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); + if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject); diff --git a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php index c20450d8..5885d560 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/FetchBookingPaymentQuotationLogic.php @@ -68,7 +68,7 @@ class FetchBookingPaymentQuotationLogic extends AbstractControllerLogic $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $request->input('amount'))), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')]); $outstanding = $this->calculatesBookingOutstanding->execute($booking); - if($conversionObject->getAmount() > $outstanding) throw new MalformedRequestException('Your payment must not be greater than '.$booking->fixedCurrency->short_code.' '. number_format((float)$outstanding, 2, '.', ',')); + if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '.$booking->fixedCurrency->short_code.' '. number_format((float)$outstanding, 2, '.', ',')); return $this->response(['data' => $this->generatesBookingQuotation->execute( $this->fetchBookingQuotation->execute($booking->company, $conversionObject), diff --git a/app/Classes/Modules/Transactions/Services/CreatesWalletTransaction.php b/app/Classes/Modules/Transactions/Services/CreatesWalletTransaction.php index 7c3eacd6..6c5003c8 100644 --- a/app/Classes/Modules/Transactions/Services/CreatesWalletTransaction.php +++ b/app/Classes/Modules/Transactions/Services/CreatesWalletTransaction.php @@ -32,7 +32,6 @@ class CreatesWalletTransaction extends AbstractUpdateRelationshipRecord $model->service_charge = $object->getServiceCharge(); $model->expires_on = $object->getExpiresOn(); $model->status = $object->getStatus(); - $model->booking_id = 0; return $this->handler($wallet->transactions(), $model); diff --git a/app/Classes/ValueObjects/Constants/RoleTypes.php b/app/Classes/ValueObjects/Constants/RoleTypes.php index 3027bcb1..ec5fe961 100644 --- a/app/Classes/ValueObjects/Constants/RoleTypes.php +++ b/app/Classes/ValueObjects/Constants/RoleTypes.php @@ -16,4 +16,12 @@ final class RoleTypes public const USER = 3; + public const CURRENCY_SUPPLIER = 4; + + public const MONEY_MULE = 5; + + public const ORIGIN_ACCOUNT_ADMIN = 6; + + public const DESTINATION_ACCOUNT_ADMIN = 7; + } \ No newline at end of file diff --git a/app/Http/Controllers/Wallets/WithdrawWalletController.php b/app/Http/Controllers/Wallets/WithdrawWalletController.php index 0d125aa7..7c7b944a 100644 --- a/app/Http/Controllers/Wallets/WithdrawWalletController.php +++ b/app/Http/Controllers/Wallets/WithdrawWalletController.php @@ -2,14 +2,14 @@ namespace App\Http\Controllers\Wallets; -use App\Classes\Modules\Wallets\ControllersLogic\CreateWalletLogic; +use App\Classes\Modules\Wallets\ControllersLogic\WithdrawWalletLogic; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; class WithdrawWalletController { - public function withdraw(Request $request, CreateWalletLogic $logic): JsonResponse { + public function withdraw(Request $request, WithdrawWalletLogic $logic): JsonResponse { return $logic->execute($request); } } diff --git a/app/Http/Resources/CompanyResource.php b/app/Http/Resources/CompanyResource.php index 655cfc98..a35c7082 100644 --- a/app/Http/Resources/CompanyResource.php +++ b/app/Http/Resources/CompanyResource.php @@ -9,6 +9,7 @@ use App\Classes\ValueObjects\Constants\BusinessType; use App\Classes\ValueObjects\Constants\DocumentType; use App\Classes\ValueObjects\Constants\RoleTypes; use App\Classes\ValueObjects\Constants\SegmentConstants; +use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Currency; use App\Models\SegmentConstant; use Illuminate\Http\Resources\Json\JsonResource; @@ -24,6 +25,7 @@ class CompanyResource extends JsonResource */ public function toArray($request) { + $lastPayment = $this->transactions()->where('transactions.type', TransactionType::PAYMENT)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->orderBy('id', 'DESC')->first(); return [ 'id' => $this->id, 'name' => $this->name, @@ -33,9 +35,11 @@ class CompanyResource extends JsonResource 'status' => (int) $this->status, 'contact' => new ContactResource ($this->when($this->has('contacts'), $this->contacts->first())), 'address' => new AddressResource($this->when($this->has('addresses'), $this->addresses->where('billing', true)->first())), - 'employee' => new UserResource(Auth::user()->type === RoleTypes::USER ? $this->employees()->where('email', '=', Auth::user()->email)->first() : $this->employees()->where('users.status', '=', ApprovalStatus::APPROVED)->orderBy('id', 'DESC')->first()), + 'employee' => new UserResource(Auth::user()->type === RoleTypes::USER ? $this->employees()->where('email', '=', Auth::user()->email)->first() : $this->employees()->orderBy('id', 'DESC')->first()), 'identification' => new DocumentResource($this->documents->whereIn('document_type', DocumentType::IDENTIFICATION_DOCUMENTS)->first()), 'bookings' => $this->whenLoaded('bookings', $this->bookings()->orderBy('id', 'DESC')->get(), []), + 'total_payments' => (float) $this->transactions()->where('transactions.type', TransactionType::PAYMENT)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->sum('amount'), + 'last_payment' => $lastPayment ? $lastPayment->created_at->diffForHumans() : 'No Payments', 'personal_banks' => BankResource::collection($this->banks->where('type', BankAccountType::PERSONAL)), 'recipient_banks' => [ 'accounts' => BankResource::collection($this->banks->where('type', BankAccountType::EXTERNAL)), @@ -46,7 +50,8 @@ class CompanyResource extends JsonResource 'currencies' => $this->when($this->business_type === BusinessType::CURRENCY_VENDOR, function(){ $segment = SegmentConstant::where('reference', SegmentConstants::SUPPLIER_CURRENCIES)->where('detail->id', $this->id)->first(); return $segment ? CurrencyResource::collection(Currency::whereIn('id', $segment->detail->currencies)->get()) : []; - }) + }), + 'created_at' => $this->created_at->format('d-m-Y') ]; diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index fc0dff73..1793d191 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -21,6 +21,7 @@ class TransactionResource extends JsonResource 'booking' => new BookingResource($this->booking), 'type' => (int) $this->type, 'bill_no' => $this->bill_no, + 'recipient_bank_account' => new BankResource($this->when((int) $this->type === TransactionType::BILL,$this->booking->bank)), 'amount' => (double) $this->amount, 'original_amount' => (double) $this->original_amount, 'currency' => new CurrencyResource($this->currency), diff --git a/app/Models/Company.php b/app/Models/Company.php index eba6677f..ab94c46e 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -12,6 +12,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; +use Staudenmeir\EloquentHasManyDeep\HasManyDeep; +use Staudenmeir\EloquentHasManyDeep\HasRelationships; + /** * Class Company @@ -27,11 +30,12 @@ use Illuminate\Support\Collection; */ class Company extends AbstractModel implements Documentable { + use HasRelationships; use SoftDeletes; protected $table = 'companies'; - protected $dates = ['deleted_at']; + protected $dates = ['deleted_at', 'created_at']; /** @@ -90,6 +94,14 @@ class Company extends AbstractModel implements Documentable return $this->HasMany(Booking::class, 'company_id'); } + /** + * @return hasManyDeep + */ + public function transactions(): hasManyDeep + { + return $this->hasManyDeep(Transaction::class, [Booking::class], ['company_id', 'booking_id'], ['id', 'id']); + } + /** * @return MorphMany */ diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index cad64361..e10e1781 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -9,6 +9,8 @@ use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphTo; @@ -46,6 +48,14 @@ class Transaction extends AbstractModel implements Documentable return $this->BelongsTo(Wallet::class, 'owner_id', 'id'); } + /** + * @return BelongsTo + */ + public function recipientBankAccount(): BelongsTo + { + return $this->BelongsTo(Bank::class); + } + /** * @return MorphMany */ diff --git a/composer.json b/composer.json index 7715f0ca..3323db18 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "php": "^7.2.5", "ext-fileinfo": "*", "ext-json": "^1.6", + "ext-zip": "*", "barryvdh/laravel-dompdf": "^0.9.0", "carlos-meneses/laravel-mpdf": "^2.1", "doctrine/dbal": "2.*", @@ -24,6 +25,7 @@ "rinvex/countries": "^6.1", "spatie/laravel-activitylog": "^3.14", "spatie/laravel-permission": "^3.17", + "staudenmeir/eloquent-has-many-deep": "^1.7", "tymon/jwt-auth": "^1.0" }, "require-dev": { diff --git a/config/database.php b/config/database.php index b42d9b30..aeabec6f 100644 --- a/config/database.php +++ b/config/database.php @@ -56,7 +56,7 @@ return [ 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'prefix_indexes' => true, - 'strict' => true, + 'strict' => false, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), diff --git a/resources/assets/vue/components/banks/elements/BankInComponent.vue b/resources/assets/vue/components/banks/elements/BankInComponent.vue new file mode 100644 index 00000000..ff42850a --- /dev/null +++ b/resources/assets/vue/components/banks/elements/BankInComponent.vue @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/resources/assets/vue/components/bookings/elements/BookingComponent.vue b/resources/assets/vue/components/bookings/elements/BookingComponent.vue index 509e9840..99c3228c 100644 --- a/resources/assets/vue/components/bookings/elements/BookingComponent.vue +++ b/resources/assets/vue/components/bookings/elements/BookingComponent.vue @@ -40,13 +40,13 @@
Payable Amount
{{this.item.amount}} {{this.item.fixed_currency.short_code}}
-
+
status
{{item.status === 3 ? 'Complete' : 'In Progress'}}
-
+
Billing
Pending... diff --git a/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue index 5356070b..c403af9d 100644 --- a/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue +++ b/resources/assets/vue/components/bookings/elements/PaymentProofComponent.vue @@ -3,48 +3,45 @@
-
-
-
+
+
+
-
Reference
- -
- {{item.booking.marking}} + -
-
Date
-
- {{item.updated_at}}
+
-
-
Marking
-
- {{item.booking.company.reference}} -
-
-
-
Currency
-
- {{item.original_currency.short_code}} -
-
-
-
Amount
-
- {{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}} -
-
-
-
+
+
+
+
+
+
+ style=" fill:#000000;">
diff --git a/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue b/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue index 414f1a42..65b355f8 100644 --- a/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/BookingFormComponent.vue @@ -7,7 +7,7 @@
- +
@@ -21,7 +21,7 @@
- +
diff --git a/resources/assets/vue/components/bookings/forms/PaymentProofFormComponent.vue b/resources/assets/vue/components/bookings/forms/PaymentProofFormComponent.vue index 612b8735..0c85a3f7 100644 --- a/resources/assets/vue/components/bookings/forms/PaymentProofFormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/PaymentProofFormComponent.vue @@ -10,6 +10,7 @@
+
diff --git a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue index de55e62c..ba3a39b1 100644 --- a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue @@ -101,9 +101,9 @@
-
+
-