Merge branch 'issue-19' into 'develop'

add status on invoices - issue #19

See merge request ldc-web/projets/support-web!53
This commit is contained in:
Vincent BRIDIER 2023-08-14 17:48:26 +00:00
commit c155e48d78
22 changed files with 419 additions and 110 deletions

View file

@ -0,0 +1,25 @@
<?php
namespace App\Enums\Invoices;
use App\Traits\Enums\BaseEnum;
enum InvoiceStatusEnum: int
{
use BaseEnum;
case opened = 1;
case refused = 2;
case canceled = 3;
case archived = 4;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::opened->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é)",
];
}

View file

@ -1,6 +1,6 @@
<?php
namespace App\Enums\Invoice;
namespace App\Enums\Invoices;
use App\Traits\Enums\BaseEnum;

View file

@ -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)

View file

@ -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'),
];
}
}

View file

@ -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;
}
}

View file

@ -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,
);
}
}

View file

@ -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'],
]);
}
}

View file

@ -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
@ -22,6 +23,15 @@ class Site extends Model
'name',
];
/**
* The accessors to append to the model's array form.
*
* @var array
*/
protected $appends = [
'value'
];
// * RELATIONSHIPS
/**
@ -64,4 +74,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,
);
}
}

View file

@ -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,
];
}
}

View file

@ -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');

View file

@ -0,0 +1,37 @@
<?php
use App\Enums\Invoices\InvoiceStatusEnum;
use App\Models\Invoice;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('invoices', function (Blueprint $table) {
$table->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');
});
}
};

View file

@ -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) {

View file

@ -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);

View file

@ -7,7 +7,7 @@
@section('content')
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-3">
<div class="d-flex flex-row align-items-center">
<a href="{{ route('bo.invoices.index') }}"
<a href="{{ route('bo.invoices.index', ['status' => \App\Enums\Invoices\InvoiceStatusEnum::opened]) }}"
class="btn btn-primary text-decoration-none m-0 me-2"
data-bs-toggle="tooltip"
data-bs-placement="top"

View file

@ -7,7 +7,7 @@
@section('content')
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-3">
<div class="d-flex flex-row align-items-start">
<a href="{{ route('bo.invoices.index') }}"
<a href="{{ route('bo.invoices.index', ['status' => \App\Enums\Invoices\InvoiceStatusEnum::opened]) }}"
class="btn btn-primary text-decoration-none m-0 me-2"
data-bs-toggle="tooltip"
data-bs-placement="top"
@ -25,6 +25,7 @@
title="{{ __('crud.helpers.view_model', ['model' => __('models.classes.invoices')]) }}">
<i class="fa-solid fa-eye"></i>
</a>
@if($invoiceModel->status->value === \App\Enums\Invoices\InvoiceStatusEnum::opened->value)
<button id="formSubmitClone"
type="submit"
class="btn btn-primary"
@ -33,9 +34,11 @@
title="{{ __('crud.helpers.save_model', ['model' => __('models.classes.invoices')]) }}">
{{ __('crud.actions.save') }}
</button>
@endif
</div>
</div>
</div>
@if($invoiceModel->status->value === \App\Enums\Invoices\InvoiceStatusEnum::opened->value)
<form action="{{ route('bo.invoices.update', $invoiceModel->id) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
@ -53,4 +56,7 @@
</div>
</div>
</form>
@else
@include('bo.pages.invoices.form-inputs')
@endif
@endsection

View file

@ -1,41 +1,30 @@
<div class="row">
{{-- DATES/STATUS --}}
@if (request()->routeIs('bo.invoices.edit'))
@if($invoiceModel->status !== \App\Enums\Invoices\InvoiceStatusEnum::opened || $invoiceModel->signed_date)
<div class="col-12">
<div class="alert alert-secondary text-white" role="alert">
<strong>
@if($invoiceModel->status !== \App\Enums\Invoices\InvoiceStatusEnum::opened)
{{ __('Impossible de mettre à jour les informations d\'un devis :status.', ['status' => $invoiceModel->status->label()]) }}
@elseif($invoiceModel->signed_date)
{{ __('Impossible de mettre à jour les informations d\'un devis déjà signé, seule la section "État" est prise en compte') }}
@endif
</strong>
</div>
</div>
@endif
{{-- DATES STATUS --}}
<div class="col-12 mb-3">
<fieldset class="row border w-100 h-100 bg-white mx-auto p-3">
<legend class="p-0">{{ __('Dates') }}</legend>
<legend class="p-0">{{ __('État') }}</legend>
<div class="row w-100 mx-auto p-0">
@if($invoiceModel->signed_date)
<div class="col-12 p-0 pe-md-3">
<div class="alert alert-secondary text-white" role="alert">
<strong>{{ __('Impossible de mettre à jour les informations d\'un devis déjà signé, seules les dates sont prises en compte !') }}</strong>
</div>
</div>
@endif
{{-- SIGNED_DATE --}}
<div class="col-12 col-sm-6 col-md-4 p-0 pe-md-3">
@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
<p class="fw-bold text-center m-0">{{ __('Envoyé le :date', ['date' => $invoiceModel->signed_date]) }}</p>
@endif
</div>
{{-- SENT_CLIENT_DATE --}}
<div class="col-12 col-sm-6 col-md-4 p-0 pe-md-3">
@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
<p class="fw-bold text-center m-0">{{ __('Signé le :date (client)', ['date' => $invoiceModel->sent_client_date]) }}</p>
@elseif ($invoiceModel->sent_client_date)
<p class="fw-bold text-center m-0">
{{ __('Envoyé le :date', ['date' => \Carbon\Carbon::parse($invoiceModel->sent_client_date)->format('d/m/Y')]) ?? "xxx" }}
</p>
@endif
</div>
{{-- SIGNED_DATE --}}
<div class="col-12 col-sm-6 col-md-4 p-0 pe-md-3">
@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)
<p class="fw-bold text-center m-0">
{{ __('Signé le :date (client)', ['date' => \Carbon\Carbon::parse($invoiceModel->signed_date)->format('d/m/Y') ?? "xxx"]) }}
</p>
@endif
</div>
{{-- SENT_ACCOUNTING_DATE --}}
<div class="col-12 col-sm-6 col-md-4 p-0 pe-md-3">
@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
<p class="fw-bold text-center m-0">{{ __('Envoyé à la compta le :date', ['date' => $invoiceModel->sent_accounting_date]) }}</p>
@elseif ($invoiceModel->sent_accounting_date)
<p class="fw-bold text-center m-0">
{{ __('Envoyé à la comptabilité le :date', ['date' => \Carbon\Carbon::parse($invoiceModel->sent_accounting_date)->format('d/m/Y') ?? "xxx"]) }}
</p>
@endif
</div>
{{-- STATUS --}}
@if ($invoiceModel->status === \App\Enums\Invoices\InvoiceStatusEnum::opened)
<div class="col-12 col-md-6 p-0 pe-md-3">
@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
<div class="col-12 p-0 pe-md-3">
<p class="fw-bold text-warning text-center m-0 mt-3">{{ $invoiceModel->status->label() }}</p>
@endif
</div>
</div>
</fieldset>
</div>
@endif
{{-- USER ASSOCIATED --}}
<div class="col-12 mb-3">
<fieldset class="row border w-100 h-100 bg-white mx-auto p-3" @if($invoiceModel->signed_date) disabled @endif>
<fieldset class="row border w-100 h-100 bg-white mx-auto p-3" @if($invoiceModel->sent_client_date) disabled @endif>
<legend class="p-0">{{ __('Informations sur la personne associée') }}</legend>
<div class="row w-100 mx-auto p-0">
{{-- FIRSTNAME --}}
@ -126,7 +160,7 @@
</div>
{{-- OBJECT --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3" @if($invoiceModel->signed_date) disabled @endif>
<fieldset class="border h-100 bg-white p-3" @if($invoiceModel->sent_client_date) disabled @endif>
<legend>{{ Str::ucfirst(__('validation.attributes.object')) }}</legend>
<div class="row">
<div class="col-12">
@ -156,7 +190,7 @@
</div>
{{-- TASKS --}}
<div class="col-12 pb-3">
<fieldset class="border bg-white h-100 p-3" @if($invoiceModel->signed_date) disabled @endif>
<fieldset class="border bg-white h-100 p-3" @if($invoiceModel->sent_client_date) disabled @endif>
<legend>{{ __('validation.attributes.tasks') }}</legend>
<div class="row">
<div class="col-12">
@ -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 @@
</div>
{{-- COMMENT --}}
<div class="col-12 pb-3">
<fieldset class="border bg-white h-100 p-3" @if($invoiceModel->signed_date) disabled @endif>
<fieldset class="border bg-white h-100 p-3">
<legend>{{ Str::ucfirst(__('validation.attributes.comment')) }}</legend>
<div class="row mb-3">
<div class="col-12">
@if($invoiceModel->sent_client_date)
<textarea class="form-control" disabled>{{ $invoiceModel->comment }}</textarea>
@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
</div>
</div>
</fieldset>
</div>
{{-- SITE_ID --}}
<div class="col-12 col-md-6 pb-3">
<fieldset class="border bg-white h-100 p-3" @if($invoiceModel->signed_date) disabled @endif>
<fieldset class="border bg-white h-100 p-3" @if($invoiceModel->sent_client_date) disabled @endif>
<legend>{{ Str::singular(trans('models.classes.sites')) }}</legend>
<div class="row">
<div class="col-12">
@ -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 @@
</div>
{{-- COMPANY_ID --}}
<div class="col-12 col-md-6 pb-3">
<fieldset class="border bg-white h-100 p-3" @if($invoiceModel->signed_date) disabled @endif>
<fieldset class="border bg-white h-100 p-3" @if($invoiceModel->sent_client_date) disabled @endif>
<legend>{{ Str::singular(trans('models.classes.companies')) }}</legend>
<div class="row">
<div class="col-12">
@ -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 @@
</div>
{{-- SHOW DAYS --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3" @if($invoiceModel->signed_date) disabled @endif>
<fieldset class="border h-100 bg-white p-3" @if($invoiceModel->sent_client_date) disabled @endif>
<legend>{{ __('Visibilité') }}</legend>
<div class="row">
<div class="col-12">
@ -302,6 +340,7 @@
</div>
</div>
@if($invoiceModel->status === \App\Enums\Invoices\InvoiceStatusEnum::opened)
@push('scripts')
<script>
document.addEventListener("DOMContentLoaded", () => {
@ -315,3 +354,4 @@
});
</script>
@endpush
@endif

View file

@ -6,13 +6,29 @@
@section('content')
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center">
@include('breadcrumbs.breadcrumb-body')
<a href="{{ route('bo.invoices.create') }}"
class="btn btn-primary float-right"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{{ __('crud.helpers.create_model', ['model' => Str::singular(__('models.classes.invoices'))]) }}">
<i class="fa-solid fa-plus"></i>
</a>
<form id="invoiceStatus" action="{{ route('bo.invoices.index') }}">
<div class="input-group justify-content-end">
<select class="form-select w-fit" name="status" id="selectInvoiceStatus">
@foreach (\App\Enums\Invoices\InvoiceStatusEnum::toArray() as $invoiceStatusEnum)
<option value="{{ $invoiceStatusEnum->value }}" @if($status === $invoiceStatusEnum->value) selected @endif>
{{ $invoiceStatusEnum->label }}
</option>
@endforeach
</select>
@push('scripts')
<script nonce="{{ $nonce }}">document.getElementById('selectInvoiceStatus').addEventListener('change', function (e) {
document.getElementById('invoiceStatus').submit();
});</script>
@endpush
<a href="{{ route('bo.invoices.create') }}"
class="btn btn-primary float-right"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{{ __('crud.helpers.create_model', ['model' => Str::singular(__('models.classes.invoices'))]) }}">
<i class="fa-solid fa-plus"></i>
</a>
</div>
</form>
</div>
@include('bo.modules.search-bar')
<table class="table table-hover">

View file

@ -7,7 +7,7 @@
@section('content')
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-3">
<div class="d-flex flex-row align-items-start">
<a href="{{ route('bo.invoices.index') }}"
<a href="{{ route('bo.invoices.index', ['status' => \App\Enums\Invoices\InvoiceStatusEnum::opened]) }}"
class="btn btn-primary text-decoration-none m-0 me-2"
data-bs-toggle="tooltip"
data-bs-placement="top"

View file

@ -0,0 +1,15 @@
<label for="{{ $id }}">
<b>{{ $label }}</b>
</label>
<input id="{{ $id }}"
name="{{ $name }}"
class="form-control"
type="date"
data-form-type="date"
aria-describedby="{{ "{$id}Help" }}"
value="{{ old($name, ((isset($model) and $model->{$name}) ? ($model->{$name}->format('Y-m-d') ?? ($defaultValue ? Carbon\Carbon::today() : '')) : ($defaultValue ? Carbon\Carbon::today() : ''))) }}">
@include('modules.input-error', [
'inputName' => $name,
'helperId' => "{$id}Help",
'helper' => $helper
])

View file

@ -1,5 +1,5 @@
@if (count($breadcrumbs))
<ol class="breadcrumb flex-nowrap border-0 w-100 p-0 m-0">
<ol class="breadcrumb flex-nowrap border-0 w-fit p-0 m-0">
@foreach ($breadcrumbs as $breadcrumb)
@if ($breadcrumb->url && !$loop->last)
<li class="breadcrumb-item d-flex h2 fw-bold m-0">

View file

@ -31,7 +31,7 @@
<a class="dropdown-item @if(Route::is('bo.projects.*'))active @endif" href="{{ route('bo.projects.index') }}">{{ __('models.classes.projects') }}</a>
@endcan
@can('isAdmin')
<a class="dropdown-item @if(Route::is('bo.invoices.*'))active @endif" href="{{ route('bo.invoices.index') }}">{{ __('models.classes.invoices') }}</a>
<a class="dropdown-item @if(Route::is('bo.invoices.*'))active @endif" href="{{ route('bo.invoices.index', ['status' => \App\Enums\Invoices\InvoiceStatusEnum::opened]) }}">{{ __('models.classes.invoices') }}</a>
@endcan
@can('isAdmin')
<a class="dropdown-item @if(Route::is('bo.companies.*'))active @endif" href="{{ route('bo.companies.index') }}">{{ __('models.classes.companies') }}</a>

View file

@ -1,5 +1,6 @@
<?php
use App\Enums\Invoices\InvoiceStatusEnum;
use App\Models\Brand;
use App\Models\Company;
use App\Models\Dutyhour;
@ -98,7 +99,7 @@ Breadcrumbs::for('bo.companies.edit', function (Generator $trail, Company $compa
// * INVOICES
Breadcrumbs::for('bo.invoices.index', function (Generator $trail) {
$trail->push(__('models.classes.invoices'), route('bo.invoices.index'));
$trail->push(__('models.classes.invoices'), route('bo.invoices.index', ['status' => InvoiceStatusEnum::opened]));
});
Breadcrumbs::for('bo.invoices.create', function (Generator $trail) {
$trail->parent('bo.invoices.index');