1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-20 16:31:33 +02:00

Merge pull request #7479 from CirkaN/Cirkovic/IINA-4

Cirkovic/iina 4
This commit is contained in:
David Bomba 2022-05-31 07:36:33 +10:00 committed by GitHub
commit 7874d52e2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1701 additions and 5 deletions

View File

@ -0,0 +1,56 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Factory;
use App\Models\Client;
use App\Models\PurchaseOrder;
use Illuminate\Database\Eloquent\Model;
class PurchaseOrderFactory
{
public static function create(int $company_id, int $user_id, object $settings = null, Client $client = null) :PurchaseOrder
{
$purchase_order = new PurchaseOrder();
$purchase_order->status_id = PurchaseOrder::STATUS_DRAFT;
$purchase_order->number = null;
$purchase_order->discount = 0;
$purchase_order->is_amount_discount = true;
$purchase_order->po_number = '';
$purchase_order->footer = '';
$purchase_order->terms = '';
$purchase_order->public_notes = '';
$purchase_order->private_notes = '';
$purchase_order->date = now()->format('Y-m-d');
$purchase_order->due_date = null;
$purchase_order->partial_due_date = null;
$purchase_order->is_deleted = false;
$purchase_order->line_items = json_encode([]);
$purchase_order->tax_name1 = '';
$purchase_order->tax_rate1 = 0;
$purchase_order->tax_name2 = '';
$purchase_order->tax_rate2 = 0;
$purchase_order->tax_name3 = '';
$purchase_order->tax_rate3 = 0;
$purchase_order->custom_value1 = '';
$purchase_order->custom_value2 = '';
$purchase_order->custom_value3 = '';
$purchase_order->custom_value4 = '';
$purchase_order->amount = 0;
$purchase_order->balance = 0;
$purchase_order->partial = 0;
$purchase_order->user_id = $user_id;
$purchase_order->company_id = $company_id;
$purchase_order->recurring_id = null;
return $purchase_order;
}
}

View File

@ -0,0 +1,185 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Filters;
use App\Models\PurchaseOrder;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
class PurchaseOrderFilters extends QueryFilters
{
/**
* Filter based on client status.
*
* Statuses we need to handle
* - all
* - paid
* - unpaid
* - overdue
* - reversed
*
* @return Builder
*/
public function credit_status(string $value = '') :Builder
{
if (strlen($value) == 0) {
return $this->builder;
}
$status_parameters = explode(',', $value);
if (in_array('all', $status_parameters)) {
return $this->builder;
}
if (in_array('draft', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_DRAFT);
}
if (in_array('partial', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_PARTIAL);
}
if (in_array('applied', $status_parameters)) {
$this->builder->where('status_id', PurchaseOrder::STATUS_APPLIED);
}
//->where('due_date', '>', Carbon::now())
//->orWhere('partial_due_date', '>', Carbon::now());
return $this->builder;
}
/**
* Filter based on search text.
*
* @param string query filter
* @return 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('purchase_orders.number', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.number', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.date', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.amount', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.balance', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.custom_value1', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.custom_value2', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.custom_value3', 'like', '%'.$filter.'%')
->orWhere('purchase_orders.custom_value4', 'like', '%'.$filter.'%');
});
}
/**
* Filters the list based on the status
* archived, active, deleted - legacy from V1.
*
* @param string filter
* @return Builder
*/
public function status(string $filter = '') : Builder
{
if (strlen($filter) == 0) {
return $this->builder;
}
$table = 'purchase_orders';
$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 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
* @param User $user
* @return Builder
* @deprecated
*/
public function baseQuery(int $company_id, User $user) : Builder
{
// ..
}
/**
* Filters the query by the users company ID.
*
* We need to ensure we are using the correct company ID
* as we could be hitting this from either the client or company auth guard
*
*/
public function entityFilter()
{
if (auth()->guard('contact')->user()) {
return $this->contactViewFilter();
} else {
return $this->builder->company();
}
// return $this->builder->whereCompanyId(auth()->user()->company()->id);
}
/**
* We need additional filters when showing purchase orders for the
* client portal. Need to automatically exclude drafts and cancelled purchase orders.
*
* @return Builder
*/
private function contactViewFilter() : Builder
{
return $this->builder
->whereCompanyId(auth()->guard('contact')->user()->company->id)
->whereNotIn('status_id', [PurchaseOrder::STATUS_DRAFT]);
}
}

View File

@ -0,0 +1,413 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Controllers;
use App\Factory\PurchaseOrderFactory;
use App\Filters\PurchaseOrderFilters;
use App\Http\Requests\PurchaseOrder\CreatePurchaseOrderRequest;
use App\Http\Requests\PurchaseOrder\DestroyPurchaseOrderRequest;
use App\Http\Requests\PurchaseOrder\EditPurchaseOrderRequest;
use App\Http\Requests\PurchaseOrder\ShowPurchaseOrderRequest;
use App\Http\Requests\PurchaseOrder\StorePurchaseOrderRequest;
use App\Http\Requests\PurchaseOrder\UpdatePurchaseOrderRequest;
use App\Models\Client;
use App\Models\PurchaseOrder;
use App\Repositories\PurchaseOrderRepository;
use App\Transformers\PurchaseOrderTransformer;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Response;
class PurchaseOrderController extends BaseController
{
use MakesHash;
protected $entity_type = PurchaseOrder::class;
protected $entity_transformer = PurchaseOrderTransformer::class;
protected $purchase_order_repository;
public function __construct(PurchaseOrderRepository $purchase_order_repository)
{
parent::__construct();
$this->purchase_order_repository = $purchase_order_repository;
}
/**
* Show the list of Purchase Orders.
*
* @param \App\Filters\PurchaseOrderFilters $filters The filters
*
* @return Response
*
* @OA\Get(
* path="/api/v1/purchase_orders",
* operationId="getPurchaseOrders",
* tags={"purchase_orders"},
* summary="Gets a list of purchase orders",
* description="Lists purchase orders, search and filters allow fine grained lists to be generated.
*
* Query parameters can be added to performed more fine grained filtering of the purchase orders, these are handled by the PurchaseOrderFilters 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\Response(
* response=200,
* description="A list of purchase orders",
* @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/Credit"),
* ),
* @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(PurchaseOrderFilters $filters)
{
$purchase_orders = PurchaseOrder::filter($filters);
return $this->listResponse($purchase_orders);
}
/**
* Show the form for creating a new resource.
*
* @param CreatePurchaseOrderRequest $request The request
*
* @return Response
*
*
* @OA\Get(
* path="/api/v1/purchase_orders/create",
* operationId="getPurchaseOrderCreate",
* tags={"purchase_orders"},
* summary="Gets a new blank purchase order 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 purchase order 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/Credit"),
* ),
* @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(CreatePurchaseOrderRequest $request)
{
$purchase_order = PurchaseOrderFactory::create(auth()->user()->company()->id, auth()->user()->id);
return $this->itemResponse($purchase_order);
}
/**
* Store a newly created resource in storage.
*
* @param StorePurchaseOrderRequest $request The request
*
* @return Response
*
*
* @OA\Post(
* path="/api/v1/purchase_orders",
* operationId="storePurchaseOrder",
* tags={"purhcase_orders"},
* summary="Adds a purchase order",
* description="Adds an purchase order to the system",
* @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 purchase order 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/Credit"),
* ),
* @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(StorePurchaseOrderRequest $request)
{
$client = Client::find($request->get('client_id'));
$purchase_order = $this->purchase_order_repository->save($request->all(), PurchaseOrderFactory::create(auth()->user()->company()->id, auth()->user()->id));
$purchase_order = $purchase_order->service()
->fillDefaults()
->save();
return $this->itemResponse($purchase_order);
}
/**
* Display the specified resource.
*
* @param ShowPurchaseOrderRequest $request The request
* @param PurchaseOrder $purchase_order The purchase order
*
* @return Response
*
*
* @OA\Get(
* path="/api/v1/purchase_orders/{id}",
* operationId="showPurchaseOrder",
* tags={"purchase_orders"},
* summary="Shows an purcase orders",
* description="Displays an purchase order 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 Purchase order Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the purchase order 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/Credit"),
* ),
* @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(ShowPurchaseOrderRequest $request, PurchaseOrder $purchase_order)
{
return $this->itemResponse($purchase_order);
}
/**
* Show the form for editing the specified resource.
*
* @param EditPurchaseOrderRequest $request The request
* @param PurchaseOrder $purchase_order The purchase order
*
* @return Response
*
* @OA\Get(
* path="/api/v1/purchase_orders/{id}/edit",
* operationId="editPurchaseOrder",
* tags={"purchase_orders"},
* summary="Shows an purchase order for editting",
* description="Displays an purchase order 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 purchase order Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the purchase order 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/Invoice"),
* ),
* @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(EditPurchaseOrderRequest $request, PurchaseOrder $purchase_order)
{
return $this->itemResponse($purchase_order);
}
/**
* Update the specified resource in storage.
*
* @param UpdatePurchaseOrderRequest $request The request
* @param PurchaseOrder $purchase_order
* @return Response
*
*
* @throws \ReflectionException
* @OA\Put(
* path="/api/v1/purchase_order/{id}",
* operationId="updatePurchaseOrder",
* tags={"purchase_orders"},
* summary="Updates an purchase order",
* description="Handles the updating of an purchase order 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 purchase order Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the purchase order 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/Credit"),
* ),
* @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(UpdatePurchaseOrderRequest $request, PurchaseOrder $purchase_order)
{
if ($request->entityIsDeleted($purchase_order)) {
return $request->disallowUpdate();
}
$purchase_order = $this->purchase_order_repository->save($request->all(), $purchase_order);
return $this->itemResponse($purchase_order);
}
/**
* Remove the specified resource from storage.
*
* @param DestroyPurchaseOrderRequest $request
* @param PurchaseOrder $purchase_order
*
* @return Response
*
* @throws \Exception
* @OA\Delete(
* path="/api/v1/purchase_orders/{id}",
* operationId="deletePurchaseOrder",
* tags={"purchase_orders"},
* summary="Deletes a purchase order",
* description="Handles the deletion of an purchase orders 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 purhcase order 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(DestroyPurchaseOrderRequest $request, PurchaseOrder $purchase_order)
{
$this->purchase_order_repository->delete($purchase_order);
return $this->itemResponse($purchase_order->fresh());
}
}

View File

@ -0,0 +1,41 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\PurchaseOrder;
use App\Http\Requests\Request;
use App\Models\PurchaseOrder;
class CreatePurchaseOrderRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('create', PurchaseOrder::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\PurchaseOrder;
use App\Http\Requests\Request;
class DestroyPurchaseOrderRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return auth()->user()->can('edit', $this->purchase_order);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\PurchaseOrder;
use App\Http\Requests\Request;
class EditPurchaseOrderRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('edit', $this->purchase_order);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\PurchaseOrder;
use App\Http\Requests\Request;
class ShowPurchaseOrderRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('view', $this->purchase_order);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\PurchaseOrder;
use App\Http\Requests\Request;
use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class StorePurchaseOrderRequest extends Request
{
use MakesHash;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->user()->can('create', PurchaseOrder::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [];
$rules['client_id'] = 'required';
$rules['number'] = ['nullable', Rule::unique('purchase_orders')->where('company_id', auth()->user()->company()->id)];
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
$rules['line_items'] = 'array';
return $rules;
}
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->decodePrimaryKeys($input);
$this->replace($input);
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Http\Requests\PurchaseOrder;
use App\Http\Requests\Request;
use App\Utils\Traits\ChecksEntityStatus;
use App\Utils\Traits\MakesHash;
use Illuminate\Validation\Rule;
class UpdatePurchaseOrderRequest extends Request
{
use ChecksEntityStatus;
use MakesHash;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->purchase_order);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [];
if($this->number)
$rules['number'] = Rule::unique('purchase_orders')->where('company_id', auth()->user()->company()->id)->ignore($this->purchase_order->id);
$rules['line_items'] = 'array';
$rules['discount'] = 'sometimes|numeric';
$rules['is_amount_discount'] = ['boolean'];
return $rules;
}
protected function prepareForValidation()
{
$input = $this->all();
$input = $this->decodePrimaryKeys($input);
$input['id'] = $this->purchase_order->id;
$this->replace($input);
}
}

View File

@ -0,0 +1,169 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Models;
use App\Services\PurchaseOrder\PurchaseOrderService;
use Illuminate\Database\Eloquent\SoftDeletes;
class PurchaseOrder extends BaseModel
{
use Filterable;
use SoftDeletes;
protected $fillable = [
'number',
'discount',
'company_id',
'status_id',
'user_id',
'last_sent_date',
'is_deleted',
'po_number',
'date',
'due_date',
'terms',
'public_notes',
'private_notes',
'tax_name1',
'tax_rate1',
'tax_name2',
'tax_rate2',
'tax_name3',
'tax_rate3',
'total_taxes',
'uses_inclusive_taxes',
'is_amount_discount',
'partial',
'recurring_id',
'next_send_date',
'reminder1_sent',
'reminder2_sent',
'reminder3_sent',
'reminder_last_sent',
'partial_due_date',
'project_id',
'custom_value1',
'custom_value2',
'custom_value3',
'custom_value4',
'backup',
'footer',
'line_items',
'client_id',
'custom_surcharge1',
'custom_surcharge2',
'custom_surcharge3',
'custom_surcharge4',
// 'custom_surcharge_tax1',
// 'custom_surcharge_tax2',
// 'custom_surcharge_tax3',
// 'custom_surcharge_tax4',
'design_id',
'invoice_id',
'assigned_user_id',
'exchange_rate',
'balance',
'partial',
'paid_to_date',
'subscription_id',
'vendor_id',
'last_viewed'
];
protected $casts = [
'line_items' => 'object',
'backup' => 'object',
'updated_at' => 'timestamp',
'created_at' => 'timestamp',
'deleted_at' => 'timestamp',
'is_amount_discount' => 'bool',
];
const STATUS_DRAFT = 1;
const STATUS_SENT = 2;
const STATUS_PARTIAL = 3;
const STATUS_APPLIED = 4;
public function assigned_user()
{
return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed();
}
public function vendor()
{
return $this->belongsTo(Vendor::class);
}
public function history()
{
return $this->hasManyThrough(Backup::class, Activity::class);
}
public function activities()
{
return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(50);
}
public function company()
{
return $this->belongsTo(Company::class);
}
public function user()
{
return $this->belongsTo(User::class)->withTrashed();
}
public function client()
{
return $this->belongsTo(Client::class)->withTrashed();
}
public function invitations()
{
return $this->hasMany(CreditInvitation::class);
}
public function project()
{
return $this->belongsTo(Project::class)->withTrashed();
}
public function invoice()
{
return $this->belongsTo(Invoice::class);
}
public function service()
{
return new PurchaseOrderService($this);
}
public function invoices()
{
return $this->belongsToMany(Invoice::class)->using(Paymentable::class);
}
public function payments()
{
return $this->morphToMany(Payment::class, 'paymentable');
}
public function documents()
{
return $this->morphMany(Document::class, 'documentable');
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Observers;
use App\Models\PurchaseOrder;
class PurchaseOrderObserver
{
/**
* Handle the client "created" event.
*
* @param PurchaseOrder $purchase_order
* @return void
*/
public function created(PurchaseOrder $purchase_order)
{
}
/**
* Handle the client "updated" event.
*
* @param PurchaseOrder $purchase_order
* @return void
*/
public function updated(PurchaseOrder $purchase_order)
{
}
/**
* Handle the client "deleted" event.
*
* @param PurchaseOrder $purchase_order
* @return void
*/
public function deleted(PurchaseOrder $purchase_order)
{
}
/**
* Handle the client "restored" event.
*
* @param PurchaseOrder $purchase_order
* @return void
*/
public function restored(PurchaseOrder $purchase_order)
{
//
}
/**
* Handle the client "force deleted" event.
*
* @param PurchaseOrder $purchase_order
* @return void
*/
public function forceDeleted(PurchaseOrder $purchase_order)
{
//
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class PurchaseOrderPolicy extends EntityPolicy
{
use HandlesAuthorization;
public function create(User $user) : bool
{
return $user->isAdmin() || $user->hasPermission('create_purchase_order') || $user->hasPermission('create_all');
}
}

View File

@ -27,6 +27,7 @@ use App\Models\Payment;
use App\Models\PaymentTerm;
use App\Models\Product;
use App\Models\Project;
use App\Models\PurchaseOrder;
use App\Models\Quote;
use App\Models\RecurringExpense;
use App\Models\RecurringInvoice;
@ -54,6 +55,7 @@ use App\Policies\PaymentPolicy;
use App\Policies\PaymentTermPolicy;
use App\Policies\ProductPolicy;
use App\Policies\ProjectPolicy;
use App\Policies\PurchaseOrderPolicy;
use App\Policies\QuotePolicy;
use App\Policies\RecurringExpensePolicy;
use App\Policies\RecurringInvoicePolicy;
@ -103,6 +105,7 @@ class AuthServiceProvider extends ServiceProvider
TaxRate::class => TaxRatePolicy::class,
User::class => UserPolicy::class,
Vendor::class => VendorPolicy::class,
PurchaseOrder::class => PurchaseOrderPolicy::class,
];
/**

View File

@ -215,6 +215,7 @@ use App\Models\Payment;
use App\Models\Product;
use App\Models\Project;
use App\Models\Proposal;
use App\Models\PurchaseOrder;
use App\Models\Quote;
use App\Models\Subscription;
use App\Models\Task;
@ -231,6 +232,7 @@ use App\Observers\PaymentObserver;
use App\Observers\ProductObserver;
use App\Observers\ProjectObserver;
use App\Observers\ProposalObserver;
use App\Observers\PurchaseOrderObserver;
use App\Observers\QuoteObserver;
use App\Observers\SubscriptionObserver;
use App\Observers\TaskObserver;
@ -593,5 +595,6 @@ class EventServiceProvider extends ServiceProvider
Quote::observe(QuoteObserver::class);
Task::observe(TaskObserver::class);
User::observe(UserObserver::class);
PurchaseOrder::observe(PurchaseOrderObserver::class);
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Repositories;
use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
class PurchaseOrderRepository extends BaseRepository
{
use MakesHash;
public function __construct()
{
}
public function save(array $data, PurchaseOrder $purchase_order) : ?PurchaseOrder
{
$purchase_order->fill($data);
$purchase_order->save();
return $purchase_order;
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\PurchaseOrder;
use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
class PurchaseOrderService
{
use MakesHash;
public PurchaseOrder $purchase_order;
public function __construct($purchase_order)
{
$this->purchase_order = $purchase_order;
}
/**
* Saves the purchase order.
* @return \App\Models\PurchaseOrder object
*/
public function save(): ?PurchaseOrder
{
$this->purchase_order->saveQuietly();
return $this->purchase_order;
}
public function fillDefaults()
{
$settings = $this->purchase_order->client->getMergedSettings();
//TODO implement design, footer, terms
/* If client currency differs from the company default currency, then insert the client exchange rate on the model.*/
if (!isset($this->purchase_order->exchange_rate) && $this->purchase_order->client->currency()->id != (int)$this->purchase_order->company->settings->currency_id)
$this->purchase_order->exchange_rate = $this->purchase_order->client->currency()->exchange_rate;
if (!isset($this->purchase_order->public_notes))
$this->purchase_order->public_notes = $this->purchase_order->client->public_notes;
return $this;
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Transformers;
use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
class PurchaseOrderTransformer extends EntityTransformer
{
use MakesHash;
public function transform(PurchaseOrder $purchase_order)
{
return [
'id' => $this->encodePrimaryKey($purchase_order->id),
'user_id' => $this->encodePrimaryKey($purchase_order->user_id),
'project_id' => $this->encodePrimaryKey($purchase_order->project_id),
'assigned_user_id' => $this->encodePrimaryKey($purchase_order->assigned_user_id),
'vendor_id' => (string) $this->encodePrimaryKey($purchase_order->vendor_id),
'amount' => (float) $purchase_order->amount,
'balance' => (float) $purchase_order->balance,
'client_id' => (string) $this->encodePrimaryKey($purchase_order->client_id),
'vendor_id' => (string) $this->encodePrimaryKey($purchase_order->vendor_id),
'status_id' => (string) ($purchase_order->status_id ?: 1),
'design_id' => (string) $this->encodePrimaryKey($purchase_order->design_id),
'created_at' => (int) $purchase_order->created_at,
'updated_at' => (int) $purchase_order->updated_at,
'archived_at' => (int) $purchase_order->deleted_at,
'is_deleted' => (bool) $purchase_order->is_deleted,
'number' => $purchase_order->number ?: '',
'discount' => (float) $purchase_order->discount,
'po_number' => $purchase_order->po_number ?: '',
'date' => $purchase_order->date ?: '',
'last_sent_date' => $purchase_order->last_sent_date ?: '',
'next_send_date' => $purchase_order->next_send_date ?: '',
'reminder1_sent' => $purchase_order->reminder1_sent ?: '',
'reminder2_sent' => $purchase_order->reminder2_sent ?: '',
'reminder3_sent' => $purchase_order->reminder3_sent ?: '',
'reminder_last_sent' => $purchase_order->reminder_last_sent ?: '',
'due_date' => $purchase_order->due_date ?: '',
'terms' => $purchase_order->terms ?: '',
'public_notes' => $purchase_order->public_notes ?: '',
'private_notes' => $purchase_order->private_notes ?: '',
'uses_inclusive_taxes' => (bool) $purchase_order->uses_inclusive_taxes,
'tax_name1' => $purchase_order->tax_name1 ? $purchase_order->tax_name1 : '',
'tax_rate1' => (float) $purchase_order->tax_rate1,
'tax_name2' => $purchase_order->tax_name2 ? $purchase_order->tax_name2 : '',
'tax_rate2' => (float) $purchase_order->tax_rate2,
'tax_name3' => $purchase_order->tax_name3 ? $purchase_order->tax_name3 : '',
'tax_rate3' => (float) $purchase_order->tax_rate3,
'total_taxes' => (float) $purchase_order->total_taxes,
'is_amount_discount' => (bool) ($purchase_order->is_amount_discount ?: false),
'footer' => $purchase_order->footer ?: '',
'partial' => (float) ($purchase_order->partial ?: 0.0),
'partial_due_date' => $purchase_order->partial_due_date ?: '',
'custom_value1' => (string) $purchase_order->custom_value1 ?: '',
'custom_value2' => (string) $purchase_order->custom_value2 ?: '',
'custom_value3' => (string) $purchase_order->custom_value3 ?: '',
'custom_value4' => (string) $purchase_order->custom_value4 ?: '',
'has_tasks' => (bool) $purchase_order->has_tasks,
'has_expenses' => (bool) $purchase_order->has_expenses,
'custom_surcharge1' => (float) $purchase_order->custom_surcharge1,
'custom_surcharge2' => (float) $purchase_order->custom_surcharge2,
'custom_surcharge3' => (float) $purchase_order->custom_surcharge3,
'custom_surcharge4' => (float) $purchase_order->custom_surcharge4,
'custom_surcharge_tax1' => (bool) $purchase_order->custom_surcharge_tax1,
'custom_surcharge_tax2' => (bool) $purchase_order->custom_surcharge_tax2,
'custom_surcharge_tax3' => (bool) $purchase_order->custom_surcharge_tax3,
'custom_surcharge_tax4' => (bool) $purchase_order->custom_surcharge_tax4,
'line_items' => $purchase_order->line_items ?: (array) [],
'entity_type' => 'credit',
'exchange_rate' => (float) $purchase_order->exchange_rate,
'paid_to_date' => (float) $purchase_order->paid_to_date,
'subscription_id' => $this->encodePrimaryKey($purchase_order->subscription_id),
];
}
}

View File

@ -0,0 +1,127 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePurchaseOrdersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('purchase_orders', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('client_id')->index();
$table->unsignedInteger('user_id');
$table->unsignedInteger('assigned_user_id')->nullable();
$table->unsignedInteger('company_id')->index();
$table->unsignedInteger('status_id');
$table->unsignedInteger('project_id')->nullable();
$table->unsignedInteger('vendor_id')->nullable();
$table->unsignedInteger('recurring_id')->nullable();
$table->unsignedInteger('design_id')->nullable();
$table->unsignedInteger('invoice_id')->nullable();
$table->string('number')->nullable();
$table->float('discount')->default(0);
$table->boolean('is_amount_discount')->default(0);
$table->string('po_number')->nullable();
$table->date('date')->nullable();
$table->datetime('last_sent_date')->nullable();
$table->date('due_date')->nullable();
$table->boolean('is_deleted')->default(false);
$table->mediumText('line_items')->nullable();
$table->mediumText('backup')->nullable();
$table->text('footer')->nullable();
$table->text('public_notes')->nullable();
$table->text('private_notes')->nullable();
$table->text('terms')->nullable();
$table->string('tax_name1')->nullable();
$table->decimal('tax_rate1', 20, 6)->default(0);
$table->string('tax_name2')->nullable();
$table->decimal('tax_rate2', 20, 6)->default(0);
$table->string('tax_name3')->nullable();
$table->decimal('tax_rate3', 20, 6)->default(0);
$table->decimal('total_taxes', 20, 6)->default(0);
$table->boolean('uses_inclusive_taxes')->default(0);
$table->date('reminder1_sent')->nullable();
$table->date('reminder2_sent')->nullable();
$table->date('reminder3_sent')->nullable();
$table->date('reminder_last_sent')->nullable();
$table->text('custom_value1')->nullable();
$table->text('custom_value2')->nullable();
$table->text('custom_value3')->nullable();
$table->text('custom_value4')->nullable();
$table->datetime('next_send_date')->nullable();
$table->decimal('custom_surcharge1', 20,6)->nullable();
$table->decimal('custom_surcharge2', 20,6)->nullable();
$table->decimal('custom_surcharge3', 20,6)->nullable();
$table->decimal('custom_surcharge4', 20,6)->nullable();
$table->boolean('custom_surcharge_tax1')->default(false);
$table->boolean('custom_surcharge_tax2')->default(false);
$table->boolean('custom_surcharge_tax3')->default(false);
$table->boolean('custom_surcharge_tax4')->default(false);
$table->decimal('exchange_rate', 20, 6)->default(1);
$table->decimal('balance', 20, 6);
$table->decimal('partial', 20, 6)->nullable();
$table->decimal('amount', 20, 6);
$table->decimal('paid_to_date', 20, 6)->default(0);
$table->datetime('partial_due_date')->nullable();
$table->datetime('last_viewed')->nullable();
$table->foreign('client_id')->references('id')->on('clients')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->index(['company_id', 'deleted_at']);
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('purchase_orders');
}
}

View File

@ -206,6 +206,8 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
Route::put('vendors/{vendor}/upload', 'VendorController@upload');
Route::resource('purchase_orders', 'PurchaseOrderController');
Route::get('users', 'UserController@index');
Route::get('users/{user}', 'UserController@show')->middleware('password_protected');
Route::put('users/{user}', 'UserController@update')->middleware('password_protected');

View File

@ -0,0 +1,142 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace Tests;
use App\Models\Client;
use App\Models\PurchaseOrder;
use App\Utils\Traits\MakesHash;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Session;
class PurchaseOrderTest extends TestCase
{
use MakesHash;
use DatabaseTransactions;
use MockAccountData;
public function setUp(): void
{
parent::setUp();
Session::start();
$this->faker = \Faker\Factory::create();
Model::reguard();
$this->makeTestData();
}
public function testPurchaseOrderRest()
{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id));
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->get('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id).'/edit');
$response->assertStatus(200);
$credit_update = [
'tax_name1' => 'dippy',
];
$this->assertNotNull($this->purchase_order);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id), $credit_update)
->assertStatus(200);
}
public function testPostNewPurchaseOrder()
{
$purchase_order = [
'status_id' => 1,
'number' => 'dfdfd',
'discount' => 0,
'is_amount_discount' => 1,
'number' => '34343xx43',
'public_notes' => 'notes',
'is_deleted' => 0,
'custom_value1' => 0,
'custom_value2' => 0,
'custom_value3' => 0,
'custom_value4' => 0,
'status' => 1,
'client_id' => $this->encodePrimaryKey($this->client->id),
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/purchase_orders/', $purchase_order)
->assertStatus(200);
}
public function testPurchaseOrderDelete()
{
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->delete('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id));
$response->assertStatus(200);
}
public function testPurchaseOrderUpdate()
{
$data = [
'status_id' => 1,
'number' => 'dfdfd',
'discount' => 0,
'is_amount_discount' => 1,
'number' => '3434343',
'public_notes' => 'notes',
'is_deleted' => 0,
'custom_value1' => 0,
'custom_value2' => 0,
'custom_value3' => 0,
'custom_value4' => 0,
'status' => 1,
'client_id' => $this->encodePrimaryKey($this->client->id),
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->put('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id), $data);
$response->assertStatus(200);
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->post('/api/v1/purchase_orders/', $data);
$response->assertStatus(302);
}
}

View File

@ -20,6 +20,7 @@ use App\Factory\InvoiceFactory;
use App\Factory\InvoiceInvitationFactory;
use App\Factory\InvoiceItemFactory;
use App\Factory\InvoiceToRecurringInvoiceFactory;
use App\Factory\PurchaseOrderFactory;
use App\Helpers\Invoice\InvoiceSum;
use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Models\Account;
@ -181,10 +182,10 @@ trait MockAccountData
'hosted_client_count' => 1000,
'hosted_company_count' => 1000
]);
$this->account->num_users = 3;
$this->account->save();
$this->company = Company::factory()->create([
'account_id' => $this->account->id,
]);
@ -212,7 +213,7 @@ trait MockAccountData
$settings->use_credits_payment = 'always';
$settings->timezone_id = '1';
$settings->entity_send_time = 0;
$this->company->settings = $settings;
$this->company->save();
@ -387,7 +388,7 @@ trait MockAccountData
$this->invoice->setRelation('company', $this->company);
$this->invoice->save();
$this->invoice->load("client");
InvoiceInvitation::factory()->create([
@ -447,13 +448,43 @@ trait MockAccountData
$this->quote->save();
$this->purchase_order = PurchaseOrderFactory::create($this->company->id, $user_id);
$this->purchase_order->client_id = $this->client->id;
$this->purchase_order->amount = 10;
$this->purchase_order->balance = 10;
// $this->credit->due_date = now()->addDays(200);
$this->purchase_order->tax_name1 = '';
$this->purchase_order->tax_name2 = '';
$this->purchase_order->tax_name3 = '';
$this->purchase_order->tax_rate1 = 0;
$this->purchase_order->tax_rate2 = 0;
$this->purchase_order->tax_rate3 = 0;
$this->purchase_order->uses_inclusive_taxes = false;
$this->purchase_order->save();
$this->credit = CreditFactory::create($this->company->id, $user_id);
$this->credit->client_id = $this->client->id;
$this->credit->line_items = $this->buildLineItems();
$this->credit->amount = 10;
$this->credit->balance = 10;
// $this->credit->due_date = now()->addDays(200);
$this->credit->tax_name1 = '';