mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Activity API fixes + Payments API (#3076)
* Fixes for Store Payment Validation * Tests for Payments * Use custom validator to ensure payments are made ONLY to payable invoices * Working on custom payment validators * Update Client balance * fixes for client balance * Fixes for activity API
This commit is contained in:
parent
81c481c071
commit
fe5a97e174
@ -29,9 +29,38 @@ class ActivityController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* @OA\Get(
|
||||||
|
* path="/api/v1/actvities",
|
||||||
|
* operationId="getActivities",
|
||||||
|
* tags={"actvities"},
|
||||||
|
* summary="Gets a list of actvities",
|
||||||
|
* description="Lists all activities",
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||||
|
* @OA\Parameter(ref="#/components/parameters/index"),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=200,
|
||||||
|
* description="A list of actvities",
|
||||||
|
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||||
|
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Activity"),
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response=422,
|
||||||
|
* description="Validation error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||||
|
|
||||||
|
* ),
|
||||||
|
* @OA\Response(
|
||||||
|
* response="default",
|
||||||
|
* description="Unexpected Error",
|
||||||
|
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
@ -185,6 +185,40 @@ class PaymentController extends BaseController
|
|||||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||||
|
* @OA\RequestBody(
|
||||||
|
* description="The payment request",
|
||||||
|
* required=true,
|
||||||
|
* @OA\MediaType(
|
||||||
|
* mediaType="application/json",
|
||||||
|
* @OA\Schema(
|
||||||
|
* type="object",
|
||||||
|
* @OA\Property(
|
||||||
|
* property="amount",
|
||||||
|
* description="The payment amount",
|
||||||
|
* type="number",
|
||||||
|
* format="float",
|
||||||
|
* ),
|
||||||
|
* @OA\Property(
|
||||||
|
* property="payment_date",
|
||||||
|
* example="2019/12/1",
|
||||||
|
* description="The payment date",
|
||||||
|
* type="string",
|
||||||
|
* ),
|
||||||
|
* @OA\Property(
|
||||||
|
* property="transation_reference",
|
||||||
|
* example="sdfasdfs98776d6kbkfd",
|
||||||
|
* description="The transaction reference for the payment",
|
||||||
|
* type="string",
|
||||||
|
* ),
|
||||||
|
* @OA\Property(
|
||||||
|
* property="invoices",
|
||||||
|
* example="j7s76d,s8765afk,D8fj3Sfdj",
|
||||||
|
* description="A comma separated list of invoice hashed ids that this payment relates to",
|
||||||
|
* type="string",
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
* ),
|
||||||
* @OA\Response(
|
* @OA\Response(
|
||||||
* response=200,
|
* response=200,
|
||||||
* description="Returns the saved Payment object",
|
* description="Returns the saved Payment object",
|
||||||
@ -449,6 +483,7 @@ class PaymentController extends BaseController
|
|||||||
public function destroy(DestroyPaymentRequest $request, Payment $payment)
|
public function destroy(DestroyPaymentRequest $request, Payment $payment)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
$payment->is_deleted = true;
|
||||||
$payment->delete();
|
$payment->delete();
|
||||||
|
|
||||||
return response()->json([], 200);
|
return response()->json([], 200);
|
||||||
|
@ -12,12 +12,14 @@
|
|||||||
namespace App\Http\Requests\Payment;
|
namespace App\Http\Requests\Payment;
|
||||||
|
|
||||||
use App\Http\Requests\Request;
|
use App\Http\Requests\Request;
|
||||||
|
use App\Http\ValidationRules\ValidPayableInvoicesRule;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
class StorePaymentRequest extends Request
|
class StorePaymentRequest extends Request
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user is authorized to make this request.
|
* Determine if the user is authorized to make this request.
|
||||||
*
|
*
|
||||||
@ -26,41 +28,47 @@ class StorePaymentRequest extends Request
|
|||||||
|
|
||||||
public function authorize() : bool
|
public function authorize() : bool
|
||||||
{
|
{
|
||||||
|
|
||||||
return auth()->user()->can('create', Payment::class);
|
return auth()->user()->can('create', Payment::class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
$this->sanitize();
|
$this->sanitize();
|
||||||
|
|
||||||
return [
|
$rules = [
|
||||||
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
'amount' => 'numeric|required',
|
||||||
'client_id' => 'integer|nullable',
|
|
||||||
'payment_type_id' => 'integer|nullable',
|
|
||||||
'amount' => 'numeric',
|
|
||||||
'payment_date' => 'required',
|
'payment_date' => 'required',
|
||||||
|
'client_id' => 'required',
|
||||||
|
'invoices' => 'required',
|
||||||
|
'invoices' => new ValidPayableInvoicesRule(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function sanitize()
|
public function sanitize()
|
||||||
{
|
{
|
||||||
|
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
if(isset($input['invoices']))
|
if(isset($input['client_id']))
|
||||||
$input['invoices'] = $this->transformKeys(array_column($input['invoices']), 'id');
|
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||||
|
|
||||||
$this->replace($input);
|
if(isset($input['invoices']))
|
||||||
|
$input['invoices'] = $this->transformKeys(explode(",", $input['invoices']));
|
||||||
|
|
||||||
|
if(is_array($input['invoices']) === false)
|
||||||
|
$input['invoices'] = null;
|
||||||
|
|
||||||
|
|
||||||
|
$this->replace($input);
|
||||||
|
|
||||||
return $this->all();
|
return $this->all();
|
||||||
}
|
|
||||||
|
|
||||||
public function messages()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
56
app/Http/ValidationRules/ValidPayableInvoicesRule.php
Normal file
56
app/Http/ValidationRules/ValidPayableInvoicesRule.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\ValidationRules;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ValidPayableInvoicesRule
|
||||||
|
* @package App\Http\ValidationRules
|
||||||
|
*/
|
||||||
|
class ValidPayableInvoicesRule implements Rule
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $attribute
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function passes($attribute, $value)
|
||||||
|
{
|
||||||
|
|
||||||
|
$invoices = Invoice::whereIn('id', $this->transformKeys(explode(",",$value)))->get();
|
||||||
|
|
||||||
|
if(!$invoices || $invoices->count() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach ($invoices as $invoice) {
|
||||||
|
|
||||||
|
if(! $invoice->isPayable())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function message()
|
||||||
|
{
|
||||||
|
return "One or more of these invoices have been paid";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
app/Jobs/Client/UpdateClientBalance.php
Normal file
51
app/Jobs/Client/UpdateClientBalance.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Client;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\Client;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
|
||||||
|
class UpdateClientBalance
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
|
||||||
|
protected $amount;
|
||||||
|
|
||||||
|
protected $client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function __construct(Client $client, $amount)
|
||||||
|
{
|
||||||
|
$this->amount = $amount;
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->client->balance += $this->amount;
|
||||||
|
$this->client->paid_to_date += $this->amount;
|
||||||
|
$this->client->save();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace App\Jobs\Invoice;
|
namespace App\Jobs\Invoice;
|
||||||
|
|
||||||
|
use App\Jobs\Client\UpdateClientBalance;
|
||||||
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
|
use App\Jobs\Company\UpdateCompanyLedgerWithInvoice;
|
||||||
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
||||||
use App\Jobs\Util\SystemLogger;
|
use App\Jobs\Util\SystemLogger;
|
||||||
@ -51,7 +52,7 @@ class UpdateInvoicePayment implements ShouldQueue
|
|||||||
|
|
||||||
$invoices_total = $invoices->sum('balance');
|
$invoices_total = $invoices->sum('balance');
|
||||||
|
|
||||||
/* Simplest scenario*/
|
/* Simplest scenario - All invoices are paid in full*/
|
||||||
if(strval($invoices_total) === strval($this->payment->amount))
|
if(strval($invoices_total) === strval($this->payment->amount))
|
||||||
{
|
{
|
||||||
$invoices->each(function ($invoice){
|
$invoices->each(function ($invoice){
|
||||||
@ -59,15 +60,18 @@ class UpdateInvoicePayment implements ShouldQueue
|
|||||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($invoice->balance*-1));
|
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($invoice->balance*-1));
|
||||||
$invoice->clearPartial();
|
$invoice->clearPartial();
|
||||||
$invoice->updateBalance($invoice->balance*-1);
|
$invoice->updateBalance($invoice->balance*-1);
|
||||||
|
|
||||||
|
UpdateClientBalance::dispatchNow($this->payment->client, $invoice->balance*-1);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/*Combination of partials and full invoices are being paid*/
|
||||||
else {
|
else {
|
||||||
|
|
||||||
$total = 0;
|
$total = 0;
|
||||||
|
|
||||||
|
/* Calculate the grand total of the invoices*/
|
||||||
foreach($invoices as $invoice)
|
foreach($invoices as $invoice)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -78,7 +82,7 @@ class UpdateInvoicePayment implements ShouldQueue
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* test if there is a batch of partial invoices that have been paid */
|
/*Test if there is a batch of partial invoices that have been paid */
|
||||||
if($this->payment->amount == $total)
|
if($this->payment->amount == $total)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -91,6 +95,8 @@ class UpdateInvoicePayment implements ShouldQueue
|
|||||||
$invoice->clearPartial();
|
$invoice->clearPartial();
|
||||||
$invoice->setDueDate();
|
$invoice->setDueDate();
|
||||||
$invoice->setStatus(Invoice::STATUS_PARTIAL);
|
$invoice->setStatus(Invoice::STATUS_PARTIAL);
|
||||||
|
|
||||||
|
UpdateClientBalance::dispatchNow($this->payment->client, $invoice->partial*-1);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -98,6 +104,9 @@ class UpdateInvoicePayment implements ShouldQueue
|
|||||||
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($invoice->balance*-1));
|
UpdateCompanyLedgerWithPayment::dispatchNow($this->payment, ($invoice->balance*-1));
|
||||||
$invoice->clearPartial();
|
$invoice->clearPartial();
|
||||||
$invoice->updateBalance($invoice->balance*-1);
|
$invoice->updateBalance($invoice->balance*-1);
|
||||||
|
|
||||||
|
UpdateClientBalance::dispatchNow($this->payment->client, $invoice->balance*-1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -120,18 +120,18 @@ class Activity extends StaticModel
|
|||||||
return $this->belongsTo(Payment::class)->withTrashed();
|
return $this->belongsTo(Payment::class)->withTrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function task()
|
// public function task()
|
||||||
{
|
// {
|
||||||
return $this->belongsTo(Task::class)->withTrashed();
|
// return $this->belongsTo(Task::class)->withTrashed();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public function expense()
|
// public function expense()
|
||||||
{
|
// {
|
||||||
return $this->belongsTo(Expense::class)->withTrashed();
|
// return $this->belongsTo(Expense::class)->withTrashed();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public function company()
|
public function company()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Company::class)->withTrashed();
|
return $this->belongsTo(Company::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class GatewayType extends StaticModel
|
|||||||
const CREDIT_CARD = 1;
|
const CREDIT_CARD = 1;
|
||||||
const BANK_TRANSFER = 2;
|
const BANK_TRANSFER = 2;
|
||||||
const PAYPAL = 3;
|
const PAYPAL = 3;
|
||||||
const BITCOIN = 4;
|
const CRYPTO = 4;
|
||||||
const DWOLLA = 5;
|
const DWOLLA = 5;
|
||||||
const CUSTOM1 = 6;
|
const CUSTOM1 = 6;
|
||||||
const ALIPAY = 7;
|
const ALIPAY = 7;
|
||||||
|
@ -18,13 +18,15 @@ use App\Utils\Number;
|
|||||||
use App\Utils\Traits\MakesDates;
|
use App\Utils\Traits\MakesDates;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class Payment extends BaseModel
|
class Payment extends BaseModel
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
use Filterable;
|
use Filterable;
|
||||||
use MakesDates;
|
use MakesDates;
|
||||||
|
use SoftDeletes;
|
||||||
|
|
||||||
const STATUS_PENDING = 1;
|
const STATUS_PENDING = 1;
|
||||||
const STATUS_VOIDED = 2;
|
const STATUS_VOIDED = 2;
|
||||||
const STATUS_FAILED = 3;
|
const STATUS_FAILED = 3;
|
||||||
@ -147,6 +149,7 @@ class Payment extends BaseModel
|
|||||||
public function resolveRouteBinding($value)
|
public function resolveRouteBinding($value)
|
||||||
{
|
{
|
||||||
return $this
|
return $this
|
||||||
|
->withTrashed()
|
||||||
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
|
|
||||||
namespace App\Repositories;
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Events\Payment\PaymentWasCreated;
|
||||||
|
use App\Jobs\Company\UpdateCompanyLedgerWithPayment;
|
||||||
|
use App\Jobs\Invoice\UpdateInvoicePayment;
|
||||||
|
use App\Models\Invoice;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@ -20,7 +24,6 @@ use Illuminate\Http\Request;
|
|||||||
class PaymentRepository extends BaseRepository
|
class PaymentRepository extends BaseRepository
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public function getClassName()
|
public function getClassName()
|
||||||
{
|
{
|
||||||
return Payment::class;
|
return Payment::class;
|
||||||
@ -30,20 +33,24 @@ class PaymentRepository extends BaseRepository
|
|||||||
{
|
{
|
||||||
|
|
||||||
$payment->fill($request->input());
|
$payment->fill($request->input());
|
||||||
|
|
||||||
|
$payment->save();
|
||||||
|
|
||||||
if($request->input('invoices'))
|
if($request->input('invoices'))
|
||||||
{
|
{
|
||||||
|
|
||||||
$invoices = Invoice::whereIn('id', $request->input('invoices'))->get();
|
$invoices = Invoice::whereIn('id', $request->input('invoices'))->get();
|
||||||
|
|
||||||
|
$payment->invoices()->saveMany($invoices);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//parse invoices[] and attach to paymentables
|
event(new PaymentWasCreated($payment));
|
||||||
//parse invoices[] and apply payments and subfunctions
|
|
||||||
|
UpdateInvoicePayment::dispatchNow($payment);
|
||||||
$payment->save();
|
|
||||||
|
|
||||||
return $payment;
|
return $payment;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -27,17 +27,17 @@ class ActivityTransformer extends EntityTransformer
|
|||||||
return [
|
return [
|
||||||
'id' => (string) $this->encodePrimaryKey($activity->id),
|
'id' => (string) $this->encodePrimaryKey($activity->id),
|
||||||
'activity_type_id' => (string) $activity->activity_type_id,
|
'activity_type_id' => (string) $activity->activity_type_id,
|
||||||
'client_id' => $activity->client ? (string) $activity->client->id : '',
|
'client_id' => $activity->client ? (string) $this->encodePrimaryKey($activity->client->id) : '',
|
||||||
'company_id' => $activity->company ? (string) $activity->company->id : '',
|
'company_id' => $activity->company ? (string) $this->encodePrimaryKey($activity->company->id) : '',
|
||||||
'user_id' => (string) $activity->user_id,
|
'user_id' => (string) $activity->user_id,
|
||||||
'invoice_id' => $activity->invoice ? (string) $activity->invoice->id : '',
|
'invoice_id' => $activity->invoice ? (string) $this->encodePrimaryKey($activity->invoice->id) : '',
|
||||||
'payment_id' => $activity->payment ? (string) $activity->payment->id : '',
|
'payment_id' => $activity->payment ? (string) $this->encodePrimaryKey($activity->payment->id) : '',
|
||||||
'credit_id' => $activity->credit ? (string) $activity->credit->id : '',
|
'credit_id' => $activity->credit ? (string) $this->encodePrimaryKey($activity->credit->id) : '',
|
||||||
'updated_at' => $activity->updated_at,
|
'updated_at' => $activity->updated_at,
|
||||||
'expense_id' => $activity->expense_id ? (string) $activity->expense->id : '',
|
'expense_id' => $activity->expense_id ? (string) $this->encodePrimaryKey($activity->expense->id) : '',
|
||||||
'is_system' => (bool) $activity->is_system,
|
'is_system' => (bool) $activity->is_system,
|
||||||
'contact_id' => $activity->contact_id ? (string) $activity->contact->id : '',
|
'contact_id' => $activity->contact_id ? (string) $this->encodePrimaryKey($activity->contact->id) : '',
|
||||||
'task_id' => $activity->task_id ? (string) $activity->task->id : '',
|
'task_id' => $activity->task_id ? (string) $this->encodePrimaryKey($activity->task->id) : '',
|
||||||
'notes' => $activity->notes ? (string) $activity->notes : '',
|
'notes' => $activity->notes ? (string) $activity->notes : '',
|
||||||
'ip' => (string) $activity->ip,
|
'ip' => (string) $activity->ip,
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class PaymentTransformer extends EntityTransformer
|
|||||||
|
|
||||||
protected $availableIncludes = [
|
protected $availableIncludes = [
|
||||||
'client',
|
'client',
|
||||||
'invoice',
|
'invoices',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct($serializer = null)
|
public function __construct($serializer = null)
|
||||||
@ -37,11 +37,11 @@ class PaymentTransformer extends EntityTransformer
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeInvoice(Payment $payment)
|
public function includeInvoices(Payment $payment)
|
||||||
{
|
{
|
||||||
$transformer = new InvoiceTransformer($this->serializer);
|
$transformer = new InvoiceTransformer($this->serializer);
|
||||||
|
|
||||||
return $this->includeItem($payment->invoice, $transformer, Invoice::class);
|
return $this->includeCollection($payment->invoices, $transformer, Invoice::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function includeClient(Payment $payment)
|
public function includeClient(Payment $payment)
|
||||||
@ -65,6 +65,7 @@ class PaymentTransformer extends EntityTransformer
|
|||||||
'is_deleted' => (bool) $payment->is_deleted,
|
'is_deleted' => (bool) $payment->is_deleted,
|
||||||
'payment_type_id' => (string) $payment->payment_type_id ?: '',
|
'payment_type_id' => (string) $payment->payment_type_id ?: '',
|
||||||
'invitation_id' => (string) $payment->invitation_id ?: '',
|
'invitation_id' => (string) $payment->invitation_id ?: '',
|
||||||
|
'client_id' => (string) $this->encodePrimaryKey($payment->client_id),
|
||||||
/*
|
/*
|
||||||
'private_notes' => $payment->private_notes ?: '',
|
'private_notes' => $payment->private_notes ?: '',
|
||||||
'exchange_rate' => (float) $payment->exchange_rate,
|
'exchange_rate' => (float) $payment->exchange_rate,
|
||||||
|
@ -6,6 +6,7 @@ use App\Models\Payment;
|
|||||||
use Faker\Generator as Faker;
|
use Faker\Generator as Faker;
|
||||||
|
|
||||||
$factory->define(App\Models\Payment::class, function (Faker $faker) {
|
$factory->define(App\Models\Payment::class, function (Faker $faker) {
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'is_deleted' => false,
|
'is_deleted' => false,
|
||||||
'amount' => $faker->numberBetween(1,10),
|
'amount' => $faker->numberBetween(1,10),
|
||||||
@ -14,6 +15,7 @@ $factory->define(App\Models\Payment::class, function (Faker $faker) {
|
|||||||
'payment_type_id' => Payment::TYPE_CREDIT_CARD,
|
'payment_type_id' => Payment::TYPE_CREDIT_CARD,
|
||||||
'status_id' => Payment::STATUS_COMPLETED
|
'status_id' => Payment::STATUS_COMPLETED
|
||||||
];
|
];
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -4,8 +4,13 @@ namespace Tests\Feature;
|
|||||||
|
|
||||||
use App\DataMapper\ClientSettings;
|
use App\DataMapper\ClientSettings;
|
||||||
use App\DataMapper\CompanySettings;
|
use App\DataMapper\CompanySettings;
|
||||||
|
use App\Factory\ClientFactory;
|
||||||
|
use App\Factory\InvoiceFactory;
|
||||||
|
use App\Factory\PaymentFactory;
|
||||||
|
use App\Helpers\Invoice\InvoiceSum;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
|
use App\Models\Invoice;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -15,6 +20,8 @@ use Illuminate\Foundation\Testing\WithFaker;
|
|||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Tests\MockAccountData;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +34,7 @@ class PaymentTest extends TestCase
|
|||||||
|
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
use DatabaseTransactions;
|
use DatabaseTransactions;
|
||||||
|
use MockAccountData;
|
||||||
public function setUp() :void
|
public function setUp() :void
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -39,67 +46,40 @@ class PaymentTest extends TestCase
|
|||||||
|
|
||||||
Model::reguard();
|
Model::reguard();
|
||||||
|
|
||||||
|
$this->makeTestData();
|
||||||
|
$this->withoutExceptionHandling();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPaymentList()
|
public function testPaymentList()
|
||||||
{
|
{
|
||||||
$data = [
|
|
||||||
'first_name' => $this->faker->firstName,
|
|
||||||
'last_name' => $this->faker->lastName,
|
|
||||||
'name' => $this->faker->company,
|
|
||||||
'email' => $this->faker->unique()->safeEmail,
|
|
||||||
'password' => 'ALongAndBrilliantPassword123',
|
|
||||||
'_token' => csrf_token(),
|
|
||||||
'privacy_policy' => 1,
|
|
||||||
'terms_of_service' => 1
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
factory(\App\Models\Client::class, 1)->create(['user_id' => $this->user->id, 'company_id' => $this->company->id])->each(function ($c) {
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
])->post('/api/v1/signup?include=account', $data);
|
|
||||||
|
|
||||||
$acc = $response->json();
|
|
||||||
|
|
||||||
$account = Account::find($this->decodePrimaryKey($acc['data'][0]['account']['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->token->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([
|
factory(\App\Models\ClientContact::class,1)->create([
|
||||||
'user_id' => $user->id,
|
'user_id' => $this->user->id,
|
||||||
'client_id' => $c->id,
|
'client_id' => $c->id,
|
||||||
'company_id' => $company->id,
|
'company_id' => $this->company->id,
|
||||||
'is_primary' => 1
|
'is_primary' => 1
|
||||||
]);
|
]);
|
||||||
|
|
||||||
factory(\App\Models\ClientContact::class,1)->create([
|
factory(\App\Models\ClientContact::class,1)->create([
|
||||||
'user_id' => $user->id,
|
'user_id' => $this->user->id,
|
||||||
'client_id' => $c->id,
|
'client_id' => $c->id,
|
||||||
'company_id' => $company->id
|
'company_id' => $this->company->id
|
||||||
]);
|
]);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$client = Client::all()->first();
|
$client = Client::all()->first();
|
||||||
|
|
||||||
factory(\App\Models\Payment::class, 1)->create(['user_id' => $user->id, 'company_id' => $company->id, 'client_id' => $client->id]);
|
factory(\App\Models\Payment::class, 1)->create(['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $client->id]);
|
||||||
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->get('/api/v1/payments');
|
])->get('/api/v1/payments');
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
@ -108,95 +88,164 @@ class PaymentTest extends TestCase
|
|||||||
|
|
||||||
public function testPaymentRESTEndPoints()
|
public function testPaymentRESTEndPoints()
|
||||||
{
|
{
|
||||||
$data = [
|
|
||||||
'first_name' => $this->faker->firstName,
|
|
||||||
'last_name' => $this->faker->lastName,
|
|
||||||
'name' => $this->faker->company,
|
|
||||||
'email' => $this->faker->unique()->safeEmail,
|
|
||||||
'password' => 'ALongAndBrilliantPassword123',
|
|
||||||
'_token' => csrf_token(),
|
|
||||||
'privacy_policy' => 1,
|
|
||||||
'terms_of_service' => 1
|
|
||||||
];
|
|
||||||
|
|
||||||
|
factory(\App\Models\Payment::class, 1)->create(['user_id' => $this->user->id, 'company_id' => $this->company->id, 'client_id' => $this->client->id]);
|
||||||
|
|
||||||
|
$Payment = Payment::all()->last();
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
])->post('/api/v1/signup?include=account', $data);
|
'X-API-TOKEN' => $this->token,
|
||||||
|
|
||||||
$acc = $response->json();
|
|
||||||
|
|
||||||
$account = Account::find($this->decodePrimaryKey($acc['data'][0]['account']['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->token->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::all()->first();
|
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $token,
|
|
||||||
])->get('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id));
|
])->get('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id));
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->get('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id).'/edit');
|
])->get('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id).'/edit');
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$Payment_update = [
|
}
|
||||||
'amount' => 10,
|
|
||||||
'payment_date' => Carbon::now()
|
public function testStorePaymentWithoutClientId()
|
||||||
];
|
{
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
$this->assertNotNull($Payment);
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
$response = $this->withHeaders([
|
$this->invoice->client_id = $client->id;
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
|
||||||
'X-API-TOKEN' => $token,
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
])->put('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id), $Payment_update)
|
$this->invoice->uses_inclusive_Taxes = false;
|
||||||
->assertStatus(200);
|
|
||||||
|
$this->invoice->save();
|
||||||
$response = $this->withHeaders([
|
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
'X-API-TOKEN' => $token,
|
$this->invoice_calc->build();
|
||||||
])->delete('/api/v1/payments/'.$this->encodePrimaryKey($Payment->id));
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
$response->assertStatus(200);
|
|
||||||
|
$data = [
|
||||||
|
'amount' => $this->invoice->amount,
|
||||||
|
'invoices' => $this->invoice->hashed_id,
|
||||||
|
'payment_date' => '2020/12/11',
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments/', $data);
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
|
||||||
|
$this->assertTrue(array_key_exists('client_id', $message));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStorePaymentWithClientId()
|
||||||
|
{
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
$this->invoice->status_id = Invoice::STATUS_SENT;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_Taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => $this->invoice->amount,
|
||||||
|
'client_id' => $client->hashed_id,
|
||||||
|
'invoices' => $this->invoice->hashed_id,
|
||||||
|
'payment_date' => '2020/12/12',
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments?include=invoices', $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
\Log::error('in the validator');
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
\Log::error($message);
|
||||||
|
$this->assertNotNull($message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$arr = $response->json();
|
||||||
|
\Log::error($arr);
|
||||||
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStorePaymentWithNoInvoiecs()
|
||||||
|
{
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->user->id);
|
||||||
|
$client->save();
|
||||||
|
|
||||||
|
$this->invoice = InvoiceFactory::create($this->company->id,$this->user->id);//stub the company and user_id
|
||||||
|
$this->invoice->client_id = $client->id;
|
||||||
|
$this->invoice->status_id = Invoice::STATUS_SENT;
|
||||||
|
|
||||||
|
$this->invoice->line_items = $this->buildLineItems();
|
||||||
|
$this->invoice->uses_inclusive_Taxes = false;
|
||||||
|
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$this->invoice_calc = new InvoiceSum($this->invoice);
|
||||||
|
$this->invoice_calc->build();
|
||||||
|
|
||||||
|
$this->invoice = $this->invoice_calc->getInvoice();
|
||||||
|
$this->invoice->save();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'amount' => $this->invoice->amount,
|
||||||
|
'client_id' => $client->hashed_id,
|
||||||
|
'invoices' => '',
|
||||||
|
'payment_date' => '2020/12/12',
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = $this->withHeaders([
|
||||||
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
|
'X-API-TOKEN' => $this->token,
|
||||||
|
])->post('/api/v1/payments?include=invoices', $data);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(ValidationException $e) {
|
||||||
|
\Log::error('in the validator');
|
||||||
|
$message = json_decode($e->validator->getMessageBag(),1);
|
||||||
|
\Log::error($message);
|
||||||
|
$this->assertNotNull($message);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user