mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 21:22:58 +01:00
Merge remote-tracking branch 'upstream/v2' into v2-cypress-payments-tests
This commit is contained in:
commit
b03387c571
31
app/Factory/ExpenseCategoryFactory.php
Normal file
31
app/Factory/ExpenseCategoryFactory.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Models\ExpenseCategory;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ExpenseCategoryFactory
|
||||
{
|
||||
public static function create(int $company_id, int $user_id) :ExpenseCategory
|
||||
{
|
||||
$expense = new ExpenseCategory();
|
||||
$expense->user_id = $user_id;
|
||||
$expense->company_id = $company_id;
|
||||
$expense->name = '';
|
||||
$expense->is_deleted = false;;
|
||||
|
||||
return $expense;
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ class ExpenseFactory
|
||||
$expense->tax_rate2 = 0;
|
||||
$expense->tax_name3 = '';
|
||||
$expense->tax_rate3 = 0;
|
||||
$expense->expense_date = null;
|
||||
$expense->date = null;
|
||||
$expense->payment_date = null;
|
||||
|
||||
return $expense;
|
||||
|
@ -19,13 +19,13 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PaymentFactory
|
||||
{
|
||||
public static function create(int $company_id, int $user_id) :Payment
|
||||
public static function create(int $company_id, int $user_id, int $client_id = 0) :Payment
|
||||
{
|
||||
$payment = new Payment;
|
||||
|
||||
$payment->company_id = $company_id;
|
||||
$payment->user_id = $user_id;
|
||||
$payment->client_id = 0;
|
||||
$payment->client_id = $client_id;
|
||||
$payment->client_contact_id = null;
|
||||
$payment->invitation_id = null;
|
||||
$payment->company_gateway_id = null;
|
||||
|
33
app/Factory/TaskFactory.php
Normal file
33
app/Factory/TaskFactory.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\Models\Task;
|
||||
|
||||
class TaskFactory
|
||||
{
|
||||
public static function create($company_id, $user_id) :Task
|
||||
{
|
||||
$task = new Task;
|
||||
|
||||
$task->description = '';
|
||||
//$task->rate = '';
|
||||
$task->company_id = $company_id;
|
||||
$task->user_id = $user_id;
|
||||
$task->time_log = '[]';
|
||||
$task->is_running = false;
|
||||
$task->is_deleted = false;
|
||||
$task->duration = 0;
|
||||
|
||||
return $task;
|
||||
}
|
||||
}
|
117
app/Filters/TaskFilters.php
Normal file
117
app/Filters/TaskFilters.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* TaskFilters.
|
||||
*/
|
||||
class TaskFilters extends QueryFilters
|
||||
{
|
||||
/**
|
||||
* Filter based on search text.
|
||||
*
|
||||
* @param string query filter
|
||||
* @return Illuminate\Database\Query\Builder
|
||||
* @deprecated
|
||||
*/
|
||||
public function filter(string $filter = '') : Builder
|
||||
{
|
||||
if (strlen($filter) == 0) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
return $this->builder->where(function ($query) use ($filter) {
|
||||
$query->where('tasks.description', 'like', '%'.$filter.'%')
|
||||
->orWhere('tasks.custom_value1', 'like', '%'.$filter.'%')
|
||||
->orWhere('tasks.custom_value2', 'like', '%'.$filter.'%')
|
||||
->orWhere('tasks.custom_value3', 'like', '%'.$filter.'%')
|
||||
->orWhere('tasks.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 = 'tasks';
|
||||
$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->company();
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
|
||||
namespace App\Http\Controllers\ClientPortal;
|
||||
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Jobs\Invoice\InjectSignature;
|
||||
@ -71,18 +72,25 @@ class PaymentController extends Controller
|
||||
{
|
||||
$gateway = CompanyGateway::findOrFail(request()->input('company_gateway_id'));
|
||||
|
||||
/*find invoices*/
|
||||
/**
|
||||
* find invoices
|
||||
*
|
||||
* ['invoice_id' => xxx, 'amount' => 22.00]
|
||||
*
|
||||
*/
|
||||
|
||||
$payable_invoices = request()->payable_invoices;
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payable_invoices, 'invoice_id')))->get();
|
||||
$payable_invoices = collect(request()->payable_invoices);
|
||||
$invoices = Invoice::whereIn('id', $this->transformKeys($payable_invoices->pluck('invoice_id')->toArray()))->get();
|
||||
|
||||
/* pop non payable invoice from the $payable_invoices array */
|
||||
$payable_invoices = $payable_invoices->filter(function ($payable_invoice) use ($invoices){
|
||||
|
||||
return $invoices->where('hashed_id', $payable_invoice['invoice_id'])->first()->isPayable();
|
||||
|
||||
/*filter only payable invoices*/
|
||||
$invoices = $invoices->filter(function ($invoice) {
|
||||
return $invoice->isPayable();
|
||||
});
|
||||
|
||||
/*return early if no invoices*/
|
||||
if ($invoices->count() == 0) {
|
||||
if ($payable_invoices->count() == 0) {
|
||||
return redirect()
|
||||
->route('client.invoices.index')
|
||||
->with(['warning' => 'No payable invoices selected.']);
|
||||
@ -91,10 +99,9 @@ class PaymentController extends Controller
|
||||
$settings = auth()->user()->client->getMergedSettings();
|
||||
|
||||
/*iterate through invoices and add gateway fees and other payment metadata*/
|
||||
foreach ($payable_invoices as $key => $payable_invoice) {
|
||||
|
||||
$payable_invoices[$key]['amount'] = Number::parseFloat($payable_invoice['amount']);
|
||||
$payable_invoice['amount'] = $payable_invoices[$key]['amount'];
|
||||
$payable_invoices = $payable_invoices->map(function($payable_invoice) use($invoices, $settings){
|
||||
|
||||
$payable_invoice['amount'] = Number::parseFloat($payable_invoice['amount']);
|
||||
|
||||
$invoice = $invoices->first(function ($inv) use ($payable_invoice) {
|
||||
return $payable_invoice['invoice_id'] == $inv->hashed_id;
|
||||
@ -135,8 +142,8 @@ class PaymentController extends Controller
|
||||
}
|
||||
} // Make sure 'amount' from form is not higher than 'amount' from invoice.
|
||||
|
||||
$payable_invoices[$key]['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format());
|
||||
$payable_invoices[$key]['invoice_number'] = $invoice->number;
|
||||
$payable_invoice['due_date'] = $this->formatDate($invoice->due_date, $invoice->client->date_format());
|
||||
$payable_invoice['invoice_number'] = $invoice->number;
|
||||
|
||||
if (isset($invoice->po_number)) {
|
||||
$additional_info = $invoice->po_number;
|
||||
@ -146,8 +153,11 @@ class PaymentController extends Controller
|
||||
$additional_info = $invoice->date;
|
||||
}
|
||||
|
||||
$payable_invoices[$key]['additional_info'] = $additional_info;
|
||||
}
|
||||
$payable_invoice['additional_info'] = $additional_info;
|
||||
|
||||
return $payable_invoice;
|
||||
|
||||
});
|
||||
|
||||
if ((bool) request()->signature) {
|
||||
$invoices->each(function ($invoice) {
|
||||
@ -155,27 +165,16 @@ class PaymentController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
//$payment_methods = auth()->user()->client->getPaymentMethods(array_sum(array_column($payable_invoices, 'amount_with_fee')));
|
||||
|
||||
$payment_method_id = request()->input('payment_method_id');
|
||||
|
||||
$invoice_totals = array_sum(array_column($payable_invoices, 'amount'));
|
||||
$invoice_totals = $payable_invoices->sum('amount');
|
||||
|
||||
$first_invoice = $invoices->first();
|
||||
|
||||
$credit_totals = $first_invoice->client->service()->getCreditBalance();
|
||||
|
||||
$starting_invoice_amount = $first_invoice->amount;
|
||||
|
||||
// $fee_totals = round($gateway->calcGatewayFee($invoice_totals, true), $first_invoice->client->currency()->precision);
|
||||
|
||||
// if (!$first_invoice->uses_inclusive_taxes) {
|
||||
// $fee_tax = 0;
|
||||
// $fee_tax += round(($first_invoice->tax_rate1 / 100) * $fee_totals, $first_invoice->client->currency()->precision);
|
||||
// $fee_tax += round(($first_invoice->tax_rate2 / 100) * $fee_totals, $first_invoice->client->currency()->precision);
|
||||
// $fee_tax += round(($first_invoice->tax_rate3 / 100) * $fee_totals, $first_invoice->client->currency()->precision);
|
||||
|
||||
// $fee_totals += $fee_tax;
|
||||
// }
|
||||
|
||||
$first_invoice->service()->addGatewayFee($gateway, $payment_method_id, $invoice_totals)->save();
|
||||
|
||||
/**
|
||||
@ -188,15 +187,16 @@ class PaymentController extends Controller
|
||||
|
||||
$payment_hash = new PaymentHash;
|
||||
$payment_hash->hash = Str::random(128);
|
||||
$payment_hash->data = $payable_invoices;
|
||||
$payment_hash->data = $payable_invoices->toArray();
|
||||
$payment_hash->fee_total = $fee_totals;
|
||||
$payment_hash->fee_invoice_id = $first_invoice->id;
|
||||
$payment_hash->save();
|
||||
|
||||
$totals = [
|
||||
'credit_totals' => $credit_totals,
|
||||
'invoice_totals' => $invoice_totals,
|
||||
'fee_total' => $fee_totals,
|
||||
'amount_with_fee' => $invoice_totals + $fee_totals,
|
||||
'amount_with_fee' => max(0, (($invoice_totals + $fee_totals) - $credit_totals)),
|
||||
];
|
||||
|
||||
$data = [
|
||||
@ -219,24 +219,67 @@ class PaymentController extends Controller
|
||||
/*Payment Gateway*/
|
||||
$gateway = CompanyGateway::find($request->input('company_gateway_id'))->firstOrFail();
|
||||
|
||||
//REFACTOR - Entry point for the gateway response - we don't need to do anything at this point.
|
||||
//
|
||||
// - Inside each gateway driver, we should use have a generic code path (in BaseDriver.php)for successful/failed payment
|
||||
//
|
||||
// Success workflow
|
||||
//
|
||||
// - Rehydrate the hash and iterate through the invoices and update the balances
|
||||
// - Update the type_id of the gateway fee to type_id 4
|
||||
// - Link invoices to payment
|
||||
//
|
||||
// Failure workflow
|
||||
//
|
||||
// - Rehydrate hash, iterate through invoices and remove type_id 3's
|
||||
// - Recalcuate invoice totals
|
||||
|
||||
return $gateway
|
||||
->driver(auth()->user()->client)
|
||||
->setPaymentMethod($request->input('payment_method_id'))
|
||||
->processPaymentResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pay for invoice/s using credits only.
|
||||
*
|
||||
* @param Request $request The request object
|
||||
* @return Response The response view
|
||||
*/
|
||||
public function credit_response(Request $request)
|
||||
{
|
||||
$payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->input('payment_hash')])->first();
|
||||
|
||||
/* Hydrate the $payment */
|
||||
if($payment_hash->payment()->exists())
|
||||
$payment = $payment_hash->payment;
|
||||
else {
|
||||
$payment = PaymentFactory::create($payment_hash->fee_invoice->company_id, $payment_hash->fee_invoice->user_id);
|
||||
$payment->client_id = $payment_hash->fee_invoice->client_id;
|
||||
$payment->save();
|
||||
|
||||
$payment_hash->payment_id = $payment->id;
|
||||
$payment_hash->save();
|
||||
}
|
||||
|
||||
/* Iterate through the invoices and apply credits to them */
|
||||
collect($payment_hash->invoices())->each(function ($payable_invoice) use ($payment, $payment_hash){
|
||||
|
||||
$invoice = Invoice::find($this->decodePrimaryKey($payable_invoice->invoice_id));
|
||||
$amount = $payable_invoice->amount;
|
||||
|
||||
$credits = $payment_hash->fee_invoice
|
||||
->client
|
||||
->service()
|
||||
->getCredits();
|
||||
|
||||
foreach($credits as $credit)
|
||||
{
|
||||
//starting invoice balance
|
||||
$invoice_balance = $invoice->balance;
|
||||
|
||||
//credit payment applied
|
||||
$credit->service()->applyPayment($invoice, $amount, $payment);
|
||||
|
||||
//amount paid from invoice calculated
|
||||
$remaining_balance = ($invoice_balance - $invoice->fresh()->balance);
|
||||
|
||||
//reduce the amount to be paid on the invoice from the NEXT credit
|
||||
$amount -= $remaining_balance;
|
||||
|
||||
//break if the invoice is no longer PAYABLE OR there is no more amount to be applied
|
||||
if(!$invoice->isPayable() || (int)$amount == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
|
||||
|
||||
}
|
||||
}
|
||||
|
435
app/Http/Controllers/ExpenseCategoryController.php
Normal file
435
app/Http/Controllers/ExpenseCategoryController.php
Normal file
@ -0,0 +1,435 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Factory\ExpenseCategoryFactory;
|
||||
use App\Http\Requests\ExpenseCategory\CreateExpenseCategoryRequest;
|
||||
use App\Http\Requests\ExpenseCategory\DestroyExpenseCategoryRequest;
|
||||
use App\Http\Requests\ExpenseCategory\EditExpenseCategoryRequest;
|
||||
use App\Http\Requests\ExpenseCategory\ShowExpenseCategoryRequest;
|
||||
use App\Http\Requests\ExpenseCategory\StoreExpenseCategoryRequest;
|
||||
use App\Http\Requests\ExpenseCategory\UpdateExpenseCategoryRequest;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Repositories\BaseRepository;
|
||||
use App\Transformers\ExpenseCategoryTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class ExpenseCategoryController.
|
||||
*/
|
||||
class ExpenseCategoryController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
protected $entity_type = ExpenseCategory::class;
|
||||
|
||||
protected $entity_transformer = ExpenseCategoryTransformer::class;
|
||||
|
||||
protected $base_repo;
|
||||
|
||||
public function __construct(BaseRepository $base_repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->base_repo = $base_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/expense_categories",
|
||||
* operationId="getExpenseCategorys",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Gets a list of expense_categories",
|
||||
* description="Lists tax rates",
|
||||
* @OA\Parameter(ref="#/components/parameters/index"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="A list of expense_categories",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/ExpenseCategory"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$expense_categories = ExpenseCategory::scope();
|
||||
|
||||
return $this->listResponse($expense_categories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/expense_categories/create",
|
||||
* operationId="getExpenseCategoryCreate",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Gets a new blank Expens Category object",
|
||||
* description="Returns a blank object with default values",
|
||||
* @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\Response(
|
||||
* response=200,
|
||||
* description="A blank Expens Category object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/ExpenseCategory"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function create(CreateExpenseCategoryRequest $request)
|
||||
{
|
||||
$expense_category = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
||||
|
||||
return $this->itemResponse($expense_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(StoreExpenseCategoryRequest $request)
|
||||
{
|
||||
$expense_category = ExpenseCategoryFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
||||
$expense_category->fill($request->all());
|
||||
$expense_category->save();
|
||||
|
||||
return $this->itemResponse($expense_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/expense_categories/{id}",
|
||||
* operationId="showExpenseCategory",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Shows a Expens Category",
|
||||
* description="Displays an ExpenseCategory by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The ExpenseCategory Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the Expens Category object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/ExpenseCategory"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function show(ShowExpenseCategoryRequest $request, ExpenseCategory $expense_category)
|
||||
{
|
||||
return $this->itemResponse($expense_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/expense_categories/{id}/edit",
|
||||
* operationId="editExpenseCategory",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Shows a Expens Category for editting",
|
||||
* description="Displays a Expens Category by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The ExpenseCategory Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the Expens Category object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/ExpenseCategory"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function edit(EditExpenseCategoryRequest $request, ExpenseCategory $expense_category)
|
||||
{
|
||||
return $this->itemResponse($expense_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param App\Models\Client $client
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/expense_categories/{id}",
|
||||
* operationId="updateExpenseCategory",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Updates a tax rate",
|
||||
* description="Handles the updating of a tax rate by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The ExpenseCategory Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the ExpenseCategory object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/ExpenseCategory"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function update(UpdateExpenseCategoryRequest $request, ExpenseCategory $expense_category)
|
||||
{
|
||||
$expense_category->fill($request->all());
|
||||
$expense_category->save();
|
||||
|
||||
return $this->itemResponse($expense_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/expense_categories/{id}",
|
||||
* operationId="deleteExpenseCategory",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Deletes a ExpenseCategory",
|
||||
* description="Handles the deletion of an ExpenseCategory by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The ExpenseCategory Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns a HTTP status",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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\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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function destroy(DestroyExpenseCategoryRequest $request, ExpenseCategory $expense_category)
|
||||
{
|
||||
$expense_category->is_deleted = true;
|
||||
$expense_category->save();
|
||||
$expense_category->delete();
|
||||
|
||||
return $this->itemResponse($expense_category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bulk actions on the list view.
|
||||
*
|
||||
* @param BulkExpenseCategoryRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/expense_categories/bulk",
|
||||
* operationId="bulkExpenseCategorys",
|
||||
* tags={"expense_categories"},
|
||||
* summary="Performs bulk actions on an array of ExpenseCategorys",
|
||||
* description="",
|
||||
* @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/index"),
|
||||
* @OA\RequestBody(
|
||||
* description="Expens Categorys",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="integer",
|
||||
* description="Array of hashed IDs to be bulk 'actioned",
|
||||
* example="[0,1,2,3]",
|
||||
* ),
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The ExpenseCategory List response",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Webhook"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function bulk()
|
||||
{
|
||||
$action = request()->input('action');
|
||||
|
||||
$ids = request()->input('ids');
|
||||
|
||||
$expense_categories = ExpenseCategory::withTrashed()->find($this->transformKeys($ids));
|
||||
|
||||
$expense_categories->each(function ($expense_category, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $expense_category)) {
|
||||
$this->base_repo->{$action}($expense_category);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(ExpenseCategory::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
}
|
13
app/Http/Controllers/OpenAPI/ExpenseCategorySchema.php
Normal file
13
app/Http/Controllers/OpenAPI/ExpenseCategorySchema.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="ExpenseCategory",
|
||||
* type="object",
|
||||
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
|
||||
* @OA\Property(property="name", type="string", example="Accounting", description="______"),
|
||||
* @OA\Property(property="user_id", type="string", example="XS987sD", description="______"),
|
||||
* @OA\Property(property="is_deleted", type="boolean", example=true, description="______"),
|
||||
* @OA\Property(property="updated_at", type="string", example="2", description="______"),
|
||||
* @OA\Property(property="created_at", type="string", example="2", description="______"),
|
||||
* )
|
||||
*/
|
@ -32,7 +32,7 @@
|
||||
* @OA\Property(property="amount", type="number", format="float", example="10.00", description="_________"),
|
||||
* @OA\Property(property="foreign_amount", type="number", format="float", example="10.00", description="_________"),
|
||||
* @OA\Property(property="exchange_rate", type="number", format="float", example="0.80", description="_________"),
|
||||
* @OA\Property(property="expense_date", type="string", example="", description="________"),
|
||||
* @OA\Property(property="date", type="string", example="", description="________"),
|
||||
* @OA\Property(property="payment_date", type="string", example="", description="________"),
|
||||
* @OA\Property(property="should_be_invoiced", type="boolean", example=true, description="_________"),
|
||||
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
|
||||
|
30
app/Http/Controllers/OpenAPI/TaskSchema.php
Normal file
30
app/Http/Controllers/OpenAPI/TaskSchema.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="Task",
|
||||
* type="object",
|
||||
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="_________"),
|
||||
* @OA\Property(property="user_id", type="string", example="", description="__________"),
|
||||
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
|
||||
* @OA\Property(property="company_id", type="string", example="", description="________"),
|
||||
* @OA\Property(property="client_id", type="string", example="", description="________"),
|
||||
* @OA\Property(property="invoice_id", type="string", example="", description="________"),
|
||||
* @OA\Property(property="project_id", type="string", example="", description="________"),
|
||||
* @OA\Property(property="number", type="string", example="", description="________"),
|
||||
* @OA\Property(property="time_log", type="string", example="", description="________"),
|
||||
* @OA\Property(property="start_time", type="integer", example="", description="________"),
|
||||
* @OA\Property(property="is_running", type="boolean", example=true, description="________"),
|
||||
* @OA\Property(property="is_deleted", type="boolean", example=true, description="________"),
|
||||
* @OA\Property(property="task_status_id", type="string", example="", description="________"),
|
||||
* @OA\Property(property="description", type="string", example="", description="________"),
|
||||
* @OA\Property(property="duration", type="integer", example="", description="________"),
|
||||
* @OA\Property(property="task_status_sort_order", type="integer", example="", description="________"),
|
||||
* @OA\Property(property="custom_value1", type="string", example="", description="________"),
|
||||
* @OA\Property(property="custom_value2", type="string", example="", description="________"),
|
||||
* @OA\Property(property="custom_value3", type="string", example="", description="________"),
|
||||
* @OA\Property(property="custom_value4", type="string", example="", description="________"),
|
||||
* @OA\Property(property="created_at", type="number", format="integer", example="1434342123", description="Timestamp"),
|
||||
* @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),
|
||||
* @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
|
||||
* )
|
||||
*/
|
@ -353,6 +353,10 @@ class ProjectController extends BaseController
|
||||
$project->fill($request->all());
|
||||
$project->save();
|
||||
|
||||
if ($request->has('documents')) {
|
||||
$this->saveDocuments($request->input('documents'), $project);
|
||||
}
|
||||
|
||||
return $this->itemResponse($project->fresh());
|
||||
}
|
||||
|
||||
|
@ -192,6 +192,8 @@ class SetupController extends Controller
|
||||
}
|
||||
|
||||
Browsershot::html('PDF GENERATION WORKS! Thank you for using Invoice Ninja!')
|
||||
->setNodeBinary(config('ninja.system.node_path'))
|
||||
->setNpmBinary(config('ninja.system.npm_path'))
|
||||
->noSandbox()
|
||||
->savePdf(
|
||||
public_path('test.pdf')
|
||||
|
504
app/Http/Controllers/TaskController.php
Normal file
504
app/Http/Controllers/TaskController.php
Normal file
@ -0,0 +1,504 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Factory\TaskFactory;
|
||||
use App\Filters\TaskFilters;
|
||||
use App\Http\Requests\Task\CreateTaskRequest;
|
||||
use App\Http\Requests\Task\DestroyTaskRequest;
|
||||
use App\Http\Requests\Task\EditTaskRequest;
|
||||
use App\Http\Requests\Task\ShowTaskRequest;
|
||||
use App\Http\Requests\Task\StoreTaskRequest;
|
||||
use App\Http\Requests\Task\UpdateTaskRequest;
|
||||
use App\Jobs\Entity\ActionEntity;
|
||||
use App\Jobs\Util\ProcessBulk;
|
||||
use App\Jobs\Util\UploadAvatar;
|
||||
use App\Models\Country;
|
||||
use App\Models\Currency;
|
||||
use App\Models\Task;
|
||||
use App\Models\Size;
|
||||
use App\Repositories\BaseRepository;
|
||||
use App\Repositories\TaskRepository;
|
||||
use App\Transformers\TaskTransformer;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\Uploadable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Class TaskController.
|
||||
* @covers App\Http\Controllers\TaskController
|
||||
*/
|
||||
class TaskController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
use Uploadable;
|
||||
use BulkOptions;
|
||||
|
||||
protected $entity_type = Task::class;
|
||||
|
||||
protected $entity_transformer = TaskTransformer::class;
|
||||
|
||||
/**
|
||||
* @var Taskepository
|
||||
*/
|
||||
protected $task_repo;
|
||||
|
||||
/**
|
||||
* TaskController constructor.
|
||||
* @param TaskRepository $taskRepo
|
||||
*/
|
||||
public function __construct(TaskRepository $task_repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->task_repo = $task_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/tasks",
|
||||
* operationId="getTasks",
|
||||
* tags={"tasks"},
|
||||
* summary="Gets a list of tasks",
|
||||
* description="Lists tasks, search and filters allow fine grained lists to be generated.
|
||||
*
|
||||
* Query parameters can be added to performed more fine grained filtering of the tasks, these are handled by the TaskFilters class which defines the methods available",
|
||||
* @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 tasks",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Task"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function index(TaskFilters $filters)
|
||||
{
|
||||
$tasks = Task::filter($filters);
|
||||
|
||||
return $this->listResponse($tasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/tasks/{id}",
|
||||
* operationId="showTask",
|
||||
* tags={"tasks"},
|
||||
* summary="Shows a client",
|
||||
* description="Displays a client by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Task Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the task object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Task"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function show(ShowTaskRequest $request, Task $task)
|
||||
{
|
||||
return $this->itemResponse($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/tasks/{id}/edit",
|
||||
* operationId="editTask",
|
||||
* tags={"tasks"},
|
||||
* summary="Shows a client for editting",
|
||||
* description="Displays a client by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Task Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the client object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Task"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function edit(EditTaskRequest $request, Task $task)
|
||||
{
|
||||
return $this->itemResponse($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param App\Models\Task $task
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/tasks/{id}",
|
||||
* operationId="updateTask",
|
||||
* tags={"tasks"},
|
||||
* summary="Updates a client",
|
||||
* description="Handles the updating of a client by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Task Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the client object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Task"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function update(UpdateTaskRequest $request, Task $task)
|
||||
{
|
||||
if ($request->entityIsDeleted($task)) {
|
||||
return $request->disallowUpdate();
|
||||
}
|
||||
|
||||
$task = $this->task_repo->save($request->all(), $task);
|
||||
|
||||
return $this->itemResponse($task->fresh());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/tasks/create",
|
||||
* operationId="getTasksCreate",
|
||||
* tags={"tasks"},
|
||||
* summary="Gets a new blank client object",
|
||||
* description="Returns a blank object with default values",
|
||||
* @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\Response(
|
||||
* response=200,
|
||||
* description="A blank client object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Task"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function create(CreateTaskRequest $request)
|
||||
{
|
||||
$task = TaskFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
||||
|
||||
return $this->itemResponse($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/tasks",
|
||||
* operationId="storeTask",
|
||||
* tags={"tasks"},
|
||||
* summary="Adds a client",
|
||||
* description="Adds an client to a company",
|
||||
* @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\Response(
|
||||
* response=200,
|
||||
* description="Returns the saved client object",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Task"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function store(StoreTaskRequest $request)
|
||||
{
|
||||
$task = $this->task_repo->save($request->all(), TaskFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
return $this->itemResponse($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/tasks/{id}",
|
||||
* operationId="deleteTask",
|
||||
* tags={"tasks"},
|
||||
* summary="Deletes a client",
|
||||
* description="Handles the deletion of a client by id",
|
||||
* @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(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Task Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns a HTTP status",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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\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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function destroy(DestroyTaskRequest $request, Task $task)
|
||||
{
|
||||
//may not need these destroy routes as we are using actions to 'archive/delete'
|
||||
$task->delete();
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform bulk actions on the list view.
|
||||
*
|
||||
* @param BulkTaskRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/tasks/bulk",
|
||||
* operationId="bulkTasks",
|
||||
* tags={"tasks"},
|
||||
* summary="Performs bulk actions on an array of tasks",
|
||||
* description="",
|
||||
* @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/index"),
|
||||
* @OA\RequestBody(
|
||||
* description="User credentials",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="integer",
|
||||
* description="Array of hashed IDs to be bulk 'actioned",
|
||||
* example="[0,1,2,3]",
|
||||
* ),
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The Task User response",
|
||||
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-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/Task"),
|
||||
* ),
|
||||
* @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"),
|
||||
* ),
|
||||
* )
|
||||
*/
|
||||
public function bulk()
|
||||
{
|
||||
$action = request()->input('action');
|
||||
|
||||
$ids = request()->input('ids');
|
||||
$tasks = Task::withTrashed()->find($this->transformKeys($ids));
|
||||
|
||||
$tasks->each(function ($task, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $task)) {
|
||||
$this->task_repo->{$action}($task);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(Task::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a client statement.
|
||||
*
|
||||
* @return [type] [description]
|
||||
*/
|
||||
public function statement()
|
||||
{
|
||||
//todo
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ class CreateAccountRequest extends Request
|
||||
'first_name' => 'string|max:100',
|
||||
'last_name' => 'string:max:100',
|
||||
'password' => 'required|string|min:6',
|
||||
'email' => 'bail|required|email',
|
||||
'email' => 'bail|required|email:rfc,dns',
|
||||
'email' => new NewUniqueUserRule(),
|
||||
'privacy_policy' => 'required',
|
||||
'terms_of_service' => 'required',
|
||||
|
@ -28,7 +28,7 @@ class RegisterRequest extends FormRequest
|
||||
'first_name' => ['required', 'string', 'max:255'],
|
||||
'last_name' => ['required', 'string', 'max:255'],
|
||||
'phone' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:client_contacts'],
|
||||
'email' => ['required', 'string', 'email:rfc,dns', 'max:255', 'unique:client_contacts'],
|
||||
'password' => ['required', 'string', 'min:6', 'confirmed'],
|
||||
];
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class UpdateContactRequest extends Request
|
||||
return [
|
||||
'first_name' => 'required',
|
||||
'last_name' => 'required',
|
||||
'email' => 'required|email|unique:client_contacts,email,'.auth()->user()->id,
|
||||
'email' => 'required|email:rfc,dns|unique:client_contacts,email,'.auth()->user()->id,
|
||||
'password' => 'sometimes|nullable|min:6|confirmed',
|
||||
];
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\ExpenseCategory;
|
||||
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class BulkExpenseCategoryRequest extends FormRequest
|
||||
{
|
||||
use BulkOptions;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\ExpenseCategory;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\ExpenseCategory;
|
||||
|
||||
class CreateExpenseCategoryRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', ExpenseCategory::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\ExpenseCategory;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\ExpenseCategory;
|
||||
|
||||
class DestroyExpenseCategoryRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->expense_category);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\ExpenseCategory;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\ExpenseCategory;
|
||||
|
||||
class EditExpenseCategoryRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->expense_category);
|
||||
}
|
||||
|
||||
// public function prepareForValidation()
|
||||
// {
|
||||
// $input = $this->all();
|
||||
|
||||
// //$input['id'] = $this->encodePrimaryKey($input['id']);
|
||||
|
||||
// $this->replace($input);
|
||||
|
||||
// }
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\ExpenseCategory;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\ExpenseCategory;
|
||||
|
||||
class ShowExpenseCategoryRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('view', $this->expense_category);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\ExpenseCategory;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\ExpenseCategory;
|
||||
|
||||
class StoreExpenseCategoryRequest extends Request
|
||||
{
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', ExpenseCategory::class);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
$rules['name'] = 'required|unique:expense_categories,name,null,null,company_id,'.auth()->user()->companyId();
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\ExpenseCategory;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
class UpdateExpenseCategoryRequest extends Request
|
||||
{
|
||||
use ChecksEntityStatus;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->expense_category);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
|
||||
$rules = [];
|
||||
|
||||
if ($this->input('name'))
|
||||
$rules['name'] = 'unique:expense_categories,name,'.$this->id.',id,company_id,'.$this->expense_category->company_id;
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
}
|
@ -34,7 +34,7 @@ class UpdateGroupSettingRequest extends Request
|
||||
{
|
||||
$rules['settings'] = new ValidClientGroupSettingsRule();
|
||||
|
||||
$rules['name'] = 'unique:group_settings,name,'.$this->id.',id,company_id,'.$this->group_setting->company_id;
|
||||
// $rules['name'] = 'unique:group_settings,name,'.$this->id.',id,company_id,'.$this->group_setting->company_id;
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class StoreProjectRequest extends Request
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
$rules['name'] ='required|unique:projects,name,null,null,company_id,'.auth()->user()->companyId();
|
||||
//$rules['name'] ='required|unique:projects,name,null,null,company_id,'.auth()->user()->companyId();
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
return $rules;
|
||||
|
@ -48,7 +48,7 @@ class StoreSetupRequest extends Request
|
||||
'terms_of_service' => 'required',
|
||||
'first_name' => 'required',
|
||||
'last_name' => 'required',
|
||||
'email' => 'required',
|
||||
'email' => 'required|email:rfc,dns',
|
||||
'password' => 'required',
|
||||
];
|
||||
}
|
||||
|
39
app/Http/Requests/Task/BulkTaskRequest.php
Normal file
39
app/Http/Requests/Task/BulkTaskRequest.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Task;
|
||||
|
||||
use App\Models\Task;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class BulkTaskRequest extends FormRequest
|
||||
{
|
||||
use BulkOptions;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->can(auth()->user()->isAdmin(), Task::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = $this->getGlobalRules();
|
||||
|
||||
/* We don't require IDs on bulk storing. */
|
||||
if ($this->action !== self::$STORE_METHOD) {
|
||||
$rules['ids'] = ['required'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
28
app/Http/Requests/Task/CreateTaskRequest.php
Normal file
28
app/Http/Requests/Task/CreateTaskRequest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Task;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Task;
|
||||
|
||||
class CreateTaskRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', Task::class);
|
||||
}
|
||||
}
|
28
app/Http/Requests/Task/DestroyTaskRequest.php
Normal file
28
app/Http/Requests/Task/DestroyTaskRequest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Task;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Task;
|
||||
|
||||
class DestroyTaskRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->task);
|
||||
}
|
||||
}
|
38
app/Http/Requests/Task/EditTaskRequest.php
Normal file
38
app/Http/Requests/Task/EditTaskRequest.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Task;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Task;
|
||||
|
||||
class EditTaskRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->task);
|
||||
}
|
||||
|
||||
// public function prepareForValidation()
|
||||
// {
|
||||
// $input = $this->all();
|
||||
|
||||
// //$input['id'] = $this->encodePrimaryKey($input['id']);
|
||||
|
||||
// $this->replace($input);
|
||||
|
||||
// }
|
||||
}
|
28
app/Http/Requests/Task/ShowTaskRequest.php
Normal file
28
app/Http/Requests/Task/ShowTaskRequest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Task;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Task;
|
||||
|
||||
class ShowTaskRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('view', $this->task);
|
||||
}
|
||||
}
|
85
app/Http/Requests/Task/StoreTaskRequest.php
Normal file
85
app/Http/Requests/Task/StoreTaskRequest.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Task;
|
||||
|
||||
use App\DataMapper\TaskSettings;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Task\UniqueTaskNumberRule;
|
||||
use App\Http\ValidationRules\ValidTaskGroupSettingsRule;
|
||||
use App\Models\Task;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreTaskRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', Task::class);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
//$rules['name'] = 'required|min:1';
|
||||
//$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
// $rules['number'] = new UniqueTaskNumberRule($this->all());
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
||||
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
|
||||
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
|
||||
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
|
||||
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('invoice_id', $input) && is_string($input['invoice_id'])) {
|
||||
$input['invoice_id'] = $this->decodePrimaryKey($input['invoice_id']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
// public function messages()
|
||||
// {
|
||||
// // return [
|
||||
// // 'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
||||
// // //'required' => trans('validation.required', ['attribute' => 'email']),
|
||||
// // 'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
|
||||
// // ];
|
||||
// }
|
||||
}
|
85
app/Http/Requests/Task/UpdateTaskRequest.php
Normal file
85
app/Http/Requests/Task/UpdateTaskRequest.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Task;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\IsDeletedRule;
|
||||
use App\Http\ValidationRules\ValidTaskGroupSettingsRule;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateTaskRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
use ChecksEntityStatus;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->task);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
|
||||
if ($this->input('number')) {
|
||||
$rules['number'] = 'unique:tasks,number,'.$this->id.',id,company_id,'.$this->taskss->company_id;
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
// public function messages()
|
||||
// {
|
||||
// return [
|
||||
// 'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
||||
// 'email' => ctrans('validation.email', ['attribute' => 'email']),
|
||||
// 'name.required' => ctrans('validation.required', ['attribute' => 'name']),
|
||||
// 'required' => ctrans('validation.required', ['attribute' => 'email']),
|
||||
// ];
|
||||
// }
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
||||
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('client_id', $input) && is_string($input['client_id'])) {
|
||||
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
|
||||
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
|
||||
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
|
||||
}
|
||||
|
||||
if (array_key_exists('invoice_id', $input) && is_string($input['invoice_id'])) {
|
||||
$input['invoice_id'] = $this->decodePrimaryKey($input['invoice_id']);
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ class UpdateUserRequest extends Request
|
||||
$rules = [];
|
||||
|
||||
if (isset($input['email'])) {
|
||||
$rules['email'] = ['sometimes', new UniqueUserRule($this->user, $input['email'])];
|
||||
$rules['email'] = ['email:rfc,dns', 'sometimes', new UniqueUserRule($this->user, $input['email'])];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
|
@ -517,7 +517,7 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
$fee_label = $gateway->calcGatewayFeeLabel($amount, $this);
|
||||
|
||||
$payment_urls[] = [
|
||||
'label' => ctrans('texts.'.$gateway->getTypeAlias($gateway_type_id)).$fee_label,
|
||||
'label' => $gateway->getTypeAlias($gateway_type_id) . $fee_label,
|
||||
'company_gateway_id' => $gateway_id,
|
||||
'gateway_type_id' => $gateway_type_id,
|
||||
];
|
||||
|
@ -79,7 +79,7 @@ class CompanyGateway extends BaseModel
|
||||
|
||||
public function getTypeAlias($gateway_type_id)
|
||||
{
|
||||
return GatewayType::find($gateway_type_id)->alias;
|
||||
return GatewayType::getAlias($gateway_type_id);
|
||||
}
|
||||
|
||||
/* This is the public entry point into the payment superclass */
|
||||
|
@ -25,7 +25,7 @@ class Expense extends BaseModel
|
||||
'client_id',
|
||||
'vendor_id',
|
||||
'expense_currency_id',
|
||||
'expense_date',
|
||||
'date',
|
||||
'invoice_currency_id',
|
||||
'amount',
|
||||
'foreign_amount',
|
||||
|
@ -37,4 +37,41 @@ class GatewayType extends StaticModel
|
||||
{
|
||||
return $this->hasMany(PaymentType::class);
|
||||
}
|
||||
|
||||
public static function getAlias($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::CREDIT_CARD:
|
||||
return ctrans('texts.credit_card');
|
||||
break;
|
||||
case self::BANK_TRANSFER:
|
||||
return ctrans('texts.bank_transfer');
|
||||
break;
|
||||
case self::PAYPAL:
|
||||
return ctrans('texts.paypal');
|
||||
break;
|
||||
case self::CRYPTO:
|
||||
return ctrans('texts.crypto');
|
||||
break;
|
||||
case self::CUSTOM:
|
||||
return ctrans('texts.custom');
|
||||
break;
|
||||
case self::ALIPAY:
|
||||
return ctrans('texts.alipay');
|
||||
break;
|
||||
case self::SOFORT:
|
||||
return ctrans('texts.sofort');
|
||||
break;
|
||||
case self::APPLE_PAY:
|
||||
return ctrans('texts.apple_pay');
|
||||
break;
|
||||
case self::SEPA:
|
||||
return ctrans('texts.sepa');
|
||||
break;
|
||||
|
||||
default:
|
||||
return 'Undefined.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +25,14 @@ class PaymentHash extends Model
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function payment()
|
||||
{
|
||||
return $this->belongsTo(Payment::class)->withTrashed();
|
||||
}
|
||||
|
||||
public function fee_invoice()
|
||||
{
|
||||
return $this->belongsTo(Invoice::class, 'fee_invoice_id', 'id');
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\Filterable;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -19,12 +20,16 @@ class Task extends BaseModel
|
||||
{
|
||||
use MakesHash;
|
||||
use SoftDeletes;
|
||||
|
||||
use Filterable;
|
||||
|
||||
protected $fillable = [
|
||||
'client_id',
|
||||
'invoice_id',
|
||||
'project_id',
|
||||
'custom_value1',
|
||||
'custom_value2',
|
||||
'custom_value3',
|
||||
'custom_value4',
|
||||
'description',
|
||||
'is_running',
|
||||
'time_log',
|
||||
@ -32,11 +37,6 @@ class Task extends BaseModel
|
||||
|
||||
protected $touches = [];
|
||||
|
||||
protected $casts = [
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
];
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
@ -66,4 +66,14 @@ class Task extends BaseModel
|
||||
{
|
||||
return $this->belongsTo(Client::class);
|
||||
}
|
||||
|
||||
public function invoice()
|
||||
{
|
||||
return $this->belongsTo(Invoice::class);
|
||||
}
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,12 @@ use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\StripePaymentDriver;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class Charge
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/** @var StripePaymentDriver */
|
||||
public $stripe;
|
||||
|
||||
@ -39,7 +42,7 @@ class Charge
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
|
||||
$invoice = sInvoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->first();
|
||||
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->first();
|
||||
|
||||
if ($invoice) {
|
||||
$description = "Invoice {$invoice->number} for {$amount} for client {$this->stripe->client->present()->name()}";
|
||||
|
32
app/Policies/ExpenseCategoryPolicy.php
Normal file
32
app/Policies/ExpenseCategoryPolicy.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Expense;
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* Class ExpensePolicy.
|
||||
*/
|
||||
class ExpenseCategoryPolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* Checks if the user has create permissions.
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user) : bool
|
||||
{
|
||||
return $user->isAdmin() || $user->hasPermission('create_expense_categories') || $user->hasPermission('create_all');
|
||||
}
|
||||
}
|
26
app/Policies/TaskPolicy.php
Normal file
26
app/Policies/TaskPolicy.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* Class TaskPolicy.
|
||||
*/
|
||||
class TaskPolicy extends EntityPolicy
|
||||
{
|
||||
public function create(User $user) : bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* Class TaxRatePolicy.
|
||||
|
@ -20,6 +20,7 @@ use App\Models\Credit;
|
||||
use App\Models\Design;
|
||||
use App\Models\Document;
|
||||
use App\Models\Expense;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
@ -29,6 +30,7 @@ use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\RecurringQuote;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
@ -41,6 +43,7 @@ use App\Policies\CompanyTokenPolicy;
|
||||
use App\Policies\CreditPolicy;
|
||||
use App\Policies\DesignPolicy;
|
||||
use App\Policies\DocumentPolicy;
|
||||
use App\Policies\ExpenseCategoryPolicy;
|
||||
use App\Policies\ExpensePolicy;
|
||||
use App\Policies\GroupSettingPolicy;
|
||||
use App\Policies\InvoicePolicy;
|
||||
@ -51,6 +54,7 @@ use App\Policies\ProjectPolicy;
|
||||
use App\Policies\QuotePolicy;
|
||||
use App\Policies\RecurringInvoicePolicy;
|
||||
use App\Policies\RecurringQuotePolicy;
|
||||
use App\Policies\TaskPolicy;
|
||||
use App\Policies\TaxRatePolicy;
|
||||
use App\Policies\UserPolicy;
|
||||
use App\Policies\VendorPolicy;
|
||||
@ -76,6 +80,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
Design::class => DesignPolicy::class,
|
||||
Document::class => DocumentPolicy::class,
|
||||
Expense::class => ExpensePolicy::class,
|
||||
ExpenseCategory::class => ExpenseCategoryPolicy::class,
|
||||
GroupSetting::class => GroupSettingPolicy::class,
|
||||
Invoice::class => InvoicePolicy::class,
|
||||
Payment::class => PaymentPolicy::class,
|
||||
@ -86,6 +91,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
RecurringInvoice::class => RecurringInvoicePolicy::class,
|
||||
RecurringQuote::class => RecurringQuotePolicy::class,
|
||||
Webhook::class => WebhookPolicy::class,
|
||||
Task::class => TaskPolicy::class,
|
||||
TaxRate::class => TaxRatePolicy::class,
|
||||
User::class => UserPolicy::class,
|
||||
Vendor::class => VendorPolicy::class,
|
||||
|
@ -52,6 +52,11 @@ class ExpenseRepository extends BaseRepository
|
||||
|
||||
$expense->save();
|
||||
|
||||
|
||||
if (array_key_exists('documents', $data)) {
|
||||
$this->saveDocuments($data['documents'], $expense);
|
||||
}
|
||||
|
||||
// if ($expense->id_number == "" || !$expense->id_number) {
|
||||
// $expense->id_number = $this->getNextExpenseNumber($expense);
|
||||
// } //todo write tests for this and make sure that custom expense numbers also works as expected from here
|
||||
|
75
app/Repositories/TaskRepository.php
Normal file
75
app/Repositories/TaskRepository.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Factory\TaskFactory;
|
||||
use App\Models\Task;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* TaskRepository.
|
||||
*/
|
||||
class TaskRepository extends BaseRepository
|
||||
{
|
||||
use GeneratesCounter;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class name.
|
||||
*
|
||||
* @return string The class name.
|
||||
*/
|
||||
public function getClassName()
|
||||
{
|
||||
return Task::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the task and its contacts.
|
||||
*
|
||||
* @param array $data The data
|
||||
* @param \App\Models\task $task The task
|
||||
*
|
||||
* @return task|\App\Models\task|null task Object
|
||||
*/
|
||||
|
||||
public function save(array $data, Task $task) : ?Task
|
||||
{
|
||||
|
||||
$task->fill($data);
|
||||
$task->save();
|
||||
|
||||
if (array_key_exists('documents', $data)) {
|
||||
$this->saveDocuments($data['documents'], $task);
|
||||
}
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store tasks in bulk.
|
||||
*
|
||||
* @param array $task
|
||||
* @return task|null
|
||||
*/
|
||||
public function create($task): ?Task
|
||||
{
|
||||
return $this->save(
|
||||
$task,
|
||||
TaskFactory::create(auth()->user()->company()->id, auth()->user()->id)
|
||||
);
|
||||
}
|
||||
}
|
@ -75,6 +75,10 @@ class VendorRepository extends BaseRepository
|
||||
$data['name'] = $vendor->present()->name();
|
||||
}
|
||||
|
||||
if (array_key_exists('documents', $data)) {
|
||||
$this->saveDocuments($data['documents'], $vendor);
|
||||
}
|
||||
|
||||
return $vendor;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@
|
||||
namespace App\Services\Client;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Utils\Number;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ClientService
|
||||
{
|
||||
@ -43,6 +45,27 @@ class ClientService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreditBalance() :float
|
||||
{
|
||||
$credits = $this->client->credits
|
||||
->where('is_deleted', false)
|
||||
->where('balance', '>', 0)
|
||||
->sortBy('created_at');
|
||||
|
||||
return Number::roundValue($credits->sum('balance'), $this->client->currency()->precision);
|
||||
}
|
||||
|
||||
public function getCredits() :Collection
|
||||
{
|
||||
|
||||
return $this->client->credits
|
||||
->where('is_deleted', false)
|
||||
->where('balance', '>', 0)
|
||||
->sortBy('created_at');
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function save() :Client
|
||||
{
|
||||
$this->client->save();
|
||||
|
152
app/Services/Credit/ApplyPayment.php
Normal file
152
app/Services/Credit/ApplyPayment.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Services\Credit;
|
||||
|
||||
use App\DataMapper\InvoiceItem;
|
||||
use App\Events\Invoice\InvoiceWasPaid;
|
||||
use App\Events\Invoice\InvoiceWasUpdated;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Ninja;
|
||||
|
||||
class ApplyPayment
|
||||
{
|
||||
|
||||
private $credit;
|
||||
|
||||
private $invoice;
|
||||
|
||||
private $amount;
|
||||
|
||||
private $amount_applied;
|
||||
|
||||
private $payment;
|
||||
|
||||
public function __construct(Credit $credit, Invoice $invoice, float $amount, Payment $payment)
|
||||
{
|
||||
$this->credit = $credit;
|
||||
$this->invoice = $invoice;
|
||||
$this->amount = $amount;
|
||||
$this->amount_applied = 0;
|
||||
$this->payment = $payment->fresh();
|
||||
}
|
||||
|
||||
public function run() :Credit
|
||||
{
|
||||
|
||||
//$available_credit_balance = $this->credit->balance;
|
||||
$applicable_amount = min($this->amount, $this->credit->balance);
|
||||
$invoice_balance = $this->invoice->balance;
|
||||
|
||||
/* Check invoice partial for amount to be cleared first */
|
||||
if($this->invoice->partial > 0){
|
||||
|
||||
$partial_payment = min($this->invoice->partial, $applicable_amount);
|
||||
|
||||
$this->invoice->partial -= $partial_payment;
|
||||
$invoice_balance -= $partial_payment;
|
||||
$this->amount -= $partial_payment;
|
||||
// $this->credit->balance -= $partial_payment;
|
||||
$applicable_amount -= $partial_payment;
|
||||
$this->amount_applied += $partial_payment;
|
||||
|
||||
}
|
||||
|
||||
/* If there is remaining amount use it on the balance */
|
||||
if($this->amount > 0 && $applicable_amount > 0 && $invoice_balance > 0){
|
||||
|
||||
$balance_payment = min($invoice_balance, $this->amount);
|
||||
|
||||
$invoice_balance -= $balance_payment;
|
||||
$this->amount -= $balance_payment;
|
||||
// $this->credit->balance -= $balance_payment;
|
||||
$this->amount_applied += $balance_payment;
|
||||
|
||||
}
|
||||
|
||||
$this->applyPaymentToCredit();
|
||||
|
||||
$this->addPaymentToLedger();
|
||||
|
||||
return $this->credit;
|
||||
|
||||
}
|
||||
|
||||
private function applyPaymentToCredit()
|
||||
{
|
||||
|
||||
$credit_item = new InvoiceItem;
|
||||
$credit_item->type_id = '1';
|
||||
$credit_item->product_key = ctrans('texts.credit');
|
||||
$credit_item->notes = ctrans('texts.credit_payment', ['invoice_number' => $this->invoice->number]);
|
||||
$credit_item->quantity = 1;
|
||||
$credit_item->cost = $this->amount_applied * -1;
|
||||
|
||||
$credit_items = $this->credit->line_items;
|
||||
$credit_items[] = $credit_item;
|
||||
|
||||
$this->credit->line_items = $credit_items;
|
||||
|
||||
$this->credit = $this->credit->calc()->getCredit();
|
||||
$this->credit->save();
|
||||
|
||||
}
|
||||
|
||||
private function addPaymentToLedger()
|
||||
{
|
||||
|
||||
$this->payment->amount += $this->amount_applied;
|
||||
$this->payment->applied += $this->amount_applied;
|
||||
$this->payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$this->payment->currency_id = $this->credit->client->getSetting('currency_id');
|
||||
$this->payment->save();
|
||||
|
||||
$this->payment
|
||||
->invoices()
|
||||
->attach($this->invoice->id, ['amount' => $this->amount_applied]);
|
||||
|
||||
$this->payment
|
||||
->credits()
|
||||
->attach($this->credit->id, ['amount' => $this->amount_applied]);
|
||||
|
||||
$this->payment
|
||||
->ledger()
|
||||
->updatePaymentBalance($this->amount_applied * -1);
|
||||
|
||||
$this->payment
|
||||
->client
|
||||
->service()
|
||||
->updateBalance($this->amount_applied * -1)
|
||||
->adjustCreditBalance($this->amount_applied * -1)
|
||||
->updatePaidToDate($this->amount_applied)
|
||||
->save();
|
||||
|
||||
$this->invoice
|
||||
->service()
|
||||
->updateBalance($this->amount_applied * -1)
|
||||
->updateStatus()
|
||||
->save();
|
||||
|
||||
$this->credit
|
||||
->ledger()
|
||||
->updateCreditBalance(($this->amount_applied * -1), "Credit payment applied to Invoice {$this->invoice->number}");
|
||||
|
||||
event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars()));
|
||||
|
||||
if((int)$this->invoice->balance == 0){
|
||||
$this->invoice->service()->deletePdf();
|
||||
event(new InvoiceWasPaid($this->invoice, $this->payment->company, Ninja::eventVars()));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
namespace App\Services\Credit;
|
||||
|
||||
use App\Models\Credit;
|
||||
use App\Services\Credit\ApplyPayment;
|
||||
use App\Services\Credit\CreateInvitations;
|
||||
use App\Services\Credit\MarkSent;
|
||||
|
||||
@ -61,6 +62,13 @@ class CreditService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function applyPayment($invoice, $amount, $payment)
|
||||
{
|
||||
$this->credit = (new ApplyPayment($this->credit, $invoice, $amount, $payment))->run();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the credit.
|
||||
* @return Credit object
|
||||
|
@ -84,7 +84,7 @@ class AutoBillInvoice extends AbstractService
|
||||
/* Build payment hash */
|
||||
$payment_hash = PaymentHash::create([
|
||||
'hash' => Str::random(128),
|
||||
'data' => ['invoice_id' => $this->invoice->hashed_id, 'amount' => $amount],
|
||||
'data' => [['invoice_id' => $this->invoice->hashed_id, 'amount' => $amount]],
|
||||
'fee_total' => $fee,
|
||||
'fee_invoice_id' => $this->invoice->id,
|
||||
]);
|
||||
@ -104,10 +104,8 @@ class AutoBillInvoice extends AbstractService
|
||||
*/
|
||||
private function finalizePaymentUsingCredits()
|
||||
{
|
||||
info("finalizing");
|
||||
info(print_r($this->used_credit,1));
|
||||
|
||||
$amount = array_sum(array_column($this->used_credit, 'amount'));
|
||||
info("amount {$amount}");
|
||||
|
||||
$payment = PaymentFactory::create($this->invoice->company_id, $this->invoice->user_id);
|
||||
$payment->amount = $amount;
|
||||
|
55
app/Transformers/ExpenseCategoryTransformer.php
Normal file
55
app/Transformers/ExpenseCategoryTransformer.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Transformers;
|
||||
|
||||
use App\Models\Document;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
* class ExpenseCategoryTransformer.
|
||||
*/
|
||||
class ExpenseCategoryTransformer extends EntityTransformer
|
||||
{
|
||||
use MakesHash;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $defaultIncludes = [
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $availableIncludes = [
|
||||
];
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param Expense $expense_category
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function transform(ExpenseCategory $expense_category)
|
||||
{
|
||||
return [
|
||||
'id' => $this->encodePrimaryKey($expense_category->id),
|
||||
'user_id' => $this->encodePrimaryKey($expense_category->user_id),
|
||||
'name' => (string) $expense_category->name ?: '',
|
||||
'is_deleted' => (bool) $expense_category->is_deleted,
|
||||
'updated_at' => (int) $expense_category->updated_at,
|
||||
'archived_at' => (int) $expense_category->deleted_at,
|
||||
'created_at' => (int) $expense_category->created_at,
|
||||
];
|
||||
}
|
||||
}
|
@ -58,7 +58,7 @@ class ExpenseTransformer extends EntityTransformer
|
||||
'bank_id' => (string) $expense->bank_id ?: '',
|
||||
'invoice_currency_id' => (string) $expense->invoice_currency_id ?: '',
|
||||
'expense_currency_id' => (string) $expense->expense_currency_id ?: '',
|
||||
'invoice_category_id' => (string) $expense->invoice_category_id ?: '',
|
||||
'category_id' => (string) $expense->category_id ?: '',
|
||||
'payment_type_id' => (string) $expense->payment_type_id ?: '',
|
||||
'recurring_expense_id' => (string) $expense->recurring_expense_id ?: '',
|
||||
'is_deleted' => (bool) $expense->is_deleted,
|
||||
@ -77,7 +77,8 @@ class ExpenseTransformer extends EntityTransformer
|
||||
'public_notes' => (string) $expense->public_notes ?: '',
|
||||
'transaction_reference' => (string) $expense->transaction_reference ?: '',
|
||||
'transaction_id' => (string) $expense->transaction_id ?: '',
|
||||
'expense_date' => $expense->expense_date ?: '',
|
||||
//'date' => $expense->date ?: '',
|
||||
'expense_date' => $expense->date ?: '',
|
||||
'payment_date' => $expense->payment_date ?: '',
|
||||
'custom_value1' => $expense->custom_value1 ?: '',
|
||||
'custom_value2' => $expense->custom_value2 ?: '',
|
||||
|
@ -44,6 +44,10 @@ class TaskTransformer extends EntityTransformer
|
||||
{
|
||||
return [
|
||||
'id' => (string) $this->encodePrimaryKey($task->id),
|
||||
'user_id' => (string) $this->encodePrimaryKey($task->user_id),
|
||||
'assigned_user_id' => (string) $this->encodePrimaryKey($task->assigned_user_id),
|
||||
'number' => (string) $task->number ?: '',
|
||||
'start_time' => (int) $task->start_time,
|
||||
'description' => $task->description ?: '',
|
||||
'duration' => 0,
|
||||
'created_at' => (int) $task->created_at,
|
||||
|
@ -35,6 +35,7 @@ class SystemHealth
|
||||
'xml',
|
||||
'bcmath',
|
||||
'mysqlnd',
|
||||
//'intl', //todo double check whether we need this for email dns validation
|
||||
];
|
||||
|
||||
private static $php_version = 7.3;
|
||||
|
@ -89,7 +89,7 @@ return [
|
||||
'date_formats' => App\Models\DateFormat::class,
|
||||
'datetime_formats' => App\Models\DatetimeFormat::class,
|
||||
'gateways' => App\Models\Gateway::class,
|
||||
'gateway_types' => App\Models\GatewayType::class,
|
||||
//'gateway_types' => App\Models\GatewayType::class,
|
||||
'industries' => App\Models\Industry::class,
|
||||
'languages' => App\Models\Language::class,
|
||||
'payment_types' => App\Models\PaymentType::class,
|
||||
|
37
database/factories/ExpenseCategoryFactory.php
Normal file
37
database/factories/ExpenseCategoryFactory.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\ExpenseCategory;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ExpenseCategoryFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = ExpenseCategory::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->text(10),
|
||||
];
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ class ExpenseFactory extends Factory
|
||||
'custom_value3' => $this->faker->text(10),
|
||||
'custom_value4' => $this->faker->text(10),
|
||||
'exchange_rate' => $this->faker->randomFloat(2, 0, 1),
|
||||
'expense_date' => $this->faker->date(),
|
||||
'date' => $this->faker->date(),
|
||||
'is_deleted' => false,
|
||||
'public_notes' => $this->faker->text(50),
|
||||
'private_notes' => $this->faker->text(50),
|
||||
|
@ -1244,11 +1244,12 @@ class CreateUsersTable extends Migration
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('user_id');
|
||||
$table->unsignedInteger('company_id')->index();
|
||||
$table->string('name')->nullable();
|
||||
$table->timestamps(6);
|
||||
$table->softDeletes();
|
||||
$table->string('name')->nullable();
|
||||
|
||||
$table->index(['company_id', 'deleted_at']);
|
||||
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade');
|
||||
});
|
||||
|
||||
Schema::create('expenses', function (Blueprint $table) {
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectNumberColumn extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('projects', function($table){
|
||||
$table->string('number')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('expenses', function ($t){
|
||||
$t->renameColumn('expense_date', 'date');
|
||||
});
|
||||
|
||||
Schema::table('expense_categories', function ($t){
|
||||
$t->boolean('is_deleted')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -100,7 +100,7 @@ class RandomDataSeeder extends Seeder
|
||||
$account->save();
|
||||
|
||||
$user = User::factory()->create([
|
||||
'email' => $faker->email,
|
||||
'email' => $faker->freeEmail,
|
||||
'account_id' => $account->id,
|
||||
'confirmation_code' => $this->createDbHash(config('database.default')),
|
||||
]);
|
||||
|
773
package-lock.json
generated
773
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"cypress": "^4.12.1",
|
||||
"laravel-mix-purgecss": "^5.0.0",
|
||||
"puppeteer": "^5.3.1",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -27,7 +28,6 @@
|
||||
"jsignature": "^2.1.3",
|
||||
"laravel-mix": "^5.0.7",
|
||||
"lodash": "^4.17.20",
|
||||
"puppeteer": "^1.20.0",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.26.10",
|
||||
"sass-loader": "^8.0.0",
|
||||
|
@ -38,7 +38,12 @@
|
||||
<li class="list-group-item d-flex list-group-item-action justify-content-between align-items-center"><strong>{{ ctrans('texts.total')}}</strong>
|
||||
<h3><span class="badge badge-primary badge-pill"><strong>{{ $amount }}</strong></span></h3>
|
||||
</li>
|
||||
@if($fee)
|
||||
@if($credit_totals > 0)
|
||||
<li class="list-group-item d-flex list-group-item-action justify-content-between align-items-center"><strong>{{ ctrans('texts.credit_amount')}}</strong>
|
||||
<h3><span class="badge badge-primary badge-pill"><strong>{{ $credit_totals }}</strong></span></h3>
|
||||
</li>
|
||||
@endifs
|
||||
@if($fee > 0)
|
||||
<li class="list-group-item d-flex list-group-item-action justify-content-between align-items-center"><strong>{{ ctrans('texts.gateway_fee')}}</strong>
|
||||
<h3><span class="badge badge-primary badge-pill"><strong>{{ $fee }}</strong></span></h3>
|
||||
</li>
|
||||
|
@ -16,6 +16,10 @@
|
||||
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
|
||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
|
||||
</form>
|
||||
<form action="{{route('client.payments.credit_response')}}" method="post" id="credit-payment">
|
||||
@csrf
|
||||
<input type="hidden" name="payment_hash" value="{{$payment_hash}}">
|
||||
</form>
|
||||
<div class="container mx-auto">
|
||||
<div class="grid grid-cols-6 gap-4">
|
||||
<div class="col-span-6 md:col-start-2 md:col-span-4">
|
||||
@ -39,21 +43,35 @@
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ App\Utils\Number::formatMoney($total['invoice_totals'], $client) }}
|
||||
</dd>
|
||||
@if($total['fee_total'] > 0)
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.gateway_fees') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ App\Utils\Number::formatMoney($total['fee_total'], $client) }}
|
||||
</dd>
|
||||
@endif
|
||||
@if($total['credit_totals'] > 0)
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.total') }}
|
||||
{{ ctrans('texts.credit_amount') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ App\Utils\Number::formatMoney($total['credit_totals'], $client) }}
|
||||
</dd>
|
||||
@endif
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.amount_due') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ App\Utils\Number::formatMoney($total['amount_with_fee'], $client) }}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@if($token)
|
||||
@if((int)$total['amount_with_fee'] == 0)
|
||||
<!-- finalize with credits only -->
|
||||
<div class="bg-white px-4 py-5 flex justify-end">
|
||||
<button form="credit-payment" class="button button-primary bg-primary inline-flex items-center">Pay with credit</button>
|
||||
</div>
|
||||
@elseif($token)
|
||||
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 flex items-center">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500 mr-4">
|
||||
{{ ctrans('texts.credit_card') }}
|
||||
|
@ -69,6 +69,14 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
|
||||
Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk');
|
||||
|
||||
Route::resource('expense_categories', 'ExpenseCategoryController'); // name = (expense_categories. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('expense_categories/bulk', 'ExpenseCategoryController@bulk')->name('expense_categories.bulk');
|
||||
|
||||
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
|
||||
|
||||
Route::resource('projects', 'ProjectController'); // name = (projects. index / create / show / update / destroy / edit
|
||||
Route::post('projects/bulk', 'ProjectController@bulk')->name('projects.bulk');
|
||||
|
||||
@ -125,7 +133,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
Route::resource('group_settings', 'GroupSettingController');
|
||||
Route::post('group_settings/bulk', 'GroupSettingController@bulk');
|
||||
|
||||
Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
Route::resource('tax_rates', 'TaxRateController'); // name = (tax_rates. index / create / show / update / destroy / edit
|
||||
Route::post('tax_rates/bulk', 'TaxRateController@bulk')->name('tax_rates.bulk');
|
||||
|
||||
Route::post('refresh', 'Auth\LoginController@refresh');
|
||||
|
@ -35,6 +35,8 @@ Route::group(['middleware' => ['auth:contact', 'locale'], 'prefix' => 'client',
|
||||
Route::get('recurring_invoices/{recurring_invoice}/request_cancellation', 'ClientPortal\RecurringInvoiceController@requestCancellation')->name('recurring_invoices.request_cancellation');
|
||||
|
||||
Route::post('payments/process', 'ClientPortal\PaymentController@process')->name('payments.process');
|
||||
Route::post('payments/credit_response', 'ClientPortal\PaymentController@credit_response')->name('payments.credit_response');
|
||||
|
||||
Route::get('payments', 'ClientPortal\PaymentController@index')->name('payments.index')->middleware('portal_enabled');
|
||||
Route::get('payments/{payment}', 'ClientPortal\PaymentController@show')->name('payments.show');
|
||||
Route::post('payments/process/response', 'ClientPortal\PaymentController@response')->name('payments.response');
|
||||
|
@ -140,18 +140,18 @@ class DocumentsApiTest extends TestCase
|
||||
|
||||
}
|
||||
|
||||
// public function testTaskDocuments()
|
||||
// {
|
||||
public function testTaskDocuments()
|
||||
{
|
||||
|
||||
// $response = $this->withHeaders([
|
||||
// 'X-API-SECRET' => config('ninja.api_secret'),
|
||||
// 'X-API-TOKEN' => $this->token,
|
||||
// ])->get('/api/v1/tasks');
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/tasks');
|
||||
|
||||
// $response->assertStatus(200);
|
||||
// $arr = $response->json();
|
||||
// $this->assertArrayHasKey('documents', $arr['data'][0]);
|
||||
$response->assertStatus(200);
|
||||
$arr = $response->json();
|
||||
$this->assertArrayHasKey('documents', $arr['data'][0]);
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
151
tests/Feature/ExpenseCategoryApiTest.php
Normal file
151
tests/Feature/ExpenseCategoryApiTest.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Models\Account;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\ExpenseCategoryContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Http\Controllers\ExpenseCategoryController
|
||||
*/
|
||||
class ExpenseCategoryApiTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
Session::start();
|
||||
|
||||
$this->faker = \Faker\Factory::create();
|
||||
|
||||
Model::reguard();
|
||||
}
|
||||
|
||||
public function testExpenseCategoryPost()
|
||||
{
|
||||
$data = [
|
||||
'name' => $this->faker->firstName,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expense_categories', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testExpenseCategoryPut()
|
||||
{
|
||||
$data = [
|
||||
'name' => $this->faker->firstName,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->put('/api/v1/expense_categories/'.$this->encodePrimaryKey($this->expense_category->id), $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testExpenseCategoryGet()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/expense_categories/'.$this->encodePrimaryKey($this->expense_category->id));
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testExpenseCategoryNotArchived()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/expense_categories/'.$this->encodePrimaryKey($this->expense_category->id));
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data']['archived_at']);
|
||||
}
|
||||
|
||||
public function testExpenseCategoryArchived()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->expense_category->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expense_categories/bulk?action=archive', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertNotNull($arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testExpenseCategoryRestored()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->expense_category->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expense_categories/bulk?action=restore', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testExpenseCategoryDeleted()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->expense_category->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expense_categories/bulk?action=delete', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertTrue($arr['data'][0]['is_deleted']);
|
||||
}
|
||||
}
|
151
tests/Feature/TaskApiTest.php
Normal file
151
tests/Feature/TaskApiTest.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Models\Account;
|
||||
use App\Models\Task;
|
||||
use App\Models\TaskContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Http\Controllers\TaskController
|
||||
*/
|
||||
class TaskApiTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
Session::start();
|
||||
|
||||
$this->faker = \Faker\Factory::create();
|
||||
|
||||
Model::reguard();
|
||||
}
|
||||
|
||||
public function testTaskPost()
|
||||
{
|
||||
$data = [
|
||||
'description' => $this->faker->firstName,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/tasks', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testTaskPut()
|
||||
{
|
||||
$data = [
|
||||
'description' => $this->faker->firstName,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->put('/api/v1/tasks/'.$this->encodePrimaryKey($this->task->id), $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testTaskGet()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/tasks/'.$this->encodePrimaryKey($this->task->id));
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testTaskNotArchived()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/tasks/'.$this->encodePrimaryKey($this->task->id));
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data']['archived_at']);
|
||||
}
|
||||
|
||||
public function testTaskArchived()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->task->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/tasks/bulk?action=archive', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertNotNull($arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testTaskRestored()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->task->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/tasks/bulk?action=restore', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testTaskDeleted()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->task->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/tasks/bulk?action=delete', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertTrue($arr['data'][0]['is_deleted']);
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ use App\Models\CompanyGateway;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Expense;
|
||||
use App\Models\ExpenseCategory;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
@ -40,6 +41,7 @@ use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
@ -77,6 +79,12 @@ trait MockAccountData
|
||||
|
||||
public $expense;
|
||||
|
||||
public $task;
|
||||
|
||||
public $expense_category;
|
||||
|
||||
public $cu;
|
||||
|
||||
public function makeTestData()
|
||||
{
|
||||
|
||||
@ -143,10 +151,10 @@ trait MockAccountData
|
||||
|
||||
$this->user->password = Hash::make('ALongAndBriliantPassword');
|
||||
|
||||
$cu = CompanyUserFactory::create($this->user->id, $this->company->id, $this->account->id);
|
||||
$cu->is_owner = true;
|
||||
$cu->is_admin = true;
|
||||
$cu->save();
|
||||
$this->cu = CompanyUserFactory::create($this->user->id, $this->company->id, $this->account->id);
|
||||
$this->cu->is_owner = true;
|
||||
$this->cu->is_admin = true;
|
||||
$this->cu->save();
|
||||
|
||||
$this->token = \Illuminate\Support\Str::random(64);
|
||||
|
||||
@ -160,6 +168,8 @@ trait MockAccountData
|
||||
|
||||
$company_token->save();
|
||||
|
||||
//todo create one token withe token name TOKEN - use firstOrCreate
|
||||
|
||||
Product::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
@ -217,6 +227,16 @@ trait MockAccountData
|
||||
'company_id' => $this->company->id,
|
||||
]);
|
||||
|
||||
$this->task = Task::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
]);
|
||||
|
||||
$this->expense_category = ExpenseCategory::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
]);
|
||||
|
||||
$gs = new GroupSetting;
|
||||
$gs->name = 'Test';
|
||||
$gs->company_id = $this->client->company_id;
|
||||
|
@ -28,6 +28,8 @@ class PdfGenerationTest extends TestCase
|
||||
private function makePdf($header, $footer, $html, $pdf)
|
||||
{
|
||||
Browsershot::html($html)
|
||||
->setNodeBinary(config('ninja.system.node_path'))
|
||||
->setNpmBinary(config('ninja.system.npm_path'))
|
||||
//->showBrowserHeaderAndFooter()
|
||||
//->headerHtml($header)
|
||||
//->footerHtml($footer)
|
||||
|
@ -143,7 +143,7 @@ class FactoryCreationTest extends TestCase
|
||||
public function testUserCreate()
|
||||
{
|
||||
$new_user = UserFactory::create($this->account->id);
|
||||
$new_user->email = $this->faker->email;
|
||||
$new_user->email = $this->faker->freeEmail;
|
||||
$new_user->save();
|
||||
|
||||
$this->assertNotNull($new_user);
|
||||
|
Loading…
Reference in New Issue
Block a user