Company + Invoice + Package + Enum TaskType Models and seeder

This commit is contained in:
Jérémy Munsch 2022-08-23 11:19:17 +02:00
parent 8a607030a4
commit b1f0f0c885
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
17 changed files with 814 additions and 6 deletions

View file

@ -0,0 +1,73 @@
<?php
namespace App\Enums\Invoice;
use Kwaadpepper\Enum\BaseEnumRoutable;
/**
* @method static self web()
* @method static self sio()
* @method static self package()
* @method static self arbitrary()
*/
class TaskType extends BaseEnumRoutable
{
/**
* Enum values
*
* @return array
*/
protected static function values(): array
{
return [
'web' => 1,
'sio' => 2,
'package' => 3,
'arbitrary' => 4,
];
}
/**
* Enum labels
*
* @return array
*/
protected static function labels(): array
{
return [
'web' => trans('Web'),
'sio' => trans('Sio'),
'package' => trans('Forfait'),
'arbitrary' => trans('Libre'),
];
}
/**
* Enum prices
*
* @return array<string,int>
*/
protected static function prices(): array
{
return [
'web' => 460,
'sio' => 690,
// * Linked to packages table
'package' => 0,
// * Arbitrary number
'arbitrary' => 0,
];
}
/**
* @param string $attribute
* @return integer|string
*/
public function __get(string $attribute)
{
if ($attribute === 'price') {
return $this->prices()[$this->getDefinition()];
}
return parent::__get($attribute);
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Kwaadpepper\CrudPolicies\Traits\CrudController;
class CompanyController extends Controller
{
use CrudController;
/** @var \App\Models\MacroSchedule\Marker */
public static $modelClass = Package::class;
/**
* Crud Layout
*
* @var string
*/
public static $layout = 'layout.app';
}

View file

@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class InvoiceController extends Controller
{
//
}

61
app/Models/Company.php Normal file
View file

@ -0,0 +1,61 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
class Company extends Model
{
use HasFactory;
use IsCrudModel;
/**
* Crud label column
*
* @var string
*/
protected $crudLabelColumn = 'name';
/**
* Crud value column
*
* @var string
*/
protected $crudValueColumn = 'id';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name'
];
/**
* Company
*
* @param array $attributes
*/
public function __construct(array $attributes = [])
{
self::$editableProperties = [
'name' => [
'label' => trans('Nom'),
'type' => CrudType::string(),
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::update(),
CrudAction::create()
]
],
];
parent::__construct($attributes);
}
}

314
app/Models/Invoice.php Normal file
View file

@ -0,0 +1,314 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
class Invoice extends Model
{
use HasFactory;
use IsCrudModel;
/**
* Crud label column
*
* @var string
*/
protected $crudLabelColumn = 'ref';
/**
* Crud value column
*
* @var string
*/
protected $crudValueColumn = 'id';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'ref', 'firstname', 'lastname',
'object', 'tasks', 'total', 'total_days',
'comment', 'show_days', 'signed_date',
'sent_client_date', 'sent_accounting_date',
'site_id', 'company_id', 'invoiced_to_id'
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'tasks' => 'array'
];
/**
* Invoice
*
* @param array $attributes
*/
public function __construct(array $attributes = [])
{
self::$editableProperties = [
'ref' => [
'label' => trans('Réference'),
'type' => CrudType::string(),
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::view()
]
],
'firstname' => [
'label' => trans('Prénom'),
'type' => CrudType::string(),
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'lastname' => [
'label' => trans('Nom'),
'type' => CrudType::string(),
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'object' => [
'label' => trans('Object'),
'type' => CrudType::string(),
'nullable' => false,
'actions' => [
// * Truncated on view any
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'tasks' => [
'label' => trans('Tâches'),
'type' => CrudType::json(),
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'total' => [
'label' => trans('Montant total'),
'type' => CrudType::float(),
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'total_days' => [
'label' => trans('Montant total jours'),
'type' => CrudType::float(),
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'comment' => [
'label' => trans('Commentaire'),
'type' => CrudType::text(),
'nullable' => true,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'show_days' => [
'label' => trans('Afficher les jours'),
'type' => CrudType::boolean(),
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'signed_date' => [
'label' => trans('Date de signature'),
'type' => CrudType::datetime(),
'nullable' => true,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'sent_client_date' => [
'label' => trans('Date d\'envoi client'),
'type' => CrudType::datetime(),
'nullable' => true,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'sent_accounting_date' => [
'label' => trans('Date d\'envoi compta'),
'type' => CrudType::datetime(),
'nullable' => true,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'site_id' => [
'label' => trans('models.classes.sites'),
'type' => CrudType::belongsTo(),
'belongsTo' => Site::class,
'belongsToLabel' => 'site',
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'company_id' => [
'label' => trans('models.classes.companies'),
'type' => CrudType::belongsTo(),
'belongsTo' => Company::class,
'belongsToLabel' => 'companie',
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'invoiced_to_id' => [
'label' => trans('models.classes.companies'),
'type' => CrudType::belongsTo(),
'belongsTo' => Company::class,
'belongsToLabel' => 'entité',
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'created_at' => [
'label' => trans('Crée le'),
'type' => CrudType::datetime(),
'nullable' => false,
'actions' => [
CrudAction::view(),
]
],
'updated_at' => [
'label' => trans('Mis à jour'),
'type' => CrudType::datetime(),
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
]
],
];
parent::__construct($attributes);
}
/**
* The attributes that are mass assignable.
*
* @return void
*/
protected static function booted()
{
static::creating(function (self $model) {
$model->ref = \sprintf(
'%s_%s_%s',
Carbon::now()->isoFormat('Y-m'),
Str::padLeft(Invoice::whereYear('created_at', Carbon::now()->isoFormat('Y'))
->whereMonth('created_at', Carbon::now()->isoFormat('M'))
->count() ?: 1, 4, '0'),
Str::substr(Str::upper(Str::slug(User::query()->inRandomOrder()->first()->name)), 0, 2)
);
});
}
// * METHODS
/**
* Get the tasks line structure skel
*
* @return array<string,string|float>
*/
public static function getTaskStructure(): array
{
return [
// * string
'desc' => '',
// * usigned float
'days' => '',
// * Invoice\TaskType
'type' => '',
// * usigned float
'amount' => '',
];
}
// * RELATIONSHIPS
/**
* BelongsTo Site
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function site()
{
return $this->BelongsTo(Site::class);
}
/**
* BelongsTo Company
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function company()
{
return $this->BelongsTo(Company::class);
}
/**
* BelongsTo Company
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function companyInvoiced()
{
return $this->BelongsTo(Company::class, 'invoiced_to_id');
}
}

View file

@ -7,13 +7,11 @@ use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
use Kwaadpepper\CrudPolicies\Enums\CrudType;
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
use Kwaadpepper\Enum\Traits\CastsEnums;
class Package extends Model
{
use HasFactory;
use IsCrudModel;
use CastsEnums;
/**
* Indicates if the model should be timestamped.
@ -46,7 +44,7 @@ class Package extends Model
];
/**
* File
* Package
*
* @param array $attributes
*/
@ -65,7 +63,7 @@ class Package extends Model
]
],
'amount' => [
'label' => trans('Montant'),
'label' => trans('Montant (par jour)'),
'type' => CrudType::float(),
'nullable' => false,
'actions' => [

View file

@ -0,0 +1,38 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Policies\Rules\UserRoleRules as URR;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
class CompanyPolicy extends RootPolicy
{
use HandlesAuthorization;
/**
* Init policies on call
*
* @param \App\Models\User $user
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
*/
public function setPolicies(User $user, Model $model = null)
{
// phpcs:enable
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'company' => [
'viewAny' => URR::make('userIsAdmin', $user),
'create' => URR::make('userIsAdmin', $user),
'update' => URR::make('userIsAdmin', $user),
'delete' => URR::make('userIsAdmin', $user)
]
];
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Policies\Rules\UserRoleRules as URR;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
class InvoicePolicy extends RootPolicy
{
use HandlesAuthorization;
/**
* Init policies on call
*
* @param \App\Models\User $user
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
*/
public function setPolicies(User $user, Model $model = null)
{
// phpcs:enable
/**
* This first rule to return true in a policy action authorises the action
*/
$this->policies = [
'invoice' => [
'viewAny' => URR::make('userIsAdmin', $user),
'create' => URR::make('userIsAdmin', $user),
'update' => URR::make('userIsAdmin', $user),
'delete' => URR::make('userIsAdmin', $user)
]
];
}
}

View file

@ -3,9 +3,11 @@
namespace App\Providers;
use App\Models\Brand;
use App\Models\Company;
use App\Models\Credit\Account;
use App\Models\Credit\CreditLog;
use App\Models\File;
use App\Models\Invoice;
use App\Models\Label;
use App\Models\Log;
use App\Models\MacroSchedule\Marker;
@ -89,6 +91,8 @@ class RouteServiceProvider extends ServiceProvider
Route::model('label', Label::class);
Route::model('marker', Marker::class);
Route::model('package', Package::class);
Route::model('company', Company::class);
Route::model('invoice', Invoice::class);
Route::model('file', File::class);
Route::model('log', Log::class);
Route::model('user', User::class);

View file

@ -0,0 +1,28 @@
<?php
namespace Database\Factories;
use App\Models\Company;
use Illuminate\Database\Eloquent\Factories\Factory;
class CompanyFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Company::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
];
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace Database\Factories;
use App\Enums\Invoice\TaskType;
use App\Models\Invoice;
use App\Models\Package;
use Illuminate\Database\Eloquent\Factories\Factory;
class InvoiceFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Invoice::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
\mt_srand();
$lines = \mt_rand(2, 9);
$tasks = [];
$total = 0;
$totalDays = 0;
for ($i = 0; $i < $lines; $i++) {
$days = 0.125 * $this->faker->numberBetween(1, 4);
$type = \collect(TaskType::toArray())->random();
$amount = 0;
switch ($type) {
case TaskType::web():
$amount = $days * $type->price;
break;
case TaskType::sio():
$amount = $days * $type->price;
break;
case TaskType::package():
$amount = $days * (\optional(Package::query()->inRandomOrder()->first())->amount ??
$this->faker->randomFloat(null, 150, 650));
break;
case TaskType::package():
$amount = $days * $this->faker->randomFloat(null, 150, 650);
break;
}
$totalDays += $days;
$total += $amount;
$tasks[] = \array_merge(Invoice::getTaskStructure(), [
'desc' => $this->faker->words(9, true),
'days' => $days,
'type' => \collect(TaskType::toArray())->random()->value,
'amount' => $amount,
]);
} //end for
return [
'firstname' => $this->faker->firstName,
'lastname' => $this->faker->lastName,
'object' => $this->faker->words(3, true),
// * constructed object
'tasks' => $tasks,
// * constructed object result
'total' => $total,
// * constructed object result
'total_days' => $totalDays,
'comment' => $this->faker->text(\rand(20, 40)),
'show_days' => $this->faker->boolean(75),
'signed_date' => $this->faker->boolean(25) ?
$this->faker->dateTimeBetween('-1 year', '+1 year') : null,
'sent_client_date' => $this->faker->boolean(25) ?
$this->faker->dateTimeBetween('-1 year', '+1 year') : null,
'sent_accounting_date' => $this->faker->boolean(25) ?
$this->faker->dateTimeBetween('-1 year', '+1 year') : null,
];
}
}

View file

@ -17,7 +17,7 @@ class CreatePackagesTable extends Migration
$table->id();
$table->string('name');
$table->unsignedFloat('amount');
$table->unsignedFloat('amount')->comment('The cost per day');
});
}

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCompaniesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('companies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('companies');
}
}

View file

@ -0,0 +1,73 @@
<?php
use App\Models\Company;
use App\Models\Site;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateInvoicesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('invoices', function (Blueprint $table) {
$table->id();
$table->string('ref')
->comment(sprintf(
'%s. %s. %s. %s.',
'The invoice reference built as YYYY_MM_INCREMENT_USER',
'INCREMENT is the N invoice of the month',
'USER is the first two letters of the user who last edited the invoice',
'This field has to generated only on create'
));
$table->string('firstname')->comment('The person to which the invoice is addressed.');
$table->string('lastname')->comment('The person to which the invoice is addressed.');
$table->string('object')->comment('The invoice object.');
$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\TaskType, amount'
);
$table->unsignedFloat('total')->comment('The total calculated using all tasks');
$table->unsignedFloat('total_days')->comment('The total days calculated using all tasks');
$table->text('comment')->nullable()
->comment('Any comment joined to the invoice');
$table->boolean('show_days')->comment('Show days on the printed invoice');
$table->timestamp('signed_date')->nullable()
->comment('The date the invoice was signed');
$table->timestamp('sent_client_date')->nullable()
->comment('The date the invoice was send to the client');
$table->timestamp('sent_accounting_date')->nullable()
->comment('The date the invoice was send to the client');
$table->foreignIdFor(Site::class)->constrained();
$table->foreignIdFor(Company::class)->constrained();
$table->foreignId('invoiced_to_id')
->constrained((new Company())->getTable());
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('invoices');
}
}

View file

@ -2,9 +2,15 @@
namespace Database\Seeders;
use App\Models\Company;
use App\Models\Invoice;
use App\Models\Package;
use App\Models\Site;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class InvoicingSeeder extends Seeder
{
@ -15,8 +21,34 @@ class InvoicingSeeder extends Seeder
*/
public function run()
{
/** Reset all invoices */
DB::table((new Invoice())->getTable())->truncate();
/** Reset all packages (forfaits) */
DB::table((new Package())->getTable())->truncate();
Package::all()->map(function (Package $model) {
$model->deleteOrFail();
});
Package::factory(6)->create();
/** Reset all companies */
Company::all()->map(function (Company $model) {
$model->deleteOrFail();
});
Company::factory(6)->create();
Invoice::factory(45)->make()->each(function (Invoice $model) {
$model->ref = \sprintf(
'%s_%s_%s',
Carbon::now()->isoFormat('Y-m'),
Str::padLeft(Invoice::whereYear('created_at', Carbon::now()->isoFormat('Y'))
->whereMonth('created_at', Carbon::now()->isoFormat('M'))
->count() ?: 1, 4, '0'),
Str::substr(Str::upper(Str::slug(User::query()->inRandomOrder()->first()->name)), 0, 2)
);
$model->site()->associate(Site::query()->inRandomOrder()->first());
$model->company()->associate(Company::query()->inRandomOrder()->first());
$model->companyInvoiced()->associate(Company::query()->inRandomOrder()->first());
$model->saveQuietly();
});
}
}

View file

@ -14,5 +14,7 @@ return [
'brands' => 'Brands',
'labels' => 'Labels',
'packages' => 'Packages',
'invoices' => 'Invoices',
'companies' => 'Companies',
]
];

View file

@ -14,5 +14,7 @@ return [
'brands' => 'Marques',
'labels' => 'Labels',
'packages' => 'Forfaits',
'invoices' => 'Factures',
'companies' => 'Compagnies',
]
];