mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Uploads for purchase orders
This commit is contained in:
parent
ea0ef763bf
commit
3e5e915acc
@ -291,7 +291,10 @@ class CompanySettings extends BaseSettings
|
|||||||
public $email_from_name = '';
|
public $email_from_name = '';
|
||||||
public $auto_archive_invoice_cancelled = false;
|
public $auto_archive_invoice_cancelled = false;
|
||||||
|
|
||||||
|
public $vendor_portal_enable_uploads=false;
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
|
'vendor_portal_enable_uploads' => 'bool',
|
||||||
'besr_id' => 'string',
|
'besr_id' => 'string',
|
||||||
'qr_iban' => 'string',
|
'qr_iban' => 'string',
|
||||||
'email_subject_purchase_order' => 'string',
|
'email_subject_purchase_order' => 'string',
|
||||||
|
@ -23,21 +23,25 @@ use App\Http\Requests\PurchaseOrder\EditPurchaseOrderRequest;
|
|||||||
use App\Http\Requests\PurchaseOrder\ShowPurchaseOrderRequest;
|
use App\Http\Requests\PurchaseOrder\ShowPurchaseOrderRequest;
|
||||||
use App\Http\Requests\PurchaseOrder\StorePurchaseOrderRequest;
|
use App\Http\Requests\PurchaseOrder\StorePurchaseOrderRequest;
|
||||||
use App\Http\Requests\PurchaseOrder\UpdatePurchaseOrderRequest;
|
use App\Http\Requests\PurchaseOrder\UpdatePurchaseOrderRequest;
|
||||||
|
use App\Http\Requests\PurchaseOrder\UploadPurchaseOrderRequest;
|
||||||
use App\Jobs\Invoice\ZipInvoices;
|
use App\Jobs\Invoice\ZipInvoices;
|
||||||
use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
|
use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
|
||||||
use App\Jobs\PurchaseOrder\ZipPurchaseOrders;
|
use App\Jobs\PurchaseOrder\ZipPurchaseOrders;
|
||||||
|
use App\Models\Account;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\PurchaseOrder;
|
use App\Models\PurchaseOrder;
|
||||||
use App\Repositories\PurchaseOrderRepository;
|
use App\Repositories\PurchaseOrderRepository;
|
||||||
use App\Transformers\PurchaseOrderTransformer;
|
use App\Transformers\PurchaseOrderTransformer;
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Utils\Traits\SavesDocuments;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class PurchaseOrderController extends BaseController
|
class PurchaseOrderController extends BaseController
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
use SavesDocuments;
|
||||||
|
|
||||||
protected $entity_type = PurchaseOrder::class;
|
protected $entity_type = PurchaseOrder::class;
|
||||||
protected $entity_transformer = PurchaseOrderTransformer::class;
|
protected $entity_transformer = PurchaseOrderTransformer::class;
|
||||||
@ -655,4 +659,69 @@ class PurchaseOrderController extends BaseController
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*
|
||||||
|
* @param UploadPurchaseOrderRequest $request
|
||||||
|
* @param PurchaseOrder $purchase_order
|
||||||
|
* @return Response
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @OA\Put(
|
||||||
|
* path="/api/v1/purchase_orders/{id}/upload",
|
||||||
|
* operationId="uploadPurchaseOrder",
|
||||||
|
* tags={"purchase_orders"},
|
||||||
|
* summary="Uploads a document to a purchase_orders",
|
||||||
|
* description="Handles the uploading of a document to a purchase_order",
|
||||||
|
* @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/Vendor"),
|
||||||
|
* ),
|
||||||
|
* @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 upload(UploadPurchaseOrderRequest $request, PurchaseOrder $purchase_order)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(!$this->checkFeature(Account::FEATURE_DOCUMENTS))
|
||||||
|
return $this->featureFailure();
|
||||||
|
|
||||||
|
if ($request->has('documents'))
|
||||||
|
$this->saveDocuments($request->file('documents'), $purchase_order);
|
||||||
|
|
||||||
|
return $this->itemResponse($purchase_order->fresh());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
41
app/Http/Controllers/VendorPortal/UploadController.php
Normal file
41
app/Http/Controllers/VendorPortal/UploadController.php
Normal 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\Controllers\VendorPortal;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\VendorPortal\Uploads\StoreUploadRequest;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use App\Utils\Traits\SavesDocuments;
|
||||||
|
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
|
|
||||||
|
class UploadController extends Controller
|
||||||
|
{
|
||||||
|
use SavesDocuments;
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main logic behind uploading the files.
|
||||||
|
*
|
||||||
|
* @param StoreUploadRequest $request
|
||||||
|
* @return Response|ResponseFactory
|
||||||
|
*/
|
||||||
|
public function upload(StoreUploadRequest $request, PurchaseOrder $purchase_order)
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->saveDocuments($request->getFile(), $purchase_order, true);
|
||||||
|
|
||||||
|
return response([], 200);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,14 @@
|
|||||||
<?php
|
<?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\ClientPortal\Uploads;
|
namespace App\Http\Requests\ClientPortal\Uploads;
|
||||||
|
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Quote Ninja (https://paymentninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/paymentninja/paymentninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Quote Ninja LLC (https://paymentninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Http\Requests\PurchaseOrder;
|
||||||
|
|
||||||
|
use App\Http\Requests\Request;
|
||||||
|
|
||||||
|
class UploadPurchaseOrderRequest 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
|
||||||
|
$rules = [];
|
||||||
|
|
||||||
|
if($this->input('documents'))
|
||||||
|
$rules['documents'] = 'file|mimes:csv,png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
|
||||||
|
|
||||||
|
return $rules;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
<?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\VendorPortal\Uploads;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class StoreUploadRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return (bool) auth()->guard('vendor')->user()->vendor->company->getSetting('vendor_portal_enable_uploads');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'file' => ['file', 'mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since saveDocuments() expects an array of uploaded files,
|
||||||
|
* we need to convert it to an array before uploading.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getFile()
|
||||||
|
{
|
||||||
|
if (gettype($this->file) !== 'array') {
|
||||||
|
return [$this->file];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->file;
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,7 @@ namespace App\Transformers;
|
|||||||
|
|
||||||
use App\Models\PurchaseOrder;
|
use App\Models\PurchaseOrder;
|
||||||
use App\Models\PurchaseOrderInvitation;
|
use App\Models\PurchaseOrderInvitation;
|
||||||
|
use App\Transformers\DocumentTransformer;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
class PurchaseOrderTransformer extends EntityTransformer
|
class PurchaseOrderTransformer extends EntityTransformer
|
||||||
@ -22,6 +23,7 @@ class PurchaseOrderTransformer extends EntityTransformer
|
|||||||
|
|
||||||
protected $defaultIncludes = [
|
protected $defaultIncludes = [
|
||||||
'invitations',
|
'invitations',
|
||||||
|
'documents'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function includeInvitations(PurchaseOrder $purchase_order)
|
public function includeInvitations(PurchaseOrder $purchase_order)
|
||||||
@ -31,6 +33,14 @@ class PurchaseOrderTransformer extends EntityTransformer
|
|||||||
return $this->includeCollection($purchase_order->invitations, $transformer, PurchaseOrderInvitation::class);
|
return $this->includeCollection($purchase_order->invitations, $transformer, PurchaseOrderInvitation::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function includeDocuments(PurchaseOrder $purchase_order)
|
||||||
|
{
|
||||||
|
$transformer = new DocumentTransformer($this->serializer);
|
||||||
|
|
||||||
|
return $this->includeCollection($purchase_order->documents, $transformer, Document::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function transform(PurchaseOrder $purchase_order)
|
public function transform(PurchaseOrder $purchase_order)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<link rel="stylesheet" href="{{ asset('vendor/dropzone-5.7.0/dist/min/basic.min.css') }}">
|
||||||
|
<script src="{{ asset('vendor/dropzone-5.7.0/dist/min/dropzone.min.js') }}"></script>
|
||||||
|
|
||||||
|
{{-- TODO: HOSTED --}}
|
||||||
|
<div class="bg-white rounded shadow p-4 mb-10">
|
||||||
|
<span class="text-sm mb-4 block text-gray-500 break-words">{{ ctrans('texts.allowed_file_types' )}} png, ai, svg, jpeg, tiff, pdf, gif, psd, txt, doc, xls, ppt, xlsx, docx, pptx</span>
|
||||||
|
<form action="{{ route('vendor.upload.store',['purchase_order' => $purchase_order->hashed_id]) }}" class="dropzone p-8 border-4 border-dashed border-gray-200 rounded-md" method="post" enctype="multipart/form-data">
|
||||||
|
@csrf
|
||||||
|
<div class="fallback">
|
||||||
|
<input name="file[]" type="file" multiple/>
|
||||||
|
<input name="purchase_order" type="hidden" value="{{$purchase_order->hashed_id}}">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
Dropzone.prototype.defaultOptions.dictDefaultMessage = '{!! ctrans('texts.dropzone_default_message') !!}';
|
||||||
|
|
||||||
|
</script>
|
@ -12,11 +12,16 @@
|
|||||||
|
|
||||||
@section('body')
|
@section('body')
|
||||||
|
|
||||||
|
@if($purchase_order->company->getSetting('vendor_portal_enable_uploads') || true)
|
||||||
|
@component('portal.ninja2020.purchase_orders.includes.upload', ['purchase_order' => $purchase_order]) @endcomponent
|
||||||
|
@endif
|
||||||
|
|
||||||
@if(in_array($purchase_order->status_id, [\App\Models\PurchaseOrder::STATUS_SENT, \App\Models\PurchaseOrder::STATUS_DRAFT]))
|
@if(in_array($purchase_order->status_id, [\App\Models\PurchaseOrder::STATUS_SENT, \App\Models\PurchaseOrder::STATUS_DRAFT]))
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
@include('portal.ninja2020.purchase_orders.includes.actions', ['purchase_order' => $purchase_order])
|
@include('portal.ninja2020.purchase_orders.includes.actions', ['purchase_order' => $purchase_order])
|
||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
|
<input type="hidden" id="approve-button">
|
||||||
<div class="bg-white shadow sm:rounded-lg mb-4">
|
<div class="bg-white shadow sm:rounded-lg mb-4">
|
||||||
<div class="px-4 py-5 sm:p-6">
|
<div class="px-4 py-5 sm:p-6">
|
||||||
<div class="sm:flex sm:items-start sm:justify-between">
|
<div class="sm:flex sm:items-start sm:justify-between">
|
||||||
|
@ -83,7 +83,6 @@
|
|||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -163,6 +162,15 @@
|
|||||||
</div>
|
</div>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-span-6 sm:col-span-6">
|
||||||
|
<label for="public_notes" class="input-label w-full">{{ ctrans('texts.notes') }}</label>
|
||||||
|
<textarea rows="4" id="public_notes" class="block p-2.5 w-full text-sm text-gray-900 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" name="public_notes" value="{{ $vendor->public_notes }}" />{{ $vendor->public_notes}}</textarea>
|
||||||
|
@error('public_notes')
|
||||||
|
<div class="validation validation-fail">
|
||||||
|
{{ $message }}
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
|
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
|
||||||
|
@ -212,6 +212,8 @@ Route::group(['middleware' => ['throttle:100,1', 'api_db', 'token_auth', 'locale
|
|||||||
|
|
||||||
Route::resource('purchase_orders', 'PurchaseOrderController');
|
Route::resource('purchase_orders', 'PurchaseOrderController');
|
||||||
Route::post('purchase_orders/bulk', 'PurchaseOrderController@bulk')->name('purchase_orders.bulk');
|
Route::post('purchase_orders/bulk', 'PurchaseOrderController@bulk')->name('purchase_orders.bulk');
|
||||||
|
Route::put('purchase_orders/{purchase_order}/upload', 'PurchaseOrderController@upload');
|
||||||
|
|
||||||
Route::get('purchase_orders/{purchase_order}/{action}', 'PurchaseOrderController@action')->name('purchase_orders.action');
|
Route::get('purchase_orders/{purchase_order}/{action}', 'PurchaseOrderController@action')->name('purchase_orders.action');
|
||||||
|
|
||||||
Route::get('users', 'UserController@index');
|
Route::get('users', 'UserController@index');
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
use App\Http\Controllers\Auth\VendorContactLoginController;
|
use App\Http\Controllers\Auth\VendorContactLoginController;
|
||||||
use App\Http\Controllers\VendorPortal\InvitationController;
|
use App\Http\Controllers\VendorPortal\InvitationController;
|
||||||
use App\Http\Controllers\VendorPortal\PurchaseOrderController;
|
use App\Http\Controllers\VendorPortal\PurchaseOrderController;
|
||||||
|
use App\Http\Controllers\VendorPortal\UploadController;
|
||||||
use App\Http\Controllers\VendorPortal\VendorContactController;
|
use App\Http\Controllers\VendorPortal\VendorContactController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ Route::group(['middleware' => ['auth:vendor', 'vendor_locale', 'domain_db'], 'pr
|
|||||||
|
|
||||||
Route::post('purchase_orders/bulk', [PurchaseOrderController::class, 'bulk'])->name('purchase_orders.bulk');
|
Route::post('purchase_orders/bulk', [PurchaseOrderController::class, 'bulk'])->name('purchase_orders.bulk');
|
||||||
Route::get('logout', [VendorContactLoginController::class, 'logout'])->name('logout');
|
Route::get('logout', [VendorContactLoginController::class, 'logout'])->name('logout');
|
||||||
|
Route::post('purchase_order/upload/{purchase_order}', [UploadController::class,'upload'])->name('upload.store');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user