diff --git a/app/Console/Commands/UpdateGitlabDatas.php b/app/Console/Commands/UpdateGitlabDatas.php
index e85a6c1..27da89a 100644
--- a/app/Console/Commands/UpdateGitlabDatas.php
+++ b/app/Console/Commands/UpdateGitlabDatas.php
@@ -2,14 +2,16 @@
namespace App\Console\Commands;
+use Carbon\Carbon;
+use App\Models\Ticket;
use App\Models\GitlabIssues;
use App\Models\GitlabLabels;
-use App\Models\GitlabMilestones;
use App\Models\GitlabProjects;
-use Carbon\Carbon;
-use GrahamCampbell\GitLab\Facades\GitLab;
use Illuminate\Console\Command;
+use App\Models\GitlabMilestones;
use Illuminate\Support\Facades\Cache;
+use App\Enums\Tickets\TicketStatusEnum;
+use GrahamCampbell\GitLab\Facades\GitLab;
class UpdateGitlabDatas extends Command
{
@@ -48,6 +50,8 @@ class UpdateGitlabDatas extends Command
$this->updateMilestonesByProject($project);
$this->updateIssuesByProject($project, $isCron);
}
+
+ $this->syncTicketStatus();
}
/**
@@ -191,6 +195,7 @@ class UpdateGitlabDatas extends Command
);
$issueModel = GitlabIssues::where('gitlab_id', $issue['id'])->firstOrFail();
+ $labelIds = [];
foreach ($issue['labels'] as $labelName) {
$label = Cache::remember("gitlab_label_{$labelName}", 300, function () use ($labelName) {
@@ -198,9 +203,11 @@ class UpdateGitlabDatas extends Command
});
if ($label) {
- $issueModel->gitlabLabels()->syncWithoutDetaching($label);
+ $labelIds[] = $label;
}
}
+
+ $issueModel->gitlabLabels()->sync($labelIds);
}//end foreach
$this->info("Gitlab issues upsert complete.");
@@ -257,4 +264,44 @@ class UpdateGitlabDatas extends Command
$this->info("Gitlab milestones upsert complete.");
}
+
+ /**
+ * Synchronizes ticket statuses based on GitLab issue labels.
+ *
+ * @return void
+ */
+ private function syncTicketStatus(): void
+ {
+ $tickets = Ticket::whereNotNull('gitlab_issue')->whereNot('status', TicketStatusEnum::archived)->get();
+
+ foreach ($tickets as $ticket) {
+ $gitlabIssue = GitlabIssues::where('url', $ticket->gitlab_issue)->with('gitlabLabels')->first();
+
+ $gitlabIssueLabels = $gitlabIssue ? $gitlabIssue->gitlabLabels->pluck('name') : [];
+
+ if ($gitlabIssueLabels) {
+ match (true) {
+ $gitlabIssueLabels->contains('g-wip') => $this->updateTicketStatus($ticket, TicketStatusEnum::inDev),
+ $gitlabIssueLabels->contains('g-production') => $this->updateTicketStatus($ticket, TicketStatusEnum::production),
+ default => null,
+ };
+ }
+ }
+ }
+
+ /**
+ * Updates the ticket status only if it has changed.
+ *
+ * @param Ticket $ticket The ticket to update.
+ * @param TicketStatusEnum $status The new status to apply.
+ *
+ * @return void
+ */
+ private function updateTicketStatus(Ticket $ticket, TicketStatusEnum $status): void
+ {
+ if ($ticket->status !== $status && $ticket->status !== TicketStatusEnum::waiting) {
+ $ticket->status = $status;
+ $ticket->saveOrFail();
+ }
+ }
}
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index f226000..6fca931 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -28,7 +28,7 @@ class Kernel extends ConsoleKernel
$schedule->job(ExportTicketsState::class)->weekly()->mondays()->at('07:00');
$schedule->job(AutoCloseOldTickets::class)->dailyAt('08:00');
$schedule->job(WarnBeforeAutoCloseTicket::class)->dailyAt('08:05');
- $schedule->command('app:update-gitlab-datas --is-cron')->twiceDaily(1, 13);
+ $schedule->command('app:update-gitlab-datas --is-cron')->hourly()->between('7:00', '19:00');
}
/**
diff --git a/app/Enums/Invoices/InvoiceStatusEnum.php b/app/Enums/Invoices/InvoiceStatusEnum.php
index f9e0163..117fb38 100644
--- a/app/Enums/Invoices/InvoiceStatusEnum.php
+++ b/app/Enums/Invoices/InvoiceStatusEnum.php
@@ -8,10 +8,12 @@ enum InvoiceStatusEnum: int
{
use BaseEnum;
- case opened = 1;
- case refused = 2;
- case canceled = 3;
- case archived = 4;
+ case opened = 1;
+ case refused = 2;
+ case canceled = 3;
+ case archived = 4;
+ case awaiting_signature = 5;
+ case awaiting_invoicing = 6;
/**
* Optionnal labels definition.
@@ -21,5 +23,7 @@ enum InvoiceStatusEnum: int
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é)",
+ self::awaiting_signature->name => "En attente de signature",
+ self::awaiting_invoicing->name => "En attente de facturation",
];
}
diff --git a/app/Enums/Tickets/TicketStatusEnum.php b/app/Enums/Tickets/TicketStatusEnum.php
index 744be98..b14de01 100644
--- a/app/Enums/Tickets/TicketStatusEnum.php
+++ b/app/Enums/Tickets/TicketStatusEnum.php
@@ -17,6 +17,7 @@ enum TicketStatusEnum: int
case recetteValidated = 7;
case production = 8;
case archived = 9;
+ case inDev = 10;
/**
* Optionnal labels definition
@@ -24,13 +25,14 @@ enum TicketStatusEnum: int
private const LABELS = [
self::new->name => "Nouveau",
self::estimateTodo->name => "Étude chef(s) de projet",
- self::takenInAccount->name => "Pris en compte",
+ self::takenInAccount->name => "À planifier",
self::waiting->name => "En attente de réponse",
self::waitingValidation->name => "En attente de validation",
self::recette->name => "En recettage",
self::recetteValidated->name => "Recettage validé",
self::production->name => "En production",
self::archived->name => "Archivé",
+ self::inDev->name => "En cours de développement",
];
/**
@@ -46,6 +48,7 @@ enum TicketStatusEnum: int
self::recetteValidated->name => 'text-success',
self::production->name => 'text-success',
self::archived->name => 'text-secondary',
+ self::inDev->name => 'text-discovery',
];
/**
diff --git a/app/Enums/Users/GitlabUsersEnum.php b/app/Enums/Users/GitlabUsersEnum.php
deleted file mode 100644
index 43cbcdd..0000000
--- a/app/Enums/Users/GitlabUsersEnum.php
+++ /dev/null
@@ -1,24 +0,0 @@
-name => "Default",
- ];
-}
diff --git a/app/Http/Controllers/Bo/InvoiceController.php b/app/Http/Controllers/Bo/InvoiceController.php
index 716a754..ded9c35 100644
--- a/app/Http/Controllers/Bo/InvoiceController.php
+++ b/app/Http/Controllers/Bo/InvoiceController.php
@@ -31,10 +31,21 @@ class InvoiceController extends Controller
? Invoice::query()->orderByDesc('created_at')
: Invoice::query();
- $status = intval($request->status);
- $invoiceModels = $invoiceModels->when($status, function ($query) use ($status) {
- $query->where('status', $status);
- });
+ $status = intval($request->status);
+
+ switch ($status) {
+ case InvoiceStatusEnum::awaiting_signature->value:
+ $invoiceModels->whereNotNull('sent_client_date')->whereNull('signed_date');
+ break;
+ case InvoiceStatusEnum::awaiting_invoicing->value:
+ $invoiceModels->whereNotNull('sent_client_date')->whereNotNull('signed_date')->whereNull('sent_accounting_date');
+ break;
+ default:
+ if ($status !== 0) {
+ $invoiceModels->where('status', $status);
+ }
+ break;
+ }
/** @var string $search Search field */
$search = $request->search;
diff --git a/app/Http/Controllers/Bo/TicketController.php b/app/Http/Controllers/Bo/TicketController.php
index 7053cb0..001889b 100644
--- a/app/Http/Controllers/Bo/TicketController.php
+++ b/app/Http/Controllers/Bo/TicketController.php
@@ -2,37 +2,37 @@
namespace App\Http\Controllers\Bo;
-use App\Enums\Tickets\TicketStatusEnum;
-use App\Enums\Tickets\TicketTypeEnum;
-use App\Enums\Users\GitlabUsersEnum;
+use App\Models\Site;
+use App\Models\User;
+use App\Models\Ticket;
+use App\Mail\NewTicket;
+use App\Models\Message;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Str;
+use Illuminate\Http\Request;
+use App\Jobs\UpdateIssueLabels;
+use App\Lib\Helpers\ToolboxHelper;
+use Illuminate\Support\Facades\DB;
use App\Events\NotifySubscriptions;
use App\Events\TicketStatusChanged;
use App\Http\Controllers\Controller;
-use App\Http\Requests\Bo\AssignTicketToUserRequest;
-use App\Http\Requests\Bo\ChangeTicketStatus;
-use App\Http\Requests\Bo\CreateMessage;
+use App\Http\Requests\Bo\LinkTicket;
+use Illuminate\Support\Facades\Mail;
+use App\Enums\Tickets\TicketTypeEnum;
use App\Http\Requests\Bo\SortTickets;
use App\Http\Requests\Bo\StoreTicket;
-use App\Http\Requests\Bo\UpdateChangeOrderRequest;
-use App\Http\Requests\Bo\UpdateMessage;
use App\Http\Requests\Bo\UpdateTicket;
-use App\Http\Requests\Bo\LinkTicket;
-use App\Http\Requests\Bo\UpdateTicketPriorityRequest;
use App\Lib\Helpers\FileStorageHelper;
-use App\Lib\Helpers\ToolboxHelper;
-use App\Mail\NewTicket;
-use App\Models\Message;
-use App\Models\Site;
-use App\Models\Ticket;
-use App\Models\User;
+use App\Enums\Tickets\TicketStatusEnum;
+use App\Http\Requests\Bo\CreateMessage;
+use App\Http\Requests\Bo\UpdateMessage;
+use Illuminate\Support\Facades\Session;
use GrahamCampbell\GitLab\Facades\GitLab;
use Illuminate\Database\Eloquent\Builder;
-use Illuminate\Http\Request;
-use Illuminate\Support\Arr;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Mail;
-use Illuminate\Support\Facades\Session;
-use Illuminate\Support\Str;
+use App\Http\Requests\Bo\ChangeTicketStatus;
+use App\Http\Requests\Bo\UpdateChangeOrderRequest;
+use App\Http\Requests\Bo\AssignTicketToUserRequest;
+use App\Http\Requests\Bo\UpdateTicketPriorityRequest;
class TicketController extends Controller
{
@@ -313,6 +313,10 @@ class TicketController extends Controller
*/
public function changeStatus(ChangeTicketStatus $request, Ticket $ticket)
{
+ if ($ticket->gitlab_issue) {
+ \dispatch(new UpdateIssueLabels($ticket, $request->status));
+ }
+
$ticket->fill($request->validated());
$ticket->saveOrFail();
@@ -745,17 +749,19 @@ class TicketController extends Controller
*/
public function createGitlabIssue(Ticket $ticket)
{
+ $userGitlabId = auth()->user()->gitlab_id;
$gitlabRepoId = $ticket->site->gitlab_project_id;
$description = \sprintf("%s
", $ticket->text);
$cLabel = match ($ticket->type) {
- TicketTypeEnum::bug => 'c-bug',
- TicketTypeEnum::evolution, TicketTypeEnum::newsletter => 'c-feature',
+ TicketTypeEnum::bug => 'c-bug,to-dispatch',
+ TicketTypeEnum::evolution => 'c-feature',
+ TicketTypeEnum::newsletter => 'c-feature,g-to-estimate',
};
$assigneeId = match ($ticket->type) {
- TicketTypeEnum::bug => GitlabUsersEnum::default->value,
+ TicketTypeEnum::bug => $userGitlabId,
TicketTypeEnum::evolution,
- TicketTypeEnum::newsletter => $ticket->site->projectManager?->gitlab_id ?? GitlabUsersEnum::default->value
+ TicketTypeEnum::newsletter => $ticket->site->projectManager?->gitlab_id
};
try {
@@ -766,7 +772,7 @@ class TicketController extends Controller
}
}
- $issue = GitLab::issues()->create($gitlabRepoId, [
+ $data = [
'title' => sprintf(
'ts-%s : %s',
$ticket->id,
@@ -777,8 +783,13 @@ class TicketController extends Controller
'c-support',
$cLabel
]),
- 'assignee_id' => $assigneeId,
- ]);
+ ];
+
+ if ($assigneeId) {
+ $data['assignee_id'] = $assigneeId;
+ }
+
+ $issue = GitLab::issues()->create($gitlabRepoId, $data);
} catch (\Exception $e) {
\report($e);
return back()->with('error', \trans("Une erreur est survenue"));
diff --git a/app/Jobs/UpdateIssueLabels.php b/app/Jobs/UpdateIssueLabels.php
new file mode 100644
index 0000000..127c1cb
--- /dev/null
+++ b/app/Jobs/UpdateIssueLabels.php
@@ -0,0 +1,85 @@
+ticket = $ticket;
+ $this->requestStatus = $requestStatus;
+ }
+
+ /**
+ * Execute the job.
+ */
+ public function handle(): void
+ {
+ $oldStatus = $this->ticket->status;
+ $requestStatus = (int) $this->requestStatus;
+ $gitlabIssue = $this->getGitlabIssue($this->ticket->site->gitlab_project_id, $this->ticket->gitlab_issue);
+
+ $issueLabels = $gitlabIssue['labels'];
+
+ if ($oldStatus === TicketStatusEnum::waiting && $requestStatus !== TicketStatusEnum::waiting->value) {
+ $issueLabels = array_values(array_diff($issueLabels, ['g-wait']));
+ }
+
+ switch ($requestStatus) {
+ case TicketStatusEnum::takenInAccount->value:
+ $newLabels = ['g-to-dispatch'];
+ $updatedLabels = array_unique(array_merge($issueLabels, $newLabels));
+
+ $updatedIssue = $this->updateGitlabIssueLabels($gitlabIssue['project_id'], $gitlabIssue['iid'], $updatedLabels);
+ break;
+ case TicketStatusEnum::waiting->value:
+ $newLabels = ['g-wait'];
+ $updatedLabels = array_unique(array_merge($issueLabels, $newLabels));
+
+ $updatedIssue = $this->updateGitlabIssueLabels($gitlabIssue['project_id'], $gitlabIssue['iid'], $updatedLabels);
+ break;
+ case TicketStatusEnum::waitingValidation->value:
+ $removeLabels = ['g-to-estimate'];
+ $updatedLabels = array_diff($issueLabels, $removeLabels);
+
+ $updatedIssue = $this->updateGitlabIssueLabels($gitlabIssue['project_id'], $gitlabIssue['iid'], $updatedLabels);
+ break;
+ case TicketStatusEnum::recetteValidated->value:
+ $newLabels = ['g-re7-ok', 'to-prod'];
+ $updatedLabels = array_unique(array_merge($issueLabels, $newLabels));
+
+ $updatedIssue = $this->updateGitlabIssueLabels($gitlabIssue['project_id'], $gitlabIssue['iid'], $updatedLabels);
+ break;
+ case TicketStatusEnum::archived->value:
+ $updatedIssue = $this->closeGitlabIssue($gitlabIssue['project_id'], $gitlabIssue['iid'], $issueLabels);
+ break;
+ default:
+ $updatedIssue = $this->updateGitlabIssueLabels($gitlabIssue['project_id'], $gitlabIssue['iid'], $issueLabels);
+ break;
+ }
+ }
+}
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index 80e4285..6cf2c8f 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -111,6 +111,10 @@ class Invoice extends Model
->reduce(function ($previous, array $task) {
return \floatval($task['amount']) + \floatval($previous);
});
+
+ if($model->sent_accounting_date && $model->status !== InvoiceStatusEnum::archived->value) {
+ $model->status = InvoiceStatusEnum::archived;
+ }
});
static::deleting(function () {
throw new \Error('Action impossible');
diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php
index b96617b..11fb8b6 100644
--- a/app/Models/Ticket.php
+++ b/app/Models/Ticket.php
@@ -2,23 +2,25 @@
namespace App\Models;
-use App\Enums\Users\RoleEnum;
-use App\Enums\Tickets\TicketStatusEnum;
-use App\Enums\Tickets\TicketTypeEnum;
use App\Events\NotifyAdmin;
-use App\Events\NotifySubscriptions;
-use App\Events\TicketStatusChanged;
-use App\Jobs\SendTicketMentionMail;
use App\Mail\TicketMention;
+use Illuminate\Support\Str;
+use App\Models\GitlabIssues;
+use App\Enums\Users\RoleEnum;
use App\Models\Credit\Account;
use App\Models\Credit\Estimation;
use App\Models\Ticket\TicketType;
-use Illuminate\Database\Eloquent\Factories\HasFactory;
-use Illuminate\Database\Eloquent\Model;
-use Illuminate\Database\Eloquent\ModelNotFoundException;
-use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
-use Illuminate\Support\Str;
+use App\Events\NotifySubscriptions;
+use App\Events\TicketStatusChanged;
+use App\Jobs\SendTicketMentionMail;
+use App\Enums\Tickets\TicketTypeEnum;
+use App\Enums\Tickets\TicketStatusEnum;
+use Illuminate\Database\Eloquent\Model;
+use GrahamCampbell\GitLab\Facades\GitLab;
+use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
class Ticket extends Model
{
@@ -75,23 +77,20 @@ class Ticket extends Model
if (
!$ticket->original['status'] !== $ticket->status->value && $ticket->original['order'] === $ticket->order
) {
+ $user = auth()->user();
+
event(new TicketStatusChanged(
$ticket,
TicketStatusEnum::make($ticket->original['status']),
- auth()->user(),
+ $user,
null,
false
));
- event(new NotifySubscriptions($ticket, sprintf(
- '%s a modifié le statut en %s',
- auth()->user()->name,
- $ticket->status->label()
- )));
- event(new NotifyAdmin($ticket, sprintf(
- '%s a modifié le statut en %s',
- auth()->user()->name,
- $ticket->status->label()
- )));
+
+ $message = $user ? sprintf('%s a modifié le statut en %s', $user->name, $ticket->status->label()) : sprintf('Le statut a été modifié en %s', $ticket->status->label());
+
+ event(new NotifySubscriptions($ticket, $message));
+ event(new NotifyAdmin($ticket, $message));
}
if ($ticket->status->equals(TicketStatusEnum::archived)) {
self::setOrder($ticket, true);
diff --git a/app/Traits/Controllers/HasGitlabIssue.php b/app/Traits/Controllers/HasGitlabIssue.php
new file mode 100644
index 0000000..a664f83
--- /dev/null
+++ b/app/Traits/Controllers/HasGitlabIssue.php
@@ -0,0 +1,59 @@
+show($projectId, (int) $matches[1]);
+
+ return $gitlabIssue;
+ }
+
+ /**
+ * Update labels for a GitLab issue.
+ *
+ * @param int|string $projectId
+ * @param int $issueId
+ * @param array $labels
+ *
+ * @return array
+ */
+ public function updateGitlabIssueLabels($projectId, int $issueId, array $labels): array
+ {
+ return GitLab::issues()->update($projectId, $issueId, [
+ 'labels' => implode(',', $labels),
+ ]);
+ }
+
+ /**
+ * Close a GitLab issue.
+ *
+ * @param int|string $projectId
+ * @param int $issueId
+ * @param array $labels
+ *
+ * @return array
+ */
+ public function closeGitlabIssue($projectId, int $issueId, array $labels): array
+ {
+ return GitLab::issues()->update($projectId, $issueId, [
+ 'state_event' => 'close',
+ 'labels' => implode(',', $labels),
+ ]);
+ }
+}
diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php
index a06cab5..bc330f4 100644
--- a/resources/lang/en/validation.php
+++ b/resources/lang/en/validation.php
@@ -223,6 +223,7 @@ return [
'default' => 'default',
'date_mep' => 'Desired Launch',
'published' => 'Publication',
- 'all' => 'All'
+ 'all' => 'All',
+ 'estimate' => 'estimate',
]
];
diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php
index 25eb798..a0d2279 100644
--- a/resources/lang/fr/validation.php
+++ b/resources/lang/fr/validation.php
@@ -259,6 +259,7 @@ return [
'default' => 'défaut',
'date_mep' => 'MEP souhaitée',
'published' => 'publication',
- 'all' => 'Tout'
+ 'all' => 'Tout',
+ 'estimate' => 'estimation',
]
];
diff --git a/resources/views/bo/pages/tickets/index.blade.php b/resources/views/bo/pages/tickets/index.blade.php
index 48dcfad..208d498 100644
--- a/resources/views/bo/pages/tickets/index.blade.php
+++ b/resources/views/bo/pages/tickets/index.blade.php
@@ -228,6 +228,11 @@
'status' => Str::ucfirst(__('validation.attributes.status')),
'title' => Str::ucfirst(__('validation.attributes.name')),
]);
+ if (session()->has('tickets.ty') && intval(session('tickets.ty')) === \App\Enums\Tickets\TicketTypeEnum::evolution->value) {
+ $cols = array_merge($cols, [
+ 'estimate' => Str::ucfirst(__('validation.custom.estimate')),
+ ]);
+ }
if (Auth::user()->isAdmin()) {
$cols = array_merge($cols, [
'tickets' => Str::ucfirst(__('validation.custom.tickets')),
@@ -241,7 +246,7 @@
@endphp
@include('bo.modules.table-col-sorter', [
'cols' => $cols,
- 'ignore' => session('tickets.or') ? array_keys($cols) : ['id', 'site', 'status', 'tickets'],
+ 'ignore' => session('tickets.or') ? array_keys($cols) : ['id', 'site', 'status', 'tickets', 'estimate'],
])