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 $auto_archive_invoice_cancelled = false;
|
||||
|
||||
public $vendor_portal_enable_uploads=false;
|
||||
|
||||
public static $casts = [
|
||||
'vendor_portal_enable_uploads' => 'bool',
|
||||
'besr_id' => 'string',
|
||||
'qr_iban' => '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\StorePurchaseOrderRequest;
|
||||
use App\Http\Requests\PurchaseOrder\UpdatePurchaseOrderRequest;
|
||||
use App\Http\Requests\PurchaseOrder\UploadPurchaseOrderRequest;
|
||||
use App\Jobs\Invoice\ZipInvoices;
|
||||
use App\Jobs\PurchaseOrder\PurchaseOrderEmail;
|
||||
use App\Jobs\PurchaseOrder\ZipPurchaseOrders;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Repositories\PurchaseOrderRepository;
|
||||
use App\Transformers\PurchaseOrderTransformer;
|
||||
use App\Utils\Ninja;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Utils\Traits\SavesDocuments;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PurchaseOrderController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
use SavesDocuments;
|
||||
|
||||
protected $entity_type = PurchaseOrder::class;
|
||||
protected $entity_transformer = PurchaseOrderTransformer::class;
|
||||
@ -655,4 +659,69 @@ class PurchaseOrderController extends BaseController
|
||||
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
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
@ -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\PurchaseOrderInvitation;
|
||||
use App\Transformers\DocumentTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class PurchaseOrderTransformer extends EntityTransformer
|
||||
@ -22,6 +23,7 @@ class PurchaseOrderTransformer extends EntityTransformer
|
||||
|
||||
protected $defaultIncludes = [
|
||||
'invitations',
|
||||
'documents'
|
||||
];
|
||||
|
||||
public function includeInvitations(PurchaseOrder $purchase_order)
|
||||
@ -31,6 +33,14 @@ class PurchaseOrderTransformer extends EntityTransformer
|
||||
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)
|
||||
{
|
||||
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')
|
||||
|
||||
@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]))
|
||||
<div class="mb-4">
|
||||
@include('portal.ninja2020.purchase_orders.includes.actions', ['purchase_order' => $purchase_order])
|
||||
</div>
|
||||
@else
|
||||
<input type="hidden" id="approve-button">
|
||||
<div class="bg-white shadow sm:rounded-lg mb-4">
|
||||
<div class="px-4 py-5 sm:p-6">
|
||||
<div class="sm:flex sm:items-start sm:justify-between">
|
||||
|
@ -83,7 +83,6 @@
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -163,6 +162,15 @@
|
||||
</div>
|
||||
@enderror
|
||||
</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 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::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('users', 'UserController@index');
|
||||
|
@ -12,6 +12,7 @@
|
||||
use App\Http\Controllers\Auth\VendorContactLoginController;
|
||||
use App\Http\Controllers\VendorPortal\InvitationController;
|
||||
use App\Http\Controllers\VendorPortal\PurchaseOrderController;
|
||||
use App\Http\Controllers\VendorPortal\UploadController;
|
||||
use App\Http\Controllers\VendorPortal\VendorContactController;
|
||||
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::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