diff --git a/app/Factory/PaymentFactory.php b/app/Factory/PaymentFactory.php new file mode 100644 index 000000000000..e626f4aa68e2 --- /dev/null +++ b/app/Factory/PaymentFactory.php @@ -0,0 +1,35 @@ +company_id = $company_id; + $payment->user_id = $user_id; + $payment->client_id = 0; + $payment->client_contact_id = null; + $payment->invitation_id = null; + $payment->account_gateway_id = null; + $payment->payment_type_id = null; + $payment->is_deleted = false; + $payment->amount = 0; + $payment->payment_date = null; + $payment->transaction_reference = null; + $payment->payer_id = null; + $payment->invoice_id = 0; + + return $payment; + } +} + + + diff --git a/app/Filters/PaymentFilters.php b/app/Filters/PaymentFilters.php new file mode 100644 index 000000000000..90902314259c --- /dev/null +++ b/app/Filters/PaymentFilters.php @@ -0,0 +1,111 @@ +builder; + + return $this->builder->where(function ($query) use ($filter) { + $query->where('payments.custom_value1', 'like', '%'.$filter.'%') + ->orWhere('payments.custom_value2', 'like' , '%'.$filter.'%') + ->orWhere('payments.custom_value3', 'like' , '%'.$filter.'%') + ->orWhere('payments.custom_value4', 'like' , '%'.$filter.'%'); + }); + } + + /** + * Filters the list based on the status + * archived, active, deleted + * + * @param string filter + * @return Illuminate\Database\Query\Builder + */ + public function status(string $filter = '') : Builder + { + if(strlen($filter) == 0) + return $this->builder; + + $table = 'payments'; + $filters = explode(',', $filter); + + return $this->builder->where(function ($query) use ($filters, $table) { + $query->whereNull($table . '.id'); + + if (in_array(parent::STATUS_ACTIVE, $filters)) { + $query->orWhereNull($table . '.deleted_at'); + } + + if (in_array(parent::STATUS_ARCHIVED, $filters)) { + $query->orWhere(function ($query) use ($table) { + $query->whereNotNull($table . '.deleted_at'); + + if (! in_array($table, ['users'])) { + $query->where($table . '.is_deleted', '=', 0); + } + }); + } + + if (in_array(parent::STATUS_DELETED, $filters)) { + $query->orWhere($table . '.is_deleted', '=', 1); + } + }); + } + + /** + * Sorts the list based on $sort + * + * @param string sort formatted as column|asc + * @return Illuminate\Database\Query\Builder + */ + public function sort(string $sort) : Builder + { + $sort_col = explode("|", $sort); + return $this->builder->orderBy($sort_col[0], $sort_col[1]); + } + + /** + * Returns the base query + * + * @param int company_id + * @return Illuminate\Database\Query\Builder + * @deprecated + */ + public function baseQuery(int $company_id, User $user) : Builder + { + + } + + /** + * Filters the query by the users company ID + * + * @param $company_id The company Id + * @return Illuminate\Database\Query\Builder + */ + public function entityFilter() + { + + return $this->builder->whereCompanyId(auth()->user()->company()->id); + + } + +} \ No newline at end of file diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 4097d1e3e3d1..fd16e04cefe7 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; +use App\Filters\PaymentFilters; use App\Http\Requests\Payment\ActionPaymentRequest; use App\Http\Requests\Payment\CreatePaymentRequest; use App\Http\Requests\Payment\DestroyPaymentRequest; @@ -13,6 +14,7 @@ use App\Http\Requests\Payment\UpdatePaymentRequest; use App\Jobs\Entity\ActionEntity; use App\Models\Payment; use App\Repositories\BaseRepository; +use App\Transformers\PaymentTransformer; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; @@ -28,14 +30,13 @@ class PaymentController extends BaseController protected $entity_type = Payment::class; - protected $entity_transformer = Paymentransformer::class; + protected $entity_transformer = PaymentTransformer::class; /** * @var PaymentRepository */ protected $payment_repo; - protected $base_repo; /** * PaymentController constructor. @@ -61,7 +62,7 @@ class PaymentController extends BaseController public function index(PaymentFilters $filters) { - $payments = Invoice::filter($filters); + $payments = Payment::filter($filters); return $this->listResponse($payments); diff --git a/app/Http/Requests/Payment/ActionPaymentRequest.php b/app/Http/Requests/Payment/ActionPaymentRequest.php new file mode 100644 index 000000000000..d60bb3babdd4 --- /dev/null +++ b/app/Http/Requests/Payment/ActionPaymentRequest.php @@ -0,0 +1,21 @@ +user()->can('edit', $this->payment); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/Payment/CreatePaymentRequest.php b/app/Http/Requests/Payment/CreatePaymentRequest.php new file mode 100644 index 000000000000..0c3c30b74749 --- /dev/null +++ b/app/Http/Requests/Payment/CreatePaymentRequest.php @@ -0,0 +1,21 @@ +user()->can('create', Payment::class); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/Payment/DestroyPaymentRequest.php b/app/Http/Requests/Payment/DestroyPaymentRequest.php new file mode 100644 index 000000000000..34604ce5ea64 --- /dev/null +++ b/app/Http/Requests/Payment/DestroyPaymentRequest.php @@ -0,0 +1,21 @@ +user()->can('edit', $this->payment); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/Payment/EditPaymentRequest.php b/app/Http/Requests/Payment/EditPaymentRequest.php new file mode 100644 index 000000000000..a336386d81ab --- /dev/null +++ b/app/Http/Requests/Payment/EditPaymentRequest.php @@ -0,0 +1,40 @@ +user()->can('edit', $this->payment); + } + + public function rules() + { + $rules = []; + + return $rules; + } + + + public function sanitize() + { + $input = $this->all(); + + //$input['id'] = $this->encodePrimaryKey($input['id']); + + //$this->replace($input); + + return $this->all(); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/Payment/ShowPaymentRequest.php b/app/Http/Requests/Payment/ShowPaymentRequest.php new file mode 100644 index 000000000000..b9a5b998b8b2 --- /dev/null +++ b/app/Http/Requests/Payment/ShowPaymentRequest.php @@ -0,0 +1,21 @@ +user()->can('view', $this->payment); + } + +} \ No newline at end of file diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php new file mode 100644 index 000000000000..2a1336aad191 --- /dev/null +++ b/app/Http/Requests/Payment/StorePaymentRequest.php @@ -0,0 +1,41 @@ +user()->can('create', Payment::class); + } + + public function rules() + { + return [ + 'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx', + ]; + } + + + public function sanitize() + { + //do post processing of Payment request here, ie. Payment_items + } + + public function messages() + { + + } + + +} + diff --git a/app/Http/Requests/Payment/UpdatePaymentRequest.php b/app/Http/Requests/Payment/UpdatePaymentRequest.php new file mode 100644 index 000000000000..96c5fe59712c --- /dev/null +++ b/app/Http/Requests/Payment/UpdatePaymentRequest.php @@ -0,0 +1,31 @@ +user()->can('edit', $this->payment); + + } + + + public function rules() + { + return [ + 'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx', + ]; + } + +} \ No newline at end of file diff --git a/app/Models/Payment.php b/app/Models/Payment.php index dab643fe3357..b32b69f967f0 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -2,27 +2,32 @@ namespace App\Models; +use App\Models\Filterable; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\Model; class Payment extends BaseModel { use MakesHash; + use Filterable; protected $guarded = [ 'id', ]; - protected $appends = ['payment_id']; - - public function getRouteKeyName() + public function client() { - return 'payment_id'; + return $this->belongsTo(Client::class); } - public function getPaymentIdAttribute() + public function company() { - return $this->encodePrimaryKey($this->id); + return $this->belongsTo(Company::class); + } + + public function user() + { + return $this->belongsTo(User::class); } public function documents() diff --git a/app/Policies/PaymentPolicy.php b/app/Policies/PaymentPolicy.php new file mode 100644 index 000000000000..986a60bac533 --- /dev/null +++ b/app/Policies/PaymentPolicy.php @@ -0,0 +1,25 @@ +isAdmin() || $user->hasPermission('create_payment'); + } + +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 91065a771b59..82ca77936732 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -4,12 +4,14 @@ namespace App\Providers; use App\Models\Client; use App\Models\Invoice; +use App\Models\Payment; use App\Models\Product; use App\Models\Quote; use App\Models\RecurringInvoice; use App\Models\User; use App\Policies\ClientPolicy; use App\Policies\InvoicePolicy; +use App\Policies\PaymentPolicy; use App\Policies\ProductPolicy; use App\Policies\QuotePolicy; use App\Policies\RecurringInvoicePolicy; @@ -28,6 +30,7 @@ class AuthServiceProvider extends ServiceProvider Client::class => ClientPolicy::class, Product::class => ProductPolicy::class, Invoice::class => InvoicePolicy::class, + Payment::class => PaymentPolicy::class, RecurringInvoice::class => RecurringInvoicePolicy::class, Quote::class => QuotePolicy::class, User::class => UserPolicy::class, diff --git a/database/factories/PaymentFactory.php b/database/factories/PaymentFactory.php new file mode 100644 index 000000000000..919a6473a10d --- /dev/null +++ b/database/factories/PaymentFactory.php @@ -0,0 +1,17 @@ +define(App\Models\Payment::class, function (Faker $faker) { + return [ + 'id_deleted' => false, + 'amount' => $faker->numberBetween(1,10), + 'payment_date' => $faker->date(), + 'transaction_reference' => $faker->text(10), + 'invoice_id' => $faker->numberBetween(1,10) + ]; +}); + + \ No newline at end of file diff --git a/tests/Feature/PaymentTest.php b/tests/Feature/PaymentTest.php new file mode 100644 index 000000000000..3ce8ac6e4d88 --- /dev/null +++ b/tests/Feature/PaymentTest.php @@ -0,0 +1,202 @@ +faker = \Faker\Factory::create(); + + Model::reguard(); + + + } + + public function testPaymentList() + { + $data = [ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->unique()->safeEmail, + 'password' => 'ALongAndBrilliantPassword123', + '_token' => csrf_token(), + 'privacy_policy' => 1, + 'terms_of_service' => 1 + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + ])->post('/api/v1/signup', $data); + + $acc = $response->json(); + + $account = Account::find($this->decodePrimaryKey($acc['data']['id'])); + + $company_token = $account->default_company->tokens()->first(); + $token = $company_token->token; + $company = $company_token->company; + + $user = $company_token->user; + + $this->assertNotNull($company_token); + $this->assertNotNull($token); + $this->assertNotNull($user); + $this->assertNotNull($company); + $this->assertNotNull($user->tokens->first()->company); + + factory(\App\Models\Client::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id])->each(function ($c) use ($user, $company){ + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id, + 'is_primary' => 1 + ]); + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id + ]); + + }); + $client = Client::all()->first(); + + factory(\App\Models\Payment::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]); + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->get('/api/v1/payments'); + + $response->assertStatus(200); + + } + + public function testPaymentRESTEndPoints() + { + $data = [ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'email' => $this->faker->unique()->safeEmail, + 'password' => 'ALongAndBrilliantPassword123', + '_token' => csrf_token(), + 'privacy_policy' => 1, + 'terms_of_service' => 1 + ]; + + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + ])->post('/api/v1/signup', $data); + + $acc = $response->json(); + + $account = Account::find($this->decodePrimaryKey($acc['data']['id'])); + + $company_token = $account->default_company->tokens()->first(); + $token = $company_token->token; + $company = $company_token->company; + + $user = $company_token->user; + + $this->assertNotNull($company_token); + $this->assertNotNull($token); + $this->assertNotNull($user); + $this->assertNotNull($company); + $this->assertNotNull($user->tokens->first()->company); + + factory(\App\Models\Client::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id])->each(function ($c) use ($user, $company){ + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id, + 'is_primary' => 1 + ]); + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $user->id, + 'client_id' => $c->id, + 'company_id' => $company->id + ]); + + }); + $client = Client::all()->first(); + + factory(\App\Models\Payment::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]); + + $Payment = Payment::where('user_id',$user->id)->first(); + $Payment->settings = $client->getMergedSettings(); + $Payment->save(); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->get('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id)); + + $response->assertStatus(200); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->get('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id).'/edit'); + + $response->assertStatus(200); + + $Payment_update = [ + 'amount' => 10 + ]; + + $this->assertNotNull($Payment); + $this->assertNotNull($Payment->settings); + + $this->assertTrue(property_exists($Payment->settings, 'custom_taxes1')); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->put('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id), $Payment_update) + ->assertStatus(200); + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $token, + ])->delete('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id)); + + $response->assertStatus(200); + + } + +}