From 88e5379610d6f3cde0622316515ae7be943070bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Fri, 26 Aug 2022 18:24:23 +0200 Subject: [PATCH] Invoice functionnality --- app/Http/Controllers/InvoiceController.php | 275 ++++++++++- .../Invoice/InvoiceSetDateRequest.php | 33 ++ app/Lib/CollectionHelper.php | 64 +++ app/Models/Invoice.php | 156 +++++-- app/Policies/InvoicePolicy.php | 13 +- app/Policies/Rules/InvoiceStaticRules.php | 35 ++ composer.json | 3 +- composer.lock | 435 +++++++++++++++--- database/factories/InvoiceFactory.php | 7 +- database/seeders/InvoicingSeeder.php | 10 +- .../assets/documents/invoice/logo_ldc.jpg | Bin 0 -> 16611 bytes resources/lang/fr/models.php | 2 +- .../ts/components/InvoiceTasksComponent.vue | 235 ++++++---- resources/views/documents/invoice.blade.php | 219 +++++++++ resources/views/invoices/create.blade.php | 43 ++ resources/views/invoices/edit.blade.php | 114 +++++ resources/views/invoices/index.blade.php | 130 ++++++ resources/views/invoices/show.blade.php | 87 ++++ resources/views/layout/nav.blade.php | 3 + resources/views/ticket/index.blade.php | 2 +- routes/web.php | 12 + 21 files changed, 1664 insertions(+), 214 deletions(-) create mode 100644 app/Http/Requests/Invoice/InvoiceSetDateRequest.php create mode 100644 app/Lib/CollectionHelper.php create mode 100644 app/Policies/Rules/InvoiceStaticRules.php create mode 100644 resources/assets/documents/invoice/logo_ldc.jpg create mode 100644 resources/views/documents/invoice.blade.php create mode 100644 resources/views/invoices/create.blade.php create mode 100644 resources/views/invoices/edit.blade.php create mode 100644 resources/views/invoices/index.blade.php create mode 100644 resources/views/invoices/show.blade.php diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 365241e..efe993e 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -2,9 +2,282 @@ namespace App\Http\Controllers; +use App\Enums\Invoice\TaskType; +use App\Http\Requests\Invoice\InvoiceSetDateRequest; +use App\Models\Company; +use App\Models\Invoice; +use App\Models\Package; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; +use Illuminate\Validation\ValidationException; +use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest; +use Kwaadpepper\CrudPolicies\Traits\CrudController; +use Kwaadpepper\Enum\Rules\EnumIsValidRule; +use Symfony\Component\HttpFoundation\Response; +/** + * * Because update is denied when invoice is sent to the client + * ForceUpdate On signed date + * ForceUpdate On compta sent date + */ class InvoiceController extends Controller { - // + use CrudController { + index as crudIndex; + show as crudShow; + create as crudCreate; + edit as crudEdit; + } + + /** @var \App\Models\Invoice */ + public static $modelClass = Invoice::class; + + /** + * Crud Layout + * + * @var string + */ + public static $layout = 'layout.app'; + + /** + * Crud index view + * + * @param \Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest $request + * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory + */ + public function index(CrudIndexRequest $request) + { + /** @var \Illuminate\View\View */ + $view = $this->crudIndex($request); + if ($request->ajax()) { + return $view; + } + if (($models = $view->getData()['models'])->count() === 1) { + return \redirect()->route('invoices.edit', $models->first()); + } + return view('invoices.index', $view->getData()); + } + + /** + * Index query middleware + * + * @param Builder $query + * @return Builder + * @throws \ErrorException If account is missing in url. + */ + public function indexQuery(Builder $query): Builder + { + $query->orderBy('created_at', 'DESC'); + + return $query->orWhere(function (Builder $q) { + $q->whereHas('site', function ($q) { + $q->where('name', 'LIKE', '%' . \filter_var(request()->search, \FILTER_SANITIZE_STRING) . '%'); + })->orWhereHas('company', function ($q) { + $q->where('name', 'LIKE', '%' . \filter_var(request()->search, \FILTER_SANITIZE_STRING) . '%'); + })->orWhereHas('companyInvoiced', function ($q) { + $q->where('name', 'LIKE', '%' . \filter_var(request()->search, \FILTER_SANITIZE_STRING) . '%'); + }); + }); + } + + /** + * Show model action + * + * @param \Illuminate\Http\Request $request + * @param mixed ...$params + * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\JsonResponse + */ + public function show(Request $request, ...$params) + { + /** @var \Illuminate\View\View|\Illuminate\Http\JsonResponse */ + $view = call_user_func_array([$this, 'crudShow'], \array_merge([$request], $params)); + if ($view instanceof \Illuminate\Http\JsonResponse) { + return $view; + } + /** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Package> */ + $packages = Package::get()->keyBy('id')->all(); + return view('invoices.show', \array_merge($view->getData(), ['packages' => $packages])); + } + + /** + * Crud create view + * + * @param \Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest $request + * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory + */ + public function create(CrudIndexRequest $request) + { + /** @var \Illuminate\View\View */ + $view = $this->crudCreate($request); + + $companies = Company::get(); + $packages = Package::get(); + $taskRowModel = Invoice::getTaskStructure(); + + return view('invoices.create', \array_merge($view->getData(), [ + 'companies' => $companies, + 'packages' => $packages, + 'taskRowModel' => $taskRowModel, + ])); + } + + /** + * Before store model middleware + * + * @param \App\Models\Invoice $model + * @return void + */ + public function storeModel(Invoice &$model) + { + $this->validateTasks($model); + } + + /** + * Before update model middleware + * + * @param \App\Models\Invoice $model + * @return void + * @throws \Illuminate\Validation\ValidationException If invoice is signed. + */ + public function updateModel(Invoice &$model) + { + if ($model->signed_date) { + throw ValidationException::withMessages([ + 'erreur' => trans('Il n\'est pas psossible de mettre a jour une facture qui est signée') + ]); + } + $this->validateTasks($model); + } + + /** + * Edit model action + * + * @param mixed ...$params + * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory + */ + public function edit(...$params) + { + /** @var \Illuminate\View\View */ + $view = call_user_func_array([$this, 'crudEdit'], $params); + + $companies = Company::get(); + $packages = Package::get(); + $taskRowModel = Invoice::getTaskStructure(); + + return view('invoices.edit', \array_merge($view->getData(), [ + 'companies' => $companies, + 'packages' => $packages, + 'taskRowModel' => $taskRowModel, + ])); + } + + /** + * Validate Tasks + * + * @param \App\Models\Invoice $invoice + * @return void + * @throws \Illuminate\Validation\ValidationException If tasks are not valid. + */ + private function validateTasks(Invoice $invoice): void + { + $tasks = \collect(\json_decode($invoice->tasks, true)) + ->map(function ($task) { + if (\is_array($task)) { + return \array_intersect_key($task, Invoice::getTaskStructure()); + } + }); + $invoice->tasks = Validator::make(['tasks' => $tasks->all()], [ + 'tasks' => 'required|array|min:1', + 'tasks.*' => 'required|array', + 'tasks.*.days' => 'required|numeric|min:0.125', + 'tasks.*.amount' => 'required|numeric|min:0.125', + 'tasks.*.desc' => 'required|string|min:10', + 'tasks.*.type' => [ + 'required', new EnumIsValidRule(TaskType::class) + ], + 'tasks.*.package' => 'required_if:tasks.*.type,' . + TaskType::package()->value . '|nullable|exists:packages,id', + ], [], [ + 'tasks' => trans('Tâches') + ])->validated()['tasks']; + } + + /** + * Download Invoice + * + * @param \App\Models\Invoice $invoice + * @return \Symfony\Component\HttpFoundation\Response + */ + public function download(Invoice $invoice): Response + { + $this->authorize($invoice, 'download'); + return $invoice->generateInvoice() + ->stream(Str::slug(trans('Facture :appname :date - ref :ref', [ + 'appname' => config('app.name'), + 'date' => $invoice->created_at->isoFormat('YYYY-MM-DD'), + 'ref' => $invoice->ref, + ])) . '.pdf'); + } + + /** + * Set signed date + * + * @param \App\Http\Requests\Invoice\InvoiceSetDateRequest $request + * @param \App\Models\Invoice $invoice + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Validation\ValidationException If already signed. + */ + public function setSignedDate(InvoiceSetDateRequest $request, Invoice $invoice) + { + if ($invoice->signed_date) { + throw ValidationException::withMessages([ + 'erreur' => trans('La date de signature est déjà actée.') + ]); + } + $invoice->signed_date = $request->date; + $invoice->saveOrFail(); + return \back()->with('success', trans('La date de signature à bien été enregistrée')); + } + + /** + * Set client signed date + * + * @param \App\Http\Requests\Invoice\InvoiceSetDateRequest $request + * @param \App\Models\Invoice $invoice + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Validation\ValidationException If already signed. + */ + public function setClientSentDate(InvoiceSetDateRequest $request, Invoice $invoice) + { + if ($invoice->sent_client_date) { + throw ValidationException::withMessages([ + 'erreur' => trans('La date de signature client est déjà actée.') + ]); + } + $invoice->sent_client_date = $request->date; + $invoice->saveOrFail(); + return \back()->with('success', trans('La date de signature client à bien été enregistrée')); + } + + /** + * Set compta signed date + * + * @param \App\Http\Requests\Invoice\InvoiceSetDateRequest $request + * @param \App\Models\Invoice $invoice + * @return \Illuminate\Http\RedirectResponse + * @throws \Illuminate\Validation\ValidationException If already signed. + */ + public function setComptaSentDate(InvoiceSetDateRequest $request, Invoice $invoice) + { + if ($invoice->sent_accounting_date) { + throw ValidationException::withMessages([ + 'erreur' => trans('La date de signature compta est déjà actée.') + ]); + } + $invoice->sent_accounting_date = $request->date; + $invoice->saveOrFail(); + return \back()->with('success', trans('La date de signature compta à bien été enregistrée')); + } } diff --git a/app/Http/Requests/Invoice/InvoiceSetDateRequest.php b/app/Http/Requests/Invoice/InvoiceSetDateRequest.php new file mode 100644 index 0000000..ae8dff3 --- /dev/null +++ b/app/Http/Requests/Invoice/InvoiceSetDateRequest.php @@ -0,0 +1,33 @@ +user(); + + return auth()->check() && $user->isAdmin(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'date' => 'required|date' + ]; + } +} diff --git a/app/Lib/CollectionHelper.php b/app/Lib/CollectionHelper.php new file mode 100644 index 0000000..a665a2f --- /dev/null +++ b/app/Lib/CollectionHelper.php @@ -0,0 +1,64 @@ +count(); + + return self::paginator($results->forPage($page, $pageSize), $total, $pageSize, $page, [ + 'path' => Paginator::resolveCurrentPath(), + 'pageName' => 'page', + ]); + } + + /** + * Create a new length-aware paginator instance. + * + * @param \Illuminate\Support\Collection $items + * @param integer $total + * @param integer $perPage + * @param integer $currentPage + * @param array $options + * @return \Illuminate\Pagination\LengthAwarePaginator + */ + protected static function paginator(Collection $items, int $total, int $perPage, int $currentPage, array $options) + { + return Container::getInstance()->makeWith(LengthAwarePaginator::class, [ + 'items' => $items, + 'total' => $total, + 'perPage' => $perPage, + 'currentPage' => $currentPage, + 'options' => $options + ]); + } +} diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 5ba7c7b..0b68a2c 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Enums\Invoice\TaskType; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Carbon; @@ -9,6 +10,7 @@ use Illuminate\Support\Str; use Kwaadpepper\CrudPolicies\Enums\CrudAction; use Kwaadpepper\CrudPolicies\Enums\CrudType; use Kwaadpepper\CrudPolicies\Traits\IsCrudModel; +use Meneses\LaravelMpdf\LaravelMpdf; class Invoice extends Model { @@ -48,7 +50,10 @@ class Invoice extends Model * @var array */ protected $casts = [ - 'tasks' => 'array' + 'tasks' => 'array', + 'signed_date' => 'date', + 'sent_client_date' => 'date', + 'sent_accounting_date' => 'date', ]; /** @@ -114,20 +119,18 @@ class Invoice extends Model 'label' => trans('Montant total'), 'type' => CrudType::float(), 'nullable' => false, + 'readonly' => true, 'actions' => [ CrudAction::view(), - CrudAction::create(), - CrudAction::update() ] ], 'total_days' => [ 'label' => trans('Montant total jours'), 'type' => CrudType::float(), 'nullable' => false, + 'readonly' => true, 'actions' => [ CrudAction::view(), - CrudAction::create(), - CrudAction::update() ] ], 'comment' => [ @@ -154,33 +157,30 @@ class Invoice extends Model 'label' => trans('Date de signature'), 'type' => CrudType::datetime(), 'nullable' => true, + 'readonly' => true, 'actions' => [ CrudAction::viewAny(), CrudAction::view(), - CrudAction::create(), - CrudAction::update() ] ], 'sent_client_date' => [ 'label' => trans('Date d\'envoi client'), 'type' => CrudType::datetime(), 'nullable' => true, + 'readonly' => true, 'actions' => [ CrudAction::viewAny(), CrudAction::view(), - CrudAction::create(), - CrudAction::update() ] ], 'sent_accounting_date' => [ 'label' => trans('Date d\'envoi compta'), 'type' => CrudType::datetime(), 'nullable' => true, + 'readonly' => true, 'actions' => [ CrudAction::viewAny(), CrudAction::view(), - CrudAction::create(), - CrudAction::update() ] ], 'site_id' => [ @@ -209,7 +209,7 @@ class Invoice extends Model ] ], 'invoiced_to_id' => [ - 'label' => trans('models.classes.companies'), + 'label' => trans('Entité à facturer'), 'type' => CrudType::belongsTo(), 'belongsTo' => Company::class, 'belongsToLabel' => 'entité', @@ -249,39 +249,40 @@ class Invoice extends Model protected static function booted() { static::creating(function (self $model) { - $model->ref = \sprintf( + $model->ref = \sprintf( '%s_%s_%s', - Carbon::now()->isoFormat('Y-m'), - Str::padLeft(Invoice::whereYear('created_at', Carbon::now()->isoFormat('Y')) - ->whereMonth('created_at', Carbon::now()->isoFormat('M')) - ->count() ?: 1, 4, '0'), - Str::substr(Str::upper(Str::slug(User::query()->inRandomOrder()->first()->name)), 0, 2) + Carbon::now()->isoFormat('YYYY'), + Str::padLeft((Invoice::whereYear('created_at', Carbon::now()->isoFormat('YYYY')) + ->whereMonth('created_at', Carbon::now()->isoFormat('MM')) + ->count() + 1) ?: 1, 4, '0'), + Str::substr(Str::upper(Str::slug(User::query()->inRandomOrder()->first()->name)), 0, 1) ); + $model->total_days = collect($model->tasks) + /** @var null|float $previous */ + ->reduce(function ($previous, array $task) { + return \floatval($task['days']) + \floatval($previous); + }); + $model->total = collect($model->tasks) + /** @var null|float $previous */ + ->reduce(function ($previous, array $task) { + return \floatval($task['amount']) + \floatval($previous); + }); + }); + static::updating(function (self $model) { + $model->total_days = collect($model->tasks) + /** @var null|float $previous */ + ->reduce(function ($previous, array $task) { + return \floatval($task['days']) + \floatval($previous); + }); + $model->total = collect($model->tasks) + /** @var null|float $previous */ + ->reduce(function ($previous, array $task) { + return \floatval($task['amount']) + \floatval($previous); + }); }); } - // * METHODS - - /** - * Get the tasks line structure skel - * - * @return array - */ - public static function getTaskStructure(): array - { - return [ - // * string - 'desc' => '', - // * usigned float - 'days' => '', - // * Invoice\TaskType - 'type' => '', - // * usigned float - 'amount' => '', - ]; - } - - // * RELATIONSHIPS + // * RELATIONSHIPS /** * BelongsTo Site @@ -311,4 +312,79 @@ class Invoice extends Model { return $this->BelongsTo(Company::class, 'invoiced_to_id'); } + + // * METHODS + + /** + * Get the tasks line structure skel + * + * @return array + */ + public static function getTaskStructure(): array + { + return [ + // * string + 'desc' => '', + // * usigned float + 'days' => 0, + // * Invoice\TaskType value + 'type' => TaskType::web()->value, + // * usigned float + 'amount' => 0, + // * package ID if task type package or null + 'package' => null + ]; + } + + /** + * Generate Invoice + * + * @return LaravelMpdf + */ + public function generateInvoice(): LaravelMpdf + { + /** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Package> */ + $packages = Package::get()->keyBy('id')->all(); + + /** @var \Meneses\LaravelMpdf\LaravelMpdfWrapper */ + $mpdf = app('mpdf.wrapper'); + /** @var \Meneses\LaravelMpdf\LaravelMpdf */ + $pdf = $mpdf->loadView( + 'documents.invoice', + $this->toArray(), + [ + 'site' => $this->site, + 'company' => $this->company, + 'companyInvoiced' => $this->companyInvoiced, + 'packages' => $packages, + ], + [ + 'mode' => '', + 'format' => 'A4', + 'default_font_size' => '12', + 'default_font' => 'sans-serif', + 'margin_left' => 0, + 'margin_right' => 0, + 'margin_top' => 0, + 'margin_bottom' => 0, + 'margin_header' => 0, + 'margin_footer' => 0, + 'orientation' => 'P', + 'title' => __('Facture Service Web :ref', ['ref' => $this->ref]), + 'author' => 'Equipe Web', + 'watermark' => '', + 'show_watermark' => false, + 'watermark_font' => 'sans-serif', + 'display_mode' => 'fullpage', + 'watermark_text_alpha' => 0.1, + 'custom_font_dir' => '', + 'custom_font_data' => [], + 'auto_language_detection' => false, + 'temp_dir' => rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR), + 'pdfa' => false, + 'pdfaauto' => false, + ] + ); + return $pdf; + } } diff --git a/app/Policies/InvoicePolicy.php b/app/Policies/InvoicePolicy.php index 70c06fb..46c5f79 100644 --- a/app/Policies/InvoicePolicy.php +++ b/app/Policies/InvoicePolicy.php @@ -4,6 +4,7 @@ namespace App\Policies; use App\Models\User; use App\Policies\Rules\UserRoleRules as URR; +use App\Policies\Rules\InvoiceStaticRules as ISR; use Illuminate\Auth\Access\HandlesAuthorization; use Illuminate\Database\Eloquent\Model; use Kwaadpepper\CrudPolicies\Policies\RootPolicy; @@ -29,9 +30,17 @@ class InvoicePolicy extends RootPolicy $this->policies = [ 'invoice' => [ 'viewAny' => URR::make('userIsAdmin', $user), + 'view' => URR::make('userIsAdmin', $user), 'create' => URR::make('userIsAdmin', $user), - 'update' => URR::make('userIsAdmin', $user), - 'delete' => URR::make('userIsAdmin', $user) + 'update' => [ + URR::make('userIsAdmin', $user), + ISR::make('invoiceHasNotBeenSentTotheClient', $user), + ], + 'delete' => URR::make('denyEveryOne'), + 'download' => [ + URR::make('userIsAdmin', $user), + ISR::make('invoiceHasTasks', $user), + ] ] ]; } diff --git a/app/Policies/Rules/InvoiceStaticRules.php b/app/Policies/Rules/InvoiceStaticRules.php new file mode 100644 index 0000000..747d998 --- /dev/null +++ b/app/Policies/Rules/InvoiceStaticRules.php @@ -0,0 +1,35 @@ +sent_client_date); + } + + /** + * Invoice has some tasks + * + * @param \App\Models\Invoice $model + * @return boolean + */ + protected static function invoiceHasTasks(Invoice $model): bool + { + return (bool)\count($model->tasks); + } +} diff --git a/composer.json b/composer.json index 2095906..338f720 100644 --- a/composer.json +++ b/composer.json @@ -10,12 +10,13 @@ "license": "MIT", "require": { "php": "^7.3|^8.0", + "carlos-meneses/laravel-mpdf": "^2.1", "doctrine/dbal": "^3.1", "erusev/parsedown": "^1.7", "fideloper/proxy": "^4.4", "fruitcake/laravel-cors": "^2.0", "guzzlehttp/guzzle": "^7.0.1", - "kwaadpepper/crud-policies": "^0.4.5", + "kwaadpepper/crud-policies": "^0.4.7", "kwaadpepper/enum": "^2.1", "kwaadpepper/exception-handler": "^0.0.9", "laravel-lang/lang": "~8.0", diff --git a/composer.lock b/composer.lock index ce363c3..b7be3ff 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "47a64c62b7e388c80216db44f86f0d44", + "content-hash": "d6431e8c4b000923f948206977031782", "packages": [ { "name": "asm89/stack-cors", @@ -122,6 +122,56 @@ ], "time": "2021-08-15T20:50:18+00:00" }, + { + "name": "carlos-meneses/laravel-mpdf", + "version": "2.1.5", + "source": { + "type": "git", + "url": "https://github.com/mccarlosen/laravel-mpdf.git", + "reference": "810008e1a4b82fb6c8c74cdf32fd130195756027" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mccarlosen/laravel-mpdf/zipball/810008e1a4b82fb6c8c74cdf32fd130195756027", + "reference": "810008e1a4b82fb6c8c74cdf32fd130195756027", + "shasum": "" + }, + "require": { + "mpdf/mpdf": "^8.0", + "php": ">=7.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Meneses\\LaravelMpdf\\LaravelMpdfServiceProvider" + ], + "aliases": { + "PDF": "Meneses\\LaravelMpdf\\Facades\\LaravelMpdf" + } + } + }, + "autoload": { + "psr-4": { + "Meneses\\LaravelMpdf\\": "src/LaravelMpdf" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Laravel Mpdf: Using Mpdf in Laravel 5 for generate Pdfs.", + "keywords": [ + "laravel", + "mpdf", + "pdf" + ], + "support": { + "issues": "https://github.com/mccarlosen/laravel-mpdf/issues", + "source": "https://github.com/mccarlosen/laravel-mpdf/tree/2.1.5" + }, + "time": "2021-03-30T21:17:19+00:00" + }, { "name": "doctrine/cache", "version": "2.2.0", @@ -1502,16 +1552,16 @@ }, { "name": "kwaadpepper/crud-policies", - "version": "0.4.6", + "version": "0.4.7", "source": { "type": "git", "url": "https://github.com/Kwaadpepper/crud-policies.git", - "reference": "8f83c66385409ce98955b1b3c65aa2da414acdc8" + "reference": "89e35c6969da683b0b5ed440539b45b5109871b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Kwaadpepper/crud-policies/zipball/8f83c66385409ce98955b1b3c65aa2da414acdc8", - "reference": "8f83c66385409ce98955b1b3c65aa2da414acdc8", + "url": "https://api.github.com/repos/Kwaadpepper/crud-policies/zipball/89e35c6969da683b0b5ed440539b45b5109871b0", + "reference": "89e35c6969da683b0b5ed440539b45b5109871b0", "shasum": "" }, "require": { @@ -1562,9 +1612,9 @@ ], "support": { "issues": "https://github.com/Kwaadpepper/crud-policies/issues", - "source": "https://github.com/Kwaadpepper/crud-policies/tree/0.4.6" + "source": "https://github.com/Kwaadpepper/crud-policies/tree/0.4.7" }, - "time": "2022-06-17T08:48:10+00:00" + "time": "2022-08-26T14:55:14+00:00" }, { "name": "kwaadpepper/enum", @@ -2690,6 +2740,142 @@ ], "time": "2022-07-24T11:55:47+00:00" }, + { + "name": "mpdf/mpdf", + "version": "v8.1.2", + "source": { + "type": "git", + "url": "https://github.com/mpdf/mpdf.git", + "reference": "a8a22f4874157e490d41b486053a20bec42e182c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mpdf/mpdf/zipball/a8a22f4874157e490d41b486053a20bec42e182c", + "reference": "a8a22f4874157e490d41b486053a20bec42e182c", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "ext-mbstring": "*", + "myclabs/deep-copy": "^1.7", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": "^5.6 || ^7.0 || ~8.0.0 || ~8.1.0", + "php-http/message-factory": "^1.0", + "psr/http-message": "^1.0", + "psr/log": "^1.0 || ^2.0", + "setasign/fpdi": "^2.1" + }, + "require-dev": { + "mockery/mockery": "^1.3.0", + "mpdf/qrcode": "^1.1.0", + "squizlabs/php_codesniffer": "^3.5.0", + "tracy/tracy": "^2.4", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-bcmath": "Needed for generation of some types of barcodes", + "ext-xml": "Needed mainly for SVG manipulation", + "ext-zlib": "Needed for compression of embedded resources, such as fonts" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mpdf\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-only" + ], + "authors": [ + { + "name": "Matěj Humpál", + "role": "Developer, maintainer" + }, + { + "name": "Ian Back", + "role": "Developer (retired)" + } + ], + "description": "PHP library generating PDF files from UTF-8 encoded HTML", + "homepage": "https://mpdf.github.io", + "keywords": [ + "pdf", + "php", + "utf-8" + ], + "support": { + "docs": "http://mpdf.github.io", + "issues": "https://github.com/mpdf/mpdf/issues", + "source": "https://github.com/mpdf/mpdf" + }, + "funding": [ + { + "url": "https://www.paypal.me/mpdf", + "type": "custom" + } + ], + "time": "2022-08-15T08:15:09+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2022-03-03T13:19:32+00:00" + }, { "name": "nesbot/carbon", "version": "2.61.0", @@ -2913,6 +3099,110 @@ }, "time": "2022-01-27T09:35:39+00:00" }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "php-http/message-factory", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-http/message-factory.git", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "Factory interfaces for PSR-7 HTTP Message", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "stream", + "uri" + ], + "support": { + "issues": "https://github.com/php-http/message-factory/issues", + "source": "https://github.com/php-http/message-factory/tree/master" + }, + "time": "2015-12-19T14:08:53+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.0", @@ -3693,6 +3983,78 @@ ], "time": "2021-09-25T23:10:38+00:00" }, + { + "name": "setasign/fpdi", + "version": "v2.3.6", + "source": { + "type": "git", + "url": "https://github.com/Setasign/FPDI.git", + "reference": "6231e315f73e4f62d72b73f3d6d78ff0eed93c31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/6231e315f73e4f62d72b73f3d6d78ff0eed93c31", + "reference": "6231e315f73e4f62d72b73f3d6d78ff0eed93c31", + "shasum": "" + }, + "require": { + "ext-zlib": "*", + "php": "^5.6 || ^7.0 || ^8.0" + }, + "conflict": { + "setasign/tfpdf": "<1.31" + }, + "require-dev": { + "phpunit/phpunit": "~5.7", + "setasign/fpdf": "~1.8", + "setasign/tfpdf": "1.31", + "squizlabs/php_codesniffer": "^3.5", + "tecnickcom/tcpdf": "~6.2" + }, + "suggest": { + "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." + }, + "type": "library", + "autoload": { + "psr-4": { + "setasign\\Fpdi\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Slabon", + "email": "jan.slabon@setasign.com", + "homepage": "https://www.setasign.com" + }, + { + "name": "Maximilian Kresse", + "email": "maximilian.kresse@setasign.com", + "homepage": "https://www.setasign.com" + } + ], + "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", + "homepage": "https://www.setasign.com/fpdi", + "keywords": [ + "fpdf", + "fpdi", + "pdf" + ], + "support": { + "issues": "https://github.com/Setasign/FPDI/issues", + "source": "https://github.com/Setasign/FPDI/tree/v2.3.6" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi", + "type": "tidelift" + } + ], + "time": "2021-02-11T11:37:01+00:00" + }, { "name": "swiftmailer/swiftmailer", "version": "v6.3.0", @@ -6720,65 +7082,6 @@ }, "time": "2022-01-20T13:18:17+00:00" }, - { - "name": "myclabs/deep-copy", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2022-03-03T13:19:32+00:00" - }, { "name": "nunomaduro/collision", "version": "v5.11.0", diff --git a/database/factories/InvoiceFactory.php b/database/factories/InvoiceFactory.php index a551c1a..393b3f5 100644 --- a/database/factories/InvoiceFactory.php +++ b/database/factories/InvoiceFactory.php @@ -49,11 +49,14 @@ class InvoiceFactory extends Factory } $totalDays += $days; $total += $amount; - $tasks[] = \array_merge(Invoice::getTaskStructure(), [ + /** @var \App\Enums\Invoice\TaskType */ + $taskType = \collect(TaskType::toArray())->random(); + $tasks[] = \array_merge(Invoice::getTaskStructure(), [ 'desc' => $this->faker->words(9, true), 'days' => $days, - 'type' => \collect(TaskType::toArray())->random()->value, + 'type' => $taskType->value, 'amount' => $amount, + 'package' => 1 ]); } //end for diff --git a/database/seeders/InvoicingSeeder.php b/database/seeders/InvoicingSeeder.php index 9cd2f4c..166c42e 100644 --- a/database/seeders/InvoicingSeeder.php +++ b/database/seeders/InvoicingSeeder.php @@ -39,11 +39,11 @@ class InvoicingSeeder extends Seeder Invoice::factory(45)->make()->each(function (Invoice $model) { $model->ref = \sprintf( '%s_%s_%s', - Carbon::now()->isoFormat('Y-m'), - Str::padLeft(Invoice::whereYear('created_at', Carbon::now()->isoFormat('Y')) - ->whereMonth('created_at', Carbon::now()->isoFormat('M')) - ->count() ?: 1, 4, '0'), - Str::substr(Str::upper(Str::slug(User::query()->inRandomOrder()->first()->name)), 0, 2) + Carbon::now()->isoFormat('YYYY-MM'), + Str::padLeft((Invoice::whereYear('created_at', Carbon::now()->isoFormat('YYYY')) + ->whereMonth('created_at', Carbon::now()->isoFormat('MM')) + ->count() + 1) ?: 1, 4, '0'), + Str::substr(Str::upper(Str::slug(User::query()->inRandomOrder()->first()->name)), 0, 1) ); $model->site()->associate(Site::query()->inRandomOrder()->first()); $model->company()->associate(Company::query()->inRandomOrder()->first()); diff --git a/resources/assets/documents/invoice/logo_ldc.jpg b/resources/assets/documents/invoice/logo_ldc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d9ad13ed37f53b89ac740be75ce1c6c3c9fcbb17 GIT binary patch literal 16611 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 jMJ^bq5tM?rz1O+UZsWI`cmOx|ZG4 literal 0 HcmV?d00001 diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index b85e130..0406f0b 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -15,6 +15,6 @@ return [ 'labels' => 'Labels', 'packages' => 'Forfaits', 'invoices' => 'Factures', - 'companies' => 'Compagnies', + 'companies' => 'Sociétés', ] ]; diff --git a/resources/ts/components/InvoiceTasksComponent.vue b/resources/ts/components/InvoiceTasksComponent.vue index c699cde..cba0b49 100644 --- a/resources/ts/components/InvoiceTasksComponent.vue +++ b/resources/ts/components/InvoiceTasksComponent.vue @@ -24,103 +24,110 @@ {{ __("Jours") }} - + {{ __("Montant") }} - - - -