diff --git a/.env.example b/.env.example index 479a50a..0152884 100644 --- a/.env.example +++ b/.env.example @@ -36,8 +36,6 @@ MAIL_ADMIN=equipe.web@ldc.fr # Sentry SENTRY_LARAVEL_DSN= -LDC_STATUS_CHECKER_API_KEY= - # ADMIN SECURE # - API ENDPOINT (Optional) LDC_WEB_API_ENDPOINT= @@ -45,3 +43,7 @@ LDC_WEB_API_ENDPOINT= LDC_WEB_API_KEY= # - LIST SEPARATED WITH ',' (Optional) LDC_WEB_CIDR_MANUAL_ALLOWED_LIST= + +# GITLAB API +GITLAB_URL= +GITLAB_TOKEN= diff --git a/app/Enums/Invoices/InvoiceStatusEnum.php b/app/Enums/Invoices/InvoiceStatusEnum.php new file mode 100644 index 0000000..f9e0163 --- /dev/null +++ b/app/Enums/Invoices/InvoiceStatusEnum.php @@ -0,0 +1,25 @@ +name => "Ouvert (nouveaux ou en cours)", + self::refused->name => "Refusé (par le client ou aucune réponse de sa part)", + self::canceled->name => "Annulé (supprimé)", + self::archived->name => "Archivé (validé et envoyé à la comptabilité)", + ]; +} diff --git a/app/Enums/Invoice/TaskTypeEnum.php b/app/Enums/Invoices/TaskTypeEnum.php similarity index 97% rename from app/Enums/Invoice/TaskTypeEnum.php rename to app/Enums/Invoices/TaskTypeEnum.php index 1420f20..2b6c0b7 100644 --- a/app/Enums/Invoice/TaskTypeEnum.php +++ b/app/Enums/Invoices/TaskTypeEnum.php @@ -1,6 +1,6 @@ name => "Olivier LEGAT", + ]; +} diff --git a/app/Http/Controllers/Bo/InvoiceController.php b/app/Http/Controllers/Bo/InvoiceController.php index 87fb954..489e6ab 100644 --- a/app/Http/Controllers/Bo/InvoiceController.php +++ b/app/Http/Controllers/Bo/InvoiceController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Bo; +use App\Enums\Invoices\InvoiceStatusEnum; use App\Http\Controllers\Controller; use App\Http\Requests\Bo\Invoices\StoreInvoiceRequest; use App\Http\Requests\Bo\Invoices\UpdateInvoiceRequest; @@ -26,6 +27,10 @@ class InvoiceController extends Controller { /** @var \Illuminate\Database\Eloquent\Builder $invoiceModels */ $invoiceModels = Invoice::query(); + $status = intval($request->status); + $invoiceModels = $invoiceModels->when($status, function ($query) use ($status) { + $query->where('status', $status); + }); /** @var string $search Search field */ $search = $request->search; @@ -46,7 +51,7 @@ class InvoiceController extends Controller /** Custom pagination */ $invoiceModels = $this->customPaginate($invoiceModels, $request->pagination); - return view('bo.pages.invoices.index', compact('invoiceModels', 'search', 'searchFields')); + return view('bo.pages.invoices.index', compact('invoiceModels', 'search', 'searchFields', 'status')); } /** @@ -79,6 +84,7 @@ class InvoiceController extends Controller 'packageModels' => $packageModels, 'siteModels' => $siteModels, 'companyModels' => $companyModels, + 'invoiceStatusArray' => $invoice->getInvoiceStatusArray($invoice), ]); } @@ -130,6 +136,7 @@ class InvoiceController extends Controller 'packageModels' => $packageModels, 'siteModels' => $siteModels, 'companyModels' => $companyModels, + 'invoiceStatusArray' => $invoice->getInvoiceStatusArray($invoice), ]); } @@ -143,12 +150,7 @@ class InvoiceController extends Controller public function update(UpdateInvoiceRequest $request, Invoice $invoice): \Illuminate\Http\RedirectResponse { return DB::transaction(function () use ($request, $invoice) { - if (isset($invoice->signed_date)) { - $fields = $request->safe()->only('signed_date', 'sent_client_date', 'sent_accounting_date'); - } else { - $fields = $request->validated(); - } - $invoice->fill($fields); + $invoice->fill(($invoice->status->value === InvoiceStatusEnum::opened->value) ? $request->validated() : []); if ($invoice->saveOrFail()) { return redirect() ->route('bo.invoices.edit', $invoice) diff --git a/app/Http/Controllers/Bo/SiteController.php b/app/Http/Controllers/Bo/SiteController.php index 49aca29..f19c2da 100644 --- a/app/Http/Controllers/Bo/SiteController.php +++ b/app/Http/Controllers/Bo/SiteController.php @@ -129,7 +129,7 @@ class SiteController extends Controller { return DB::transaction(function () use ($request, $site) { $site->fill($request->validated()); - $site->users()->attach(collect($request->validated('users'))->pluck('id')); + $site->users()->sync(collect($request->validated('users'))->pluck('id')); if ($site->saveOrFail()) { return redirect() ->route('bo.sites.edit', $site) diff --git a/app/Http/Controllers/Bo/TicketController.php b/app/Http/Controllers/Bo/TicketController.php index 506918c..c270fe6 100644 --- a/app/Http/Controllers/Bo/TicketController.php +++ b/app/Http/Controllers/Bo/TicketController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Bo; use App\Enums\Tickets\TicketStatusEnum; use App\Enums\Tickets\TicketTypeEnum; +use App\Enums\Users\GitlabUsersEnum; use App\Events\NotifySubscriptions; use App\Events\TicketStatusChanged; use App\Http\Controllers\Controller; @@ -21,6 +22,7 @@ use App\Models\Message; use App\Models\Site; use App\Models\Ticket; use App\Models\User; +use GrahamCampbell\GitLab\Facades\GitLab; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -541,7 +543,7 @@ class TicketController extends Controller Session::get('tickets.sort_way') ); } else { - $tickets->orderBy('id', 'DESC'); + $tickets->orderBy('last_activity', 'DESC'); } return $tickets; } @@ -624,4 +626,82 @@ class TicketController extends Controller ); }); } + + /** + * Create a gitlab issue from a ticket. + * + * @param \App\Models\Ticket $ticket + * @return \Illuminate\Http\RedirectResponse + */ + public function createGitlabIssue(Ticket $ticket) + { + $gitlabRepoId = $ticket->site->gitlab_project_id; + $description = \sprintf("%s
", $ticket->text); + $cLabel = match ($ticket->type) { + TicketTypeEnum::bug => 'c-bug', + TicketTypeEnum::evolution => 'c-feature', + }; + + try { + if (!\is_null($ticket->file)) { + $uploadedFile = GitLab::projects()->uploadFile($gitlabRepoId, $ticket->file); + $description .= $this->formatGitlabFiles($ticket, $uploadedFile, 1); + } + + if (!\is_null($ticket->file2)) { + $uploadedFile2 = GitLab::projects()->uploadFile($gitlabRepoId, $ticket->file2); + $description .= $this->formatGitlabFiles($ticket, $uploadedFile2, 2); + } + + $issue = GitLab::issues()->create($gitlabRepoId, [ + 'title' => 'ts-' . $ticket->id, + 'description' => $description, + 'labels' => \implode(",", [ + 'c-support', + $cLabel + ]), + 'assignee_id' => GitlabUsersEnum::olivier, + ]); + } catch (\Exception $e) { + \report($e); + return back()->with('error', \trans("Une erreur est survenue")); + } catch (\Throwable $e) { + \report($e); + return back()->with('error', \trans("Une erreur est survenue")); + } //end try + + $ticket->gitlab_issue = $issue['web_url']; + $ticket->save(); + + return back()->with( + 'success', + trans("L'issue a été créée sur Gitlab.") + ); + } + + /** + * Format files for Gitlab issues + * + * @param \App\Models\Ticket $ticket + * @param array $uploadedFile + * @param integer $fileNb + * @return string + */ + private function formatGitlabFiles(Ticket $ticket, array $uploadedFile, int $fileNb) + { + if (Str::endsWith($ticket->file, ['jpg', 'png', 'jpeg'])) { + $file = \sprintf( + "![Fichier joint %d](%s)", + $fileNb, + $uploadedFile['url'] + ); + } else { + $file = \sprintf( + '%s', + $uploadedFile['url'], + __('Lien vers le fichier'), + ); + } + return \sprintf("

Fichier %d :
%s
", $fileNb, $file); + } } diff --git a/app/Http/Controllers/Bo/UserController.php b/app/Http/Controllers/Bo/UserController.php index 1b2bb1d..b5e0cd4 100644 --- a/app/Http/Controllers/Bo/UserController.php +++ b/app/Http/Controllers/Bo/UserController.php @@ -120,10 +120,7 @@ class UserController extends Controller { return DB::transaction(function () use ($request, $user) { $user->fill($request->safe()->except(['sites'])); - $user->sites()->detach(); - if (collect($request->validated('sites'))->pluck('id')) { - $user->sites()->attach(collect($request->validated('sites'))->pluck('id')); - } + $user->sites()->sync(collect($request->validated('sites'))->pluck('id')); if ($user->saveOrFail()) { return redirect() ->route('bo.users.edit', $user) diff --git a/app/Http/Requests/Bo/Invoices/StoreInvoiceRequest.php b/app/Http/Requests/Bo/Invoices/StoreInvoiceRequest.php index 824a742..d20ddd9 100644 --- a/app/Http/Requests/Bo/Invoices/StoreInvoiceRequest.php +++ b/app/Http/Requests/Bo/Invoices/StoreInvoiceRequest.php @@ -2,7 +2,7 @@ namespace App\Http\Requests\Bo\Invoices; -use App\Enums\Invoice\TaskTypeEnum; +use App\Enums\Invoices\TaskTypeEnum; use App\Models\Invoice; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; @@ -90,7 +90,8 @@ class StoreInvoiceRequest extends FormRequest 'invoiced_to_id' => trans('Entité à facturer'), 'signed_date' => trans('Date de signature du Devis'), 'sent_client_date' => trans('Date d\'envoi du Devis au client'), - 'sent_accounting_date' => trans('Date d\'envoi du Devis à la compta'), + 'sent_accounting_date' => trans('Date d\'envoi du Devis à la comptabilité'), + 'status' => trans('État du devis'), ]; } } diff --git a/app/Http/Requests/Bo/Invoices/UpdateInvoiceRequest.php b/app/Http/Requests/Bo/Invoices/UpdateInvoiceRequest.php index bef04c2..338d9eb 100644 --- a/app/Http/Requests/Bo/Invoices/UpdateInvoiceRequest.php +++ b/app/Http/Requests/Bo/Invoices/UpdateInvoiceRequest.php @@ -2,6 +2,9 @@ namespace App\Http\Requests\Bo\Invoices; +use App\Enums\Invoices\InvoiceStatusEnum; +use Illuminate\Validation\Rules\Enum; + class UpdateInvoiceRequest extends StoreInvoiceRequest { /** @@ -12,14 +15,34 @@ class UpdateInvoiceRequest extends StoreInvoiceRequest public function rules() { $rules = []; - if (!$this->route('invoice')->signed_date) { - $rules = array_merge($rules, parent::rules()); - } - $rules = array_merge($rules, [ - 'signed_date' => 'sometimes|nullable|date', - 'sent_client_date' => 'sometimes|nullable|date', - 'sent_accounting_date' => 'sometimes|nullable|date', - ]); + $model = $this->route('invoice'); + if ($model->status->value === InvoiceStatusEnum::opened->value) { + if (!$model->sent_client_date) { + $rules = array_merge($rules, parent::rules()); + } + $rules = array_merge($rules, [ + 'sent_client_date' => 'required_with:signed_date,sent_accounting_date|sometimes|nullable|date', + 'signed_date' => 'required_with:sent_accounting_date|sometimes|nullable|date', + 'sent_accounting_date' => 'sometimes|nullable|date', + 'status' => [ + 'required', + new Enum(InvoiceStatusEnum::class), + //phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + function ($attribute, $value, $fail) use ($model) { + if ( + (intval($value) === InvoiceStatusEnum::archived->value) && + (is_null($model->sent_client_date) || + is_null($model->signed_date) || + is_null($model->sent_accounting_date)) + ) { + $fail(__('Le status :status doit être assigné lorsque les 3 dates sont saisies', [ + 'status' => InvoiceStatusEnum::archived->label() + ])); + } + }, + ], + ]); + } //end if return $rules; } } diff --git a/app/Http/Requests/Bo/Sites/StoreSiteRequest.php b/app/Http/Requests/Bo/Sites/StoreSiteRequest.php index e85e410..44b5a2d 100644 --- a/app/Http/Requests/Bo/Sites/StoreSiteRequest.php +++ b/app/Http/Requests/Bo/Sites/StoreSiteRequest.php @@ -27,7 +27,8 @@ class StoreSiteRequest extends FormRequest return [ 'name' => 'required|string|min:3|max:255', 'users' => 'nullable|array', - 'users.*.id' => 'required|exists:users,id' + 'users.*.id' => 'required|exists:users,id', + 'gitlab_project_id' => 'nullable|unique:sites,gitlab_project_id' ]; } @@ -42,6 +43,7 @@ class StoreSiteRequest extends FormRequest 'name' => trans('Nom du site'), 'users' => trans('Utilisateurs associés au site'), 'users.*.id' => trans('Élément parmis les Utilisateurs associés au site'), + 'gitlab_project_id' => trans('ID du dépôt Gitlab') ]; } } diff --git a/app/Http/Requests/Bo/Sites/UpdateSiteRequest.php b/app/Http/Requests/Bo/Sites/UpdateSiteRequest.php index 2f665be..56899c7 100644 --- a/app/Http/Requests/Bo/Sites/UpdateSiteRequest.php +++ b/app/Http/Requests/Bo/Sites/UpdateSiteRequest.php @@ -4,4 +4,15 @@ namespace App\Http\Requests\Bo\Sites; class UpdateSiteRequest extends StoreSiteRequest { + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return array_merge(parent::rules(), [ + 'gitlab_project_id' => 'nullable|unique:sites,gitlab_project_id,' . $this->route('site')->id . ',id', + ]); + } } diff --git a/app/Models/Company.php b/app/Models/Company.php index 06b583b..63cd49d 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Casts\Attribute; /** * @property string $name @@ -21,6 +22,15 @@ class Company extends Model 'name' ]; + /** + * The accessors to append to the model's array form. + * + * @var array + */ + protected $appends = [ + 'value' + ]; + /** * HasMany Invoices * @@ -30,4 +40,16 @@ class Company extends Model { return $this->hasMany(Invoice::class); } + + /** + * Return value attribute for belongsto search component + * + * @return \Illuminate\Database\Eloquent\Casts\Attribute + */ + protected function value(): Attribute + { + return new Attribute( + get: fn () => $this->id, + ); + } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 90abab3..44c5ca3 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -2,7 +2,8 @@ namespace App\Models; -use App\Enums\Invoice\TaskTypeEnum; +use App\Enums\Invoices\TaskTypeEnum; +use App\Enums\Invoices\InvoiceStatusEnum; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Carbon; @@ -26,6 +27,7 @@ use Mccarlosen\LaravelMpdf\LaravelMpdf; * @property Site $site_id * @property Company $company_id * @property Company $invoiced_to_id + * @property InvoiceStatusEnum $status */ class Invoice extends Model { @@ -51,7 +53,8 @@ class Invoice extends Model 'sent_accounting_date', 'site_id', 'company_id', - 'invoiced_to_id' + 'invoiced_to_id', + 'status', ]; /** @@ -64,6 +67,7 @@ class Invoice extends Model 'signed_date' => 'date', 'sent_client_date' => 'date', 'sent_accounting_date' => 'date', + 'status' => InvoiceStatusEnum::class, ]; /** @@ -92,6 +96,7 @@ class Invoice extends Model ->reduce(function ($previous, array $task) { return \floatval($task['amount']) + \floatval($previous); }); + $model->status = InvoiceStatusEnum::opened; }); static::updating(function (self $model) { $model->total_days = collect($model->tasks) @@ -215,4 +220,36 @@ class Invoice extends Model ); return $pdf; } + + /** + * Get the invoice status array depending to the dates entered. + * + * @param \App\Models\Invoice $invoice + * @return array + */ + public static function getInvoiceStatusArray(Invoice $invoice): array + { + $invoiceStatusArray = []; + if ($invoice->signed_date && $invoice->sent_client_date && $invoice->sent_accounting_date) { + return array_merge($invoiceStatusArray, [ + InvoiceStatusEnum::toArray()['opened'], + InvoiceStatusEnum::toArray()['archived'], + ]); + } + if ($invoice->signed_date) { + return array_merge($invoiceStatusArray, [ + InvoiceStatusEnum::toArray()['opened'] + ]); + } + if ($invoice->sent_client_date) { + return array_merge($invoiceStatusArray, [ + InvoiceStatusEnum::toArray()['opened'], + InvoiceStatusEnum::toArray()['refused'], + ]); + } + return array_merge($invoiceStatusArray, [ + InvoiceStatusEnum::toArray()['opened'], + InvoiceStatusEnum::toArray()['canceled'], + ]); + } } diff --git a/app/Models/Message.php b/app/Models/Message.php index ae3887c..41daedd 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -51,7 +51,9 @@ class Message extends Model $q->withTrashed(); }]); /** @var \App\Models\Ticket $ticket */ - $ticket = $message->ticket; + $ticket = $message->ticket; + $ticket->last_activity = $message->updated_at; + $ticket->saveQuietly(); if ($message->user) { $ticket->load(['user' => function ($q) { $q->withTrashed(); diff --git a/app/Models/Site.php b/app/Models/Site.php index 89c2581..d90b579 100644 --- a/app/Models/Site.php +++ b/app/Models/Site.php @@ -4,10 +4,12 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Casts\Attribute; /** - * @property string $name - * @property Brand $brand_id + * @property string $name + * @property integer $gitlab_project_id + * @property Brand $brand_id */ class Site extends Model { @@ -20,6 +22,16 @@ class Site extends Model */ protected $fillable = [ 'name', + 'gitlab_project_id' + ]; + + /** + * The accessors to append to the model's array form. + * + * @var array + */ + protected $appends = [ + 'value' ]; // * RELATIONSHIPS @@ -64,4 +76,16 @@ class Site extends Model { return $this->belongsTo(Project::class); } + + /** + * Return value attribute for belongsto search component + * + * @return \Illuminate\Database\Eloquent\Casts\Attribute + */ + protected function value(): Attribute + { + return new Attribute( + get: fn () => $this->id, + ); + } } diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index ba3b00a..899e558 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -33,7 +33,8 @@ class Ticket extends Model 'type', 'status', 'text', - 'is_internal' + 'is_internal', + 'last_activity', ]; /** @@ -44,7 +45,8 @@ class Ticket extends Model protected $casts = [ 'is_internal' => 'bool', 'type' => TicketTypeEnum::class, - 'status' => TicketStatusEnum::class + 'status' => TicketStatusEnum::class, + 'last_activity' => 'timestamp', ]; /** diff --git a/app/Policies/TicketPolicy.php b/app/Policies/TicketPolicy.php index 1b8c828..bcb8074 100644 --- a/app/Policies/TicketPolicy.php +++ b/app/Policies/TicketPolicy.php @@ -3,6 +3,7 @@ namespace App\Policies; use App\Enums\Tickets\TicketStatusEnum; +use App\Enums\Tickets\TicketTypeEnum; use App\Enums\Users\RoleEnum; use App\Models\Ticket; use App\Models\User; @@ -260,4 +261,19 @@ class TicketPolicy return $user->role->equals(RoleEnum::developper) && $ticket->estimation()->exists(); } + + /** + * Determine if the user can create a gitlab issue from a given ticket. + * + * @param \App\Models\User $user + * @param \App\Models\Ticket $ticket + * @return boolean + */ + public function createGitlabIssue(User $user, Ticket $ticket) + { + return $user->role->equals(RoleEnum::developper) + && ($ticket->type->equals(TicketTypeEnum::bug) || $ticket->type->equals(TicketTypeEnum::evolution)) + && !\is_null($ticket->site->gitlab_project_id) + && \is_null($ticket->gitlab_issue); + } } diff --git a/composer.json b/composer.json index 543b3d2..7e0f231 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "carlos-meneses/laravel-mpdf": "^2.1", "diglactic/laravel-breadcrumbs": "^8.0", "doctrine/dbal": "^3.1", + "graham-campbell/gitlab": "^7.0", "guzzlehttp/guzzle": "^7.2", "laravel-lang/lang": "~8.0", "laravel-validation-rules/colour": "^1.6", @@ -20,7 +21,6 @@ "laravel/tinker": "^2.8", "laravel/ui": "^4.2", "ldc/web-admin-secure-laravel": "^2.0", - "ldc/web-status-checker-laravel": "^1.0", "mews/purifier": "^3.4", "sentry/sentry-laravel": "*", "uwil/k8s-laravel": "^1.2" diff --git a/composer.lock b/composer.lock index 59be3f3..4410ac9 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": "8b8d80affdf1ee455767d5e675bf5fe8", + "content-hash": "4f5fe189b89c5ccc99fc4c7a913a64a6", "packages": [ { "name": "brick/math", @@ -1251,6 +1251,228 @@ ], "time": "2022-02-20T15:07:15+00:00" }, + { + "name": "graham-campbell/bounded-cache", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Bounded-Cache.git", + "reference": "2840f46f38185e610e62af3662dac75ca1877a84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Bounded-Cache/zipball/2840f46f38185e610e62af3662dac75ca1877a84", + "reference": "2840f46f38185e610e62af3662dac75ca1877a84", + "shasum": "" + }, + "require": { + "php": "^8.0.2", + "psr/simple-cache": "^2.0 || ^3.0" + }, + "require-dev": { + "graham-campbell/analyzer": "^4.0", + "graham-campbell/testbench-core": "^4.0", + "mockery/mockery": "^1.5.1", + "phpunit/phpunit": "^9.6.3 || ^10.0.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\BoundedCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "A Bounded TTL PSR-16 Cache Implementation", + "keywords": [ + "Bounded Cache", + "Bounded-Cache", + "Graham Campbell", + "GrahamCampbell", + "bounded", + "cache", + "psr16" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Bounded-Cache/issues", + "source": "https://github.com/GrahamCampbell/Bounded-Cache/tree/v2.1.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/bounded-cache", + "type": "tidelift" + } + ], + "time": "2023-02-25T23:00:30+00:00" + }, + { + "name": "graham-campbell/gitlab", + "version": "v7.0.0", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Laravel-GitLab.git", + "reference": "fb037dedadacb16e4ab75b8bab58b52b703a4e33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Laravel-GitLab/zipball/fb037dedadacb16e4ab75b8bab58b52b703a4e33", + "reference": "fb037dedadacb16e4ab75b8bab58b52b703a4e33", + "shasum": "" + }, + "require": { + "graham-campbell/bounded-cache": "^1.2 || ^2.0", + "graham-campbell/manager": "^5.0", + "guzzlehttp/guzzle": "^7.5", + "guzzlehttp/psr7": "^2.4", + "illuminate/contracts": "^8.75 || ^9.0 || ^10.0", + "illuminate/support": "^8.75 || ^9.0 || ^10.0", + "m4tthumphrey/php-gitlab-api": "11.9.*", + "php": "^7.4.15 || ^8.0.2", + "symfony/cache": "^5.4 || ^6.0" + }, + "require-dev": { + "graham-campbell/analyzer": "^4.0", + "graham-campbell/testbench": "^6.0", + "mockery/mockery": "^1.5.1", + "phpunit/phpunit": "^9.6.3 || ^10.0.12" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "GrahamCampbell\\GitLab\\GitLabServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "GrahamCampbell\\GitLab\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "GitLab Is A GitLab Bridge For Laravel", + "keywords": [ + "Bridge", + "Graham Campbell", + "GrahamCampbell", + "Laravel GitLab", + "Laravel-GitLab", + "PHP GitLab API", + "framework", + "gitlab", + "gitlab bridge", + "laravel", + "php-gitlab-api" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Laravel-GitLab/issues", + "source": "https://github.com/GrahamCampbell/Laravel-GitLab/tree/v7.0.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/gitlab", + "type": "tidelift" + } + ], + "time": "2023-03-07T11:41:34+00:00" + }, + { + "name": "graham-campbell/manager", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Laravel-Manager.git", + "reference": "f1cf95a0bb11ba086387b8049e0e40b82d01588d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Laravel-Manager/zipball/f1cf95a0bb11ba086387b8049e0e40b82d01588d", + "reference": "f1cf95a0bb11ba086387b8049e0e40b82d01588d", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.75 || ^9.0 || ^10.0", + "illuminate/support": "^8.75 || ^9.0 || ^10.0", + "php": "^7.4.15 || ^8.0.2" + }, + "require-dev": { + "graham-campbell/analyzer": "^4.0", + "graham-campbell/testbench-core": "^4.0", + "mockery/mockery": "^1.5.1", + "phpunit/phpunit": "^9.6.3 || ^10.0.12" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\Manager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Manager Provides Some Manager Functionality For Laravel", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Laravel Manager", + "Laravel-Manager", + "connector", + "framework", + "interface", + "laravel", + "manager" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Laravel-Manager/issues", + "source": "https://github.com/GrahamCampbell/Laravel-Manager/tree/v5.0.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/manager", + "type": "tidelift" + } + ], + "time": "2023-02-25T23:53:34+00:00" + }, { "name": "graham-campbell/result-type", "version": "v1.1.1", @@ -2400,113 +2622,6 @@ ], "description": "Restrict access to LDC Network" }, - { - "name": "ldc/web-status-checker", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://lab.s-web.fr/ldc-web/projets/ldc-web-status-checker.git", - "reference": "7ce62449f3eb052de2d7bfa2e32d37d4524d60b0" - }, - "dist": { - "type": "zip", - "url": "https://lab.s-web.fr/api/v4/projects/21/packages/composer/archives/ldc/web-status-checker.zip?sha=7ce62449f3eb052de2d7bfa2e32d37d4524d60b0", - "reference": "7ce62449f3eb052de2d7bfa2e32d37d4524d60b0", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": "~7.3|7.4|~8" - }, - "require-dev": { - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "LDC\\WebStatusChecker\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "LDC\\WebStatusChecker\\Tests\\": "tests" - } - }, - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jérémy Munsch", - "email": "jeremy.munsch@ldc.fr", - "homepage": "https://jeremydev.ovh" - } - ], - "description": "Status checker: report status to ldc-web" - }, - { - "name": "ldc/web-status-checker-laravel", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://lab.s-web.fr/ldc-web/projets/ldc-web-status-checker-laravel.git", - "reference": "3918891846f1e8440ba54b23dc47422f005070b9" - }, - "dist": { - "type": "zip", - "url": "https://lab.s-web.fr/api/v4/projects/22/packages/composer/archives/ldc/web-status-checker-laravel.zip?sha=3918891846f1e8440ba54b23dc47422f005070b9", - "reference": "3918891846f1e8440ba54b23dc47422f005070b9", - "shasum": "" - }, - "require": { - "illuminate/support": "~8|~9|~10", - "laravel/framework": "~8|~9|~10", - "ldc/web-status-checker": "^1.1.0", - "php": "~7.3|7.4|~8" - }, - "require-dev": { - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "LDC\\WebStatusCheckerLaravel\\StatusCheckerServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "LDC\\WebStatusCheckerLaravel\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "LDC\\WebStatusCheckerLaravel\\Tests\\": "tests" - } - }, - "scripts": { - "test": [ - "vendor/bin/phpunit" - ], - "post-autoload-dump": [ - "@php ./vendor/bin/testbench package:discover --ansi" - ] - }, - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jérémy Munsch", - "email": "jeremy.munsch@ldc.fr", - "homepage": "https://jeremydev.ovh" - } - ], - "description": "Status checker: report status to ldc-web" - }, { "name": "league/commonmark", "version": "2.4.0", @@ -2899,6 +3014,96 @@ ], "time": "2022-04-17T13:12:02+00:00" }, + { + "name": "m4tthumphrey/php-gitlab-api", + "version": "11.9.1", + "source": { + "type": "git", + "url": "https://github.com/GitLabPHP/Client.git", + "reference": "148ed10609f92e03afbf756e734ee4b76be36832" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GitLabPHP/Client/zipball/148ed10609f92e03afbf756e734ee4b76be36832", + "reference": "148ed10609f92e03afbf756e734ee4b76be36832", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-xml": "*", + "php": "^7.4.15 || ^8.0.2", + "php-http/cache-plugin": "^1.7.5", + "php-http/client-common": "^2.6", + "php-http/discovery": "^1.15", + "php-http/httplug": "^2.3", + "php-http/multipart-stream-builder": "^1.2", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "psr/http-client-implementation": "^1.0", + "psr/http-factory-implementation": "^1.0", + "psr/http-message": "^1.0", + "symfony/options-resolver": "^4.4 || ^5.0 || ^6.0", + "symfony/polyfill-php80": "^1.26" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "guzzlehttp/guzzle": "^7.5", + "http-interop/http-factory-guzzle": "^1.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "Gitlab\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Bourigault", + "email": "bourigaultfabien@gmail.com", + "homepage": "https://github.com/fbourigault" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Matt Humphrey", + "email": "matth@windsor-telecom.co.uk", + "homepage": "https://github.com/m4tthumphrey" + }, + { + "name": "Miguel Piedrafita", + "email": "github@miguelpiedrafita.com", + "homepage": "https://github.com/m1guelpf" + } + ], + "description": "GitLab API v4 client for PHP", + "keywords": [ + "api", + "gitlab" + ], + "support": { + "issues": "https://github.com/GitLabPHP/Client/issues", + "source": "https://github.com/GitLabPHP/Client/tree/11.9.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + } + ], + "time": "2023-04-30T19:02:00+00:00" + }, { "name": "mews/purifier", "version": "3.4.0", @@ -3779,6 +3984,60 @@ }, "time": "2020-10-15T08:29:30+00:00" }, + { + "name": "php-http/cache-plugin", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/cache-plugin.git", + "reference": "6bf9fbf66193f61d90c2381b75eb1fa0202fd314" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/cache-plugin/zipball/6bf9fbf66193f61d90c2381b75eb1fa0202fd314", + "reference": "6bf9fbf66193f61d90c2381b75eb1fa0202fd314", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/client-common": "^1.9 || ^2.0", + "php-http/message-factory": "^1.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/options-resolver": "^2.6 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Client\\Common\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "PSR-6 Cache plugin for HTTPlug", + "homepage": "http://httplug.io", + "keywords": [ + "cache", + "http", + "httplug", + "plugin" + ], + "support": { + "issues": "https://github.com/php-http/cache-plugin/issues", + "source": "https://github.com/php-http/cache-plugin/tree/1.8.0" + }, + "time": "2023-04-28T10:56:55+00:00" + }, { "name": "php-http/client-common", "version": "2.7.0", @@ -4107,6 +4366,62 @@ "abandoned": "psr/http-factory", "time": "2023-04-14T14:16:17+00:00" }, + { + "name": "php-http/multipart-stream-builder", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/multipart-stream-builder.git", + "reference": "f5938fd135d9fa442cc297dc98481805acfe2b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/f5938fd135d9fa442cc297dc98481805acfe2b6a", + "reference": "f5938fd135d9fa442cc297dc98481805acfe2b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "php-http/discovery": "^1.15", + "psr/http-factory-implementation": "^1.0" + }, + "require-dev": { + "nyholm/psr7": "^1.0", + "php-http/message": "^1.5", + "php-http/message-factory": "^1.0.2", + "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Http\\Message\\MultipartStream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com" + } + ], + "description": "A builder class that help you create a multipart stream", + "homepage": "http://php-http.org", + "keywords": [ + "factory", + "http", + "message", + "multipart stream", + "stream" + ], + "support": { + "issues": "https://github.com/php-http/multipart-stream-builder/issues", + "source": "https://github.com/php-http/multipart-stream-builder/tree/1.3.0" + }, + "time": "2023-04-28T14:10:22+00:00" + }, { "name": "php-http/promise", "version": "1.1.0", @@ -5387,6 +5702,178 @@ ], "time": "2023-02-09T10:38:43+00:00" }, + { + "name": "symfony/cache", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "d176b97600860df13e851538c2df2ad88e9e5ca9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/d176b97600860df13e851538c2df2ad88e9e5ca9", + "reference": "d176b97600860df13e851538c2df2ad88e9e5ca9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.2.10" + }, + "conflict": { + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^2.13.1|^3.0", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-27T16:19:14+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/ad945640ccc0ae6e208bcea7d7de4b39b569896b", + "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, { "name": "symfony/console", "version": "v6.3.0", @@ -7955,6 +8442,80 @@ ], "time": "2023-06-21T12:08:28+00:00" }, + { + "name": "symfony/var-exporter", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "3400949782c0cb5b3e73aa64cfd71dde000beccc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/3400949782c0cb5b3e73aa64cfd71dde000beccc", + "reference": "3400949782c0cb5b3e73aa64cfd71dde000beccc", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-26T17:39:03+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "2.2.6", diff --git a/config/gitlab.php b/config/gitlab.php new file mode 100644 index 0000000..6f8f388 --- /dev/null +++ b/config/gitlab.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + + /** + *-------------------------------------------------------------------------- + * Default Connection Name + *-------------------------------------------------------------------------- + * + * Here you may specify which of the connections below you wish to use as + * your default connection for all work. Of course, you may use many + * connections at once using the manager class. + * + */ + + 'default' => 'main', + + /** + *-------------------------------------------------------------------------- + * GitLab Connections + *-------------------------------------------------------------------------- + * + * Here are each of the connections setup for your application. Example + * configuration has been included, but you may add as many connections as + * you would like. Note that the 4 supported authentication methods are: + * "none", "oauth", "job_token", and "token". + * + */ + + 'connections' => [ + 'main' => [ + 'token' => env('GITLAB_TOKEN'), + 'method' => 'token', + 'url' => env('GITLAB_URL'), + ], + ], + + /** + *-------------------------------------------------------------------------- + * HTTP Cache + *-------------------------------------------------------------------------- + * + * Here are each of the cache configurations setup for your application. + * Only the "illuminate" driver is provided out of the box. Example + * configuration has been included. + * + */ + + 'cache' => [ + + 'main' => [ + 'driver' => 'illuminate', + 'connector' => null, + ], + + 'bar' => [ + 'driver' => 'illuminate', + 'connector' => 'redis', + ], + + ], + +]; diff --git a/database/factories/InvoiceFactory.php b/database/factories/InvoiceFactory.php index 88673c3..d41b89c 100644 --- a/database/factories/InvoiceFactory.php +++ b/database/factories/InvoiceFactory.php @@ -2,7 +2,8 @@ namespace Database\Factories; -use App\Enums\Invoice\TaskTypeEnum; +use App\Enums\Invoices\TaskTypeEnum; +use App\Enums\Invoices\InvoiceStatusEnum; use App\Models\Invoice; use App\Models\Package; use Illuminate\Database\Eloquent\Factories\Factory; @@ -16,18 +17,58 @@ class InvoiceFactory extends Factory */ protected $model = Invoice::class; + /** + * A JSON object, each line has to be contructed using these + * fields description, days, type Invoice\TaskType, amount. + * + * @var array + */ + protected $tasks = []; + + /** + * The total calculated using all tasks. + * + * @var float + */ + protected $total = 0; + + /** + * The total days calculated using all tasks. + * + * @var float + */ + protected $totalDays = 0; + + /** + * The date of the signing. + * + * @var boolean + */ + protected $signedDateBoolean = 0; + + /** + * The date of the shipping to the client. + * + * @var boolean + */ + protected $sentClientDateBoolean = 0; + + /** + * The date of the shipping to the accounting. + * + * @var boolean + */ + protected $sentAccountingDateBoolean = 0; + /** * Define the model's default state. * - * @return array + * @return void */ - public function definition() + private function customData(): void { \mt_srand(); - $lines = \mt_rand(2, 9); - $tasks = []; - $total = 0; - $totalDays = 0; + $lines = \mt_rand(2, 9); for ($i = 0; $i < $lines; $i++) { $days = 0.125 * $this->faker->numberBetween(1, 4); $type = \collect(TaskTypeEnum::cases())->random(); @@ -47,37 +88,54 @@ class InvoiceFactory extends Factory $amount = $days * $this->faker->randomFloat(null, 150, 650); break; } - $totalDays += $days; - $total += $amount; - /** @var \App\Enums\Invoice\TaskTypeEnum */ - $taskType = \collect(TaskTypeEnum::cases())->random(); - $tasks[] = \array_merge(Invoice::getTaskStructure(), [ + $this->totalDays += $days; + $this->total += $amount; + /** @var \App\Enums\Invoices\TaskTypeEnum */ + $this->tasks[] = \array_merge(Invoice::getTaskStructure(), [ 'desc' => $this->faker->words(9, true), 'days' => $days, - 'type' => $taskType->value, + 'type' => \collect(TaskTypeEnum::cases())->random()->value, 'amount' => $amount, 'package' => 1 ]); + /** Dates */ + $this->signedDateBoolean = $this->faker->boolean(25); + $this->sentClientDateBoolean = $this->faker->boolean(25); + $this->sentAccountingDateBoolean = $this->faker->boolean(25); } //end for + } + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + $this->customData(); return [ 'firstname' => $this->faker->firstName, 'lastname' => $this->faker->lastName, 'object' => $this->faker->words(3, true), // * constructed object - 'tasks' => $tasks, + 'tasks' => $this->tasks, // * constructed object result - 'total' => $total, + 'total' => $this->total, // * constructed object result - 'total_days' => $totalDays, + 'total_days' => $this->totalDays, 'comment' => $this->faker->text(\rand(20, 40)), 'show_days' => $this->faker->boolean(75), - 'signed_date' => $this->faker->boolean(25) ? + 'signed_date' => + $this->signedDateBoolean ? $this->faker->dateTimeBetween('-1 year', '+1 year') : null, - 'sent_client_date' => $this->faker->boolean(25) ? + 'sent_client_date' => + $this->signedDateBoolean && $this->sentClientDateBoolean ? $this->faker->dateTimeBetween('-1 year', '+1 year') : null, - 'sent_accounting_date' => $this->faker->boolean(25) ? + 'sent_accounting_date' => + $this->signedDateBoolean && $this->sentClientDateBoolean && $this->sentAccountingDateBoolean ? $this->faker->dateTimeBetween('-1 year', '+1 year') : null, + 'status' => $this->signedDateBoolean && $this->sentClientDateBoolean && $this->sentAccountingDateBoolean ? + InvoiceStatusEnum::archived : InvoiceStatusEnum::opened, ]; } } diff --git a/database/migrations/2022_08_23_085833_create_invoices_table.php b/database/migrations/2022_08_23_085833_create_invoices_table.php index a0db936..c079c23 100644 --- a/database/migrations/2022_08_23_085833_create_invoices_table.php +++ b/database/migrations/2022_08_23_085833_create_invoices_table.php @@ -34,7 +34,7 @@ class CreateInvoicesTable extends Migration $table->text('tasks') ->comment( // phpcs:ignore Generic.Files.LineLength.TooLong - 'A JSON object, each line has to be contructed using these fields description, days, type Invoice\TaskTypeEnum, amount' + 'A JSON object, each line has to be contructed using these fields description, days, type Invoices\TaskTypeEnum, amount' ); $table->unsignedFloat('total')->comment('The total calculated using all tasks'); diff --git a/database/migrations/2023_08_14_100506_add_status_invoices_table.php b/database/migrations/2023_08_14_100506_add_status_invoices_table.php new file mode 100644 index 0000000..4b8d827 --- /dev/null +++ b/database/migrations/2023_08_14_100506_add_status_invoices_table.php @@ -0,0 +1,37 @@ +unsignedTinyInteger('status') + ->default(InvoiceStatusEnum::opened->value) + ->comment('Status of the invoice.') + ->after('invoiced_to_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('invoices', function (Blueprint $table) { + $table->dropColumn('status'); + }); + } +}; diff --git a/database/migrations/2023_08_14_134026_add_gitlab_id_to_sites_table.php b/database/migrations/2023_08_14_134026_add_gitlab_id_to_sites_table.php new file mode 100644 index 0000000..43f46f5 --- /dev/null +++ b/database/migrations/2023_08_14_134026_add_gitlab_id_to_sites_table.php @@ -0,0 +1,36 @@ +unsignedInteger('gitlab_project_id') + ->unique() + ->nullable() + ->comment("ID du dépôt Gitlab") + ->after('name'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('sites', function (Blueprint $table) { + $table->dropColumn('gitlab_project_id'); + }); + } +}; diff --git a/database/migrations/2023_08_14_142748_add_last_activity_tickets_table.php b/database/migrations/2023_08_14_142748_add_last_activity_tickets_table.php new file mode 100644 index 0000000..e4b1128 --- /dev/null +++ b/database/migrations/2023_08_14_142748_add_last_activity_tickets_table.php @@ -0,0 +1,43 @@ +timestamp('last_activity') + ->default(Carbon::now()) + ->comment('Last activity on the ticket.') + ->after('is_internal'); + }); + Ticket::get()->map(function (object $ticket) { + $lastMessage = $ticket->messages->whereNotNull('user_id')->sortByDesc('updated_at')->first(); + $ticket->last_activity = + $lastMessage->updated_at ?? $ticket->updated_at; + $ticket->saveQuietly(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('tickets', function (Blueprint $table) { + $table->dropColumn('last_activity'); + }); + } +}; diff --git a/database/migrations/2023_08_14_151531_add_gitlab_issue_to_tickets_table.php b/database/migrations/2023_08_14_151531_add_gitlab_issue_to_tickets_table.php new file mode 100644 index 0000000..cbb42db --- /dev/null +++ b/database/migrations/2023_08_14_151531_add_gitlab_issue_to_tickets_table.php @@ -0,0 +1,35 @@ +string('gitlab_issue') + ->nullable() + ->comment("URL de l'issue Gitlab") + ->after('is_internal'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('tickets', function (Blueprint $table) { + $table->dropColumn('gitlab_issue'); + }); + } +}; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index 825dbe1..b4f16c9 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -155,6 +155,7 @@ return [ 'excerpt' => 'extrait', 'firstname' => 'prénom', 'gender' => 'genre', + 'gitlab_project_id' => 'id du dépôt Gitlab', 'hour' => 'heure', 'invoiced_to_id' => 'Entité à facturer', 'label' => 'label', diff --git a/resources/ts/bo/components/CkEditorComponent.vue b/resources/ts/bo/components/CkEditorComponent.vue index 529910a..dc84daf 100644 --- a/resources/ts/bo/components/CkEditorComponent.vue +++ b/resources/ts/bo/components/CkEditorComponent.vue @@ -28,15 +28,6 @@ export default defineComponent({ throw new Error("this component requires json"); } }, - mounted() { - const json = String(this.$attrs.json ?? "{}"), - data = JSON.parse(json); - this.name = data.name; - this.internalText = data.text ? String(data.text) : ""; - this.internalId = data.id ? data.id : ""; - this.internalRequired = Boolean(data.required); - this.makeSuperBuild(); - }, data(): { name: string; internalText: string; @@ -57,6 +48,15 @@ export default defineComponent({ chars: 0, }; }, + mounted() { + const json = String(this.$attrs.json ?? "{}"), + data = JSON.parse(json); + this.name = data.name; + this.internalText = data.text ? String(data.text) : ""; + this.internalId = data.id ? data.id : ""; + this.internalRequired = Boolean(data.required); + this.makeSuperBuild(); + }, watch: { dbtext() { if (this.internalText != this.dbtext) { diff --git a/resources/ts/utilities/fontawesome.ts b/resources/ts/utilities/fontawesome.ts index 9e6b5d1..35cf7a7 100644 --- a/resources/ts/utilities/fontawesome.ts +++ b/resources/ts/utilities/fontawesome.ts @@ -8,7 +8,9 @@ import { faArrowRightLong, faArrowUp, faBan, + faBox, faBoxArchive, + faBoxOpen, faChevronLeft, faChevronRight, faClipboard, @@ -43,7 +45,9 @@ import { config.autoReplaceSvg = false; library.add(faTags); library.add(faSquarePlus); +library.add(faBox); library.add(faBoxArchive); +library.add(faBoxOpen); library.add(faEraser); library.add(faSignHanging); library.add(faDiagramProject); diff --git a/resources/views/bo/modules/pagination.blade.php b/resources/views/bo/modules/pagination.blade.php index 9326c83..687ca1b 100644 --- a/resources/views/bo/modules/pagination.blade.php +++ b/resources/views/bo/modules/pagination.blade.php @@ -31,7 +31,7 @@ @foreach (\App\Enums\ItemsPerPaginationEnum::toArray() as $itemsPerPaginationEnum)
  • {{ $itemsPerPaginationEnum->value }} diff --git a/resources/views/bo/pages/invoices/create.blade.php b/resources/views/bo/pages/invoices/create.blade.php index 113c923..5293e1f 100644 --- a/resources/views/bo/pages/invoices/create.blade.php +++ b/resources/views/bo/pages/invoices/create.blade.php @@ -7,7 +7,7 @@ @section('content')
    -
    - + @if($invoiceModel->status->value === \App\Enums\Invoices\InvoiceStatusEnum::opened->value) + @endif
    +@if($invoiceModel->status->value === \App\Enums\Invoices\InvoiceStatusEnum::opened->value)
    @csrf @method('PUT') @@ -53,4 +56,7 @@
    +@else +@include('bo.pages.invoices.form-inputs') +@endif @endsection diff --git a/resources/views/bo/pages/invoices/form-inputs.blade.php b/resources/views/bo/pages/invoices/form-inputs.blade.php index b3e5c3f..731db72 100644 --- a/resources/views/bo/pages/invoices/form-inputs.blade.php +++ b/resources/views/bo/pages/invoices/form-inputs.blade.php @@ -1,41 +1,30 @@
    + {{-- DATES/STATUS --}} @if (request()->routeIs('bo.invoices.edit')) + @if($invoiceModel->status !== \App\Enums\Invoices\InvoiceStatusEnum::opened || $invoiceModel->signed_date) +
    + +
    + @endif {{-- DATES STATUS --}}
    - {{ __('Dates') }} + {{ __('État') }}
    - @if($invoiceModel->signed_date) -
    - -
    - @endif - {{-- SIGNED_DATE --}} -
    - @if(!$invoiceModel->signed_date) - @include('bo.partials.inputs.timestamp-datetime', [ - 'id' => 'invoiceSigneddate', - 'label' => __('Date d\'envoi du :model', [ - 'model' => __('models.classes.invoices'), - ]), - 'name' => 'signed_date', - 'defaultValue' => false, - 'required' => true, - 'model' => $invoiceModel ?? null, - 'helper' => __('Doît être une date réelle et existante.'), - ]) - @else -

    {{ __('Envoyé le :date', ['date' => $invoiceModel->signed_date]) }}

    - @endif -
    {{-- SENT_CLIENT_DATE --}}
    - @if(!$invoiceModel->sent_client_date) - @include('bo.partials.inputs.timestamp-datetime', [ + @if($invoiceModel->status === \App\Enums\Invoices\InvoiceStatusEnum::opened AND !$invoiceModel->sent_client_date ) + @include('bo.partials.inputs.date', [ 'id' => 'invoiceSentclientdate', - 'label' => __('Date de signature du :model (client)', [ + 'label' => __('Date d\'envoi du :model', [ 'model' => __('models.classes.invoices'), ]), 'name' => 'sent_client_date', @@ -44,16 +33,38 @@ 'model' => $invoiceModel ?? null, 'helper' => __('Doît être une date réelle et existante.'), ]) - @else -

    {{ __('Signé le :date (client)', ['date' => $invoiceModel->sent_client_date]) }}

    + @elseif ($invoiceModel->sent_client_date) +

    + {{ __('Envoyé le :date', ['date' => \Carbon\Carbon::parse($invoiceModel->sent_client_date)->format('d/m/Y')]) ?? "xxx" }} +

    + @endif +
    + {{-- SIGNED_DATE --}} +
    + @if($invoiceModel->status === \App\Enums\Invoices\InvoiceStatusEnum::opened AND !$invoiceModel->signed_date) + @include('bo.partials.inputs.date', [ + 'id' => 'invoiceSigneddate', + 'label' => __('Date de signature du :model (client)', [ + 'model' => __('models.classes.invoices'), + ]), + 'name' => 'signed_date', + 'defaultValue' => false, + 'required' => true, + 'model' => $invoiceModel ?? null, + 'helper' => __('Doît être une date réelle et existante.'), + ]) + @elseif ($invoiceModel->signed_date) +

    + {{ __('Signé le :date (client)', ['date' => \Carbon\Carbon::parse($invoiceModel->signed_date)->format('d/m/Y') ?? "xxx"]) }} +

    @endif
    {{-- SENT_ACCOUNTING_DATE --}}
    - @if(!$invoiceModel->sent_accounting_date) - @include('bo.partials.inputs.timestamp-datetime', [ + @if($invoiceModel->status === \App\Enums\Invoices\InvoiceStatusEnum::opened AND !$invoiceModel->sent_accounting_date) + @include('bo.partials.inputs.date', [ 'id' => 'invoiceSentaccountingdate', - 'label' => __('Date d\'envoi du :model à la compta', [ + 'label' => __('Date d\'envoi du :model à la comptabilité', [ 'model' => __('models.classes.invoices'), ]), 'name' => 'sent_accounting_date', @@ -62,17 +73,40 @@ 'model' => $invoiceModel ?? null, 'helper' => __('Doît être une date réelle et existante.'), ]) - @else -

    {{ __('Envoyé à la compta le :date', ['date' => $invoiceModel->sent_accounting_date]) }}

    + @elseif ($invoiceModel->sent_accounting_date) +

    + {{ __('Envoyé à la comptabilité le :date', ['date' => \Carbon\Carbon::parse($invoiceModel->sent_accounting_date)->format('d/m/Y') ?? "xxx"]) }} +

    @endif
    + {{-- STATUS --}} + @if ($invoiceModel->status === \App\Enums\Invoices\InvoiceStatusEnum::opened) +
    + @include('bo.partials.inputs.tinyint-enum', [ + 'id' => 'invoiceStatus', + 'label' => __('crud.minicrud.associated', [ + 'entity' => 'État', + ]), + 'name' => 'status', + 'model' => $invoiceModel ?? null, + 'required' => true, + 'associatedModels' => $invoiceStatusArray, + 'helper' => __('crud.minicrud.select', [ + 'entity' => 'État', + ]), + ]) + @else +
    +

    {{ $invoiceModel->status->label() }}

    + @endif +
    @endif {{-- USER ASSOCIATED --}}
    -
    signed_date) disabled @endif> +
    sent_client_date) disabled @endif> {{ __('Informations sur la personne associée') }}
    {{-- FIRSTNAME --}} @@ -126,7 +160,7 @@
    {{-- OBJECT --}}
    -
    signed_date) disabled @endif> +
    sent_client_date) disabled @endif> {{ Str::ucfirst(__('validation.attributes.object')) }}
    @@ -156,7 +190,7 @@
    {{-- TASKS --}}
    -
    signed_date) disabled @endif> +
    sent_client_date) disabled @endif> {{ __('validation.attributes.tasks') }}
    @@ -164,7 +198,7 @@ $data = [ 'companies' => $companyModels, 'packages' => $packageModels, - 'taskTypeEnums' => collect(\App\Enums\Invoice\TaskTypeEnum::toArray())->values(), + 'taskTypeEnums' => collect(\App\Enums\Invoices\TaskTypeEnum::toArray())->values(), 'taskRowModel' => $taskRowModel, 'tasks' => $invoiceModel->tasks ?? [] ]; @@ -176,10 +210,13 @@
    {{-- COMMENT --}}
    -
    signed_date) disabled @endif> +
    {{ Str::ucfirst(__('validation.attributes.comment')) }}
    + @if($invoiceModel->sent_client_date) + + @else @include('bo.partials.inputs.text-html', [ 'id' => 'invoiceComment', 'label' => __('Commentaire du :model', [ @@ -187,18 +224,19 @@ ]), 'name' => 'comment', 'model' => $invoiceModel, - 'required' => true, + 'required' => false, 'helper' => __('crud.minicrud.text_warning', [ 'number' => 15 ]), ]) + @endif
    {{-- SITE_ID --}}
    -
    signed_date) disabled @endif> +
    sent_client_date) disabled @endif> {{ Str::singular(trans('models.classes.sites')) }}
    @@ -212,7 +250,7 @@ 'routeName' => 'invoices.json-paginate', 'modelRelated' => $siteModels ?? [], 'modelSelected' => $invoiceModel->site ?? [], - 'readonly' => ($invoiceModel->signed_date) ? true : false, + 'readonly' => ($invoiceModel->sent_client_date) ? true : false, 'placeholder' => __('Rechercher une :model', [ 'model' => Str::singular(trans('models.classes.sites')), ]), @@ -227,7 +265,7 @@
    {{-- COMPANY_ID --}}
    -
    signed_date) disabled @endif> +
    sent_client_date) disabled @endif> {{ Str::singular(trans('models.classes.companies')) }}
    @@ -241,7 +279,7 @@ 'routeName' => 'invoices.json-paginate', 'modelRelated' => $companyModels ?? [], 'modelSelected' => $invoiceModel->company ?? [], - 'readonly' => ($invoiceModel->signed_date) ? true : false, + 'readonly' => ($invoiceModel->sent_client_date) ? true : false, 'placeholder' => __('Rechercher une :model', [ 'model' => Str::singular(trans('models.classes.companies')), ]), @@ -268,7 +306,7 @@ 'routeName' => 'invoices.json-paginate', 'modelRelated' => $companyModels ?? [], 'modelSelected' => $invoiceModel->companyInvoiced ?? [], - 'readonly' => ($invoiceModel->signed_date) ? true : false, + 'readonly' => ($invoiceModel->sent_client_date) ? true : false, 'placeholder' => __('Rechercher une :model', [ 'model' => Str::singular(trans('models.classes.companies')), ]), @@ -283,7 +321,7 @@
    {{-- SHOW DAYS --}}
    -
    signed_date) disabled @endif> +
    sent_client_date) disabled @endif> {{ __('Visibilité') }}
    @@ -302,6 +340,7 @@
    +@if($invoiceModel->status === \App\Enums\Invoices\InvoiceStatusEnum::opened) @push('scripts') @endpush +@endif diff --git a/resources/views/bo/pages/invoices/index.blade.php b/resources/views/bo/pages/invoices/index.blade.php index f15ce37..1881c1e 100644 --- a/resources/views/bo/pages/invoices/index.blade.php +++ b/resources/views/bo/pages/invoices/index.blade.php @@ -6,13 +6,29 @@ @section('content')
    @include('breadcrumbs.breadcrumb-body') - - - +
    +
    + + @push('scripts') + + @endpush + + + +
    +
    @include('bo.modules.search-bar') diff --git a/resources/views/bo/pages/invoices/show.blade.php b/resources/views/bo/pages/invoices/show.blade.php index 69c0280..29a322b 100644 --- a/resources/views/bo/pages/invoices/show.blade.php +++ b/resources/views/bo/pages/invoices/show.blade.php @@ -7,7 +7,7 @@ @section('content') @push('scripts') diff --git a/resources/views/bo/pages/tickets/index.blade.php b/resources/views/bo/pages/tickets/index.blade.php index 41550c1..172473f 100644 --- a/resources/views/bo/pages/tickets/index.blade.php +++ b/resources/views/bo/pages/tickets/index.blade.php @@ -22,7 +22,7 @@ $userPr = Auth::user()->sites ?? collect([]); @endcan   {{ __('ticket') }} - @if(session('tickets.st') == \App\Enums\Tickets\TicketStatusEnum::archived) + @if(session('tickets.st') == \App\Enums\Tickets\TicketStatusEnum::archived->value) @else @@ -172,7 +172,7 @@ $userPr = Auth::user()->sites ?? collect([]); diff --git a/resources/views/bo/pages/tickets/show.blade.php b/resources/views/bo/pages/tickets/show.blade.php index f448edd..8fa0819 100644 --- a/resources/views/bo/pages/tickets/show.blade.php +++ b/resources/views/bo/pages/tickets/show.blade.php @@ -256,4 +256,18 @@ @endif +@can('createGitlabIssue', $ticket) + + @csrf + + +@else + @if (!\is_null($ticket->gitlab_issue)) +
    +

    + {{ __("Lien vers l'issue Gitlab : ")}} + {{ __("Ouvrir") }} +

    + @endif +@endcan @endsection diff --git a/resources/views/bo/partials/inputs/date.blade.php b/resources/views/bo/partials/inputs/date.blade.php new file mode 100644 index 0000000..652dede --- /dev/null +++ b/resources/views/bo/partials/inputs/date.blade.php @@ -0,0 +1,15 @@ + + +@include('modules.input-error', [ + 'inputName' => $name, + 'helperId' => "{$id}Help", + 'helper' => $helper +]) diff --git a/resources/views/breadcrumbs/breadcrumb-layout.blade.php b/resources/views/breadcrumbs/breadcrumb-layout.blade.php index 2cba069..e9ff09d 100644 --- a/resources/views/breadcrumbs/breadcrumb-layout.blade.php +++ b/resources/views/breadcrumbs/breadcrumb-layout.blade.php @@ -1,5 +1,5 @@ @if (count($breadcrumbs)) -
    @foreach ($ticket->labels as $label) - {{ $label->name }} + {{ $label->name }} @endforeach