refactor contracts

shift-build-2464
Nadim Salloum 2021-05-14 10:09:46 +02:00
parent 87ee800b73
commit 210d9cb831
22 changed files with 279 additions and 122 deletions

View File

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

View File

@ -28,8 +28,8 @@ class CarController extends Controller
'id' => $car->id,
'stammnummer' => $car->stammnummer,
'vin' => $car->vin,
'bought_at' => $car->bought_at,
'buy_price' => $car->buy_price->format(),
'buy_price' => $car->latestSellerContract() ? $car->latestSellerContract()->price : '',
// 'buy_price' => $car->buy_price->format(),
// 'seller' => $car->seller->only('name'),
// 'buyer' => $car->buyer->only('name'),
'car_model' => $car->carModel->only('name'),

View File

@ -19,10 +19,16 @@ class ContactController extends Controller
*/
public function index(Request $request)
{
$sortby = $request->only('sortby') ?: 'lastname';
$direction = $request->only('direction') ?: 'asc';
return Inertia::render('Contacts/Index', [
'filters' => $request->all('search', 'trashed'),
'sort' => [
'by' => $sortby,
'direction' => $direction,
],
'contacts' => Contact::filter($request->only('search', 'trashed'))
->orderByName()
->orderBy($sortby, $direction)
->paginate(50)
->withQueryString()
->through(fn ($contact) => [
@ -95,23 +101,24 @@ class ContactController extends Controller
'city' => $contact->city,
'country' => $contact->country,
'deleted_at' => $contact->deleted_at,
'bought_cars' => $contact->contracts()
'bought_cars' => $contact->buyContracts()
->with('car')
->paginate(10)
->through(fn ($contract) => [
'sold_at' => $contract->sold_at,
'sell_price' => $contract->sell_price,
'date' => $contract->date,
'price' => $contract->price,
'name' => $contract->car->name,
'link' => route('cars.edit', $contract->car),
'insurance_type' => InsuranceType::fromValue((int)$contract->insurance_type)->key,
]),
'sold_cars' => $contact->soldCars()
'sold_cars' => $contact->sellContracts()
->with('car')
->paginate(10)
->through(fn ($car) => [
'bought_at' => $car->bought_at,
'buy_price' => $car->buy_price,
'name' => $car->name,
'link' => route('cars.edit', $car),
->through(fn ($contract) => [
'date' => $contract->date,
'price' => $contract->price,
'name' => $contract->car->name,
'link' => route('cars.edit', $contract->car),
]),
]
]);

View File

@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Carbon\Carbon;
use Cknow\Money\Money;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class BuyContract extends Model
{
use HasFactory, softDeletes;
protected $fillable = [
'date',
'price',
'contact_id',
'car_id',
];
public function getDateAttribute($date)
{
return Carbon::parse($date)->format('d.m.Y');
}
public function getPriceAttribute($price)
{
return Money::CHF($price)->format();
}
public function contact()
{
return $this->belongsTo(Contact::class);
}
public function car()
{
return $this->belongsTo(Car::class);
}
}

View File

@ -21,9 +21,6 @@ class Car extends Model
'initial_date',
'last_check_date',
'kilometers',
'bought_at',
'buy_price',
'seller_contact_id',
'car_model_id',
];
@ -39,21 +36,10 @@ class Car extends Model
return $kilometers;
}
public function getStammnummerAttribute($stammnummer)
{
$out = substr($stammnummer, 0, 3);
$out .= '.';
$out .= substr($stammnummer, 3, 3);
$out .= '.';
$out .= substr($stammnummer, 6, 3);
return $out;
}
public function getBuyPriceAttribute($price)
{
return Money::CHF($price);
}
// public function getBuyPriceAttribute()
// {
// return Money::CHF($this->buyContracts()->price);
// }
public function getInitialDateAttribute($initialDate)
{
@ -69,41 +55,66 @@ class Car extends Model
{
return $this->belongsTo(CarModel::class);
}
public function seller()
public function latestSellerContract()
{
return $this->belongsTo(Contact::class, 'seller_contact_id');
return $this->sellContracts()->latest('date')->first();
}
public function buyer()
public function latestBuyerContracts()
{
return $this->hasOneThrough(Contract::class, Contact::class);
return $this->buyContracts()->latest('date')->first();
}
public function contract()
public function isUnsold()
{
return $this->hasOne(Contract::class);
return $this->sellers()->count() > $this->buyers()->count();
}
public function isSold()
{
return $this->sellers()->count() == $this->buyers()->count();
}
public function sellers()
{
return $this->hasManyThrough(SellContract::class, Contact::class);
}
public function buyers()
{
return $this->hasManyThrough(BuyContract::class, Contact::class);
}
public function buyContracts()
{
return $this->hasMany(buyContract::class);
}
public function sellContracts()
{
return $this->hasMany(sellContract::class);
}
public function carPayment()
{
return $this->hasManyThrough(CarPayment::class, Contract::class);
return $this->hasManyThrough(CarPayment::class, SellContract::class);
}
public function scopeSoldThisYear($query)
{
return $query->whereDate('sold_at', '>=', Carbon::today()->format('Y'));
}
// public function scopeSoldThisYear($query)
// {
// return $query->whereDate('sold_at', '>=', Carbon::today()->format('Y'));
// }
public function scopeSoldCars($query)
{
return $query->whereDate('sold_at', '>=', Carbon::today()->format('Y'));
}
// public function scopeSoldCars($query)
// {
// return $query->whereDate('sold_at', '>=', Carbon::today()->format('Y'));
// }
public function scopeUnsoldCars($query)
{
return $query->whereDate('sold_at', );
}
// public function scopeUnsoldCars($query)
// {
// return $query->whereDate('sold_at', );
// }
public function scopeOrderByInitialDate($query)
{

View File

@ -11,18 +11,18 @@ class CarPayment extends Model
protected $fillable = [
'amount',
'paid_at',
'payment_type',
'contract_id'
'date',
'type',
'sell_contract_id',
];
public function contract()
public function sellContract()
{
return $this->belongsTo(Contract::class);
return $this->belongsTo(SellContract::class);
}
public function car()
{
return $this->hasOneThrough(Car::class, Contract::class);
return $this->hasOneThrough(Car::class, SellContract::class);
}
}

View File

@ -47,19 +47,24 @@ class Contact extends Model
$query->orderBy('lastname')->orderBy('firstname');
}
public function contracts()
public function sellContracts()
{
return $this->hasMany(Contract::class);
return $this->hasMany(SellContract::class);
}
public function buyContracts()
{
return $this->hasMany(BuyContract::class);
}
public function boughtCars()
{
return $this->hasManyThrough(Car::class, Contract::class);
return $this->hasManyThrough(Car::class, SellContract::class);
}
public function soldCars()
{
return $this->hasMany(Car::class, 'seller_contact_id');
return $this->hasManyThrough(Car::class, BuyContract::class);
}
public function scopeFilter($query, array $filters)

View File

@ -2,22 +2,34 @@
namespace App\Models;
use Carbon\Carbon;
use Cknow\Money\Money;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Contract extends Model
class SellContract extends Model
{
use HasFactory, softDeletes;
protected $fillable = [
'sold_at',
'sell_price',
'date',
'price',
'contact_id',
'car_id',
'insurance_type',
];
public function getDateAttribute($date)
{
return Carbon::parse($date)->format('d.m.Y');
}
public function getPriceAttribute($price)
{
return Money::CHF($price)->format();
}
public function contact()
{
return $this->belongsTo(Contact::class);

View File

@ -0,0 +1,33 @@
<?php
namespace Database\Factories;
use App\Models\BuyContract;
use App\Models\Car;
use App\Models\Contact;
use Illuminate\Database\Eloquent\Factories\Factory;
class BuyContractFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = BuyContract::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'date' => $this->faker->date(),
'price' => $this->faker->numberBetween(150000, 3500000),
'contact_id' => $this->faker->numberBetween(1, Contact::count()),
'car_id' => $this->faker->unique()->numberBetween(1, Car::count()),
];
}
}

View File

@ -4,7 +4,6 @@ namespace Database\Factories;
use App\Models\Car;
use App\Models\CarModel;
use App\Models\Contact;
use Illuminate\Database\Eloquent\Factories\Factory;
class CarFactory extends Factory
@ -24,17 +23,14 @@ class CarFactory extends Factory
public function definition()
{
return [
'stammnummer' => $this->faker->regexify('[0-9]{3}.[0-9]{3}.[0-9]{3}'),
'stammnummer' => $this->faker->randomNumber(3, true) . '.' . $this->faker->randomNumber(3, true) . '.' . $this->faker->randomNumber(3, true),
'vin' => $this->faker->regexify('[A-Z]{3}ZZZ[A-Z0-9]{3}[A-Z1-9]{1}[A-Z]{1}[0-9]{6}'),
'colour' => $this->faker->safeColorName(),
'notes' => $this->faker->paragraph(),
'known_damage' => $this->faker->paragraph(),
'initial_date' => $this->faker->date(),
'last_check_date' => $this->faker->date(),
'bought_at' => $this->faker->date(),
'kilometers' => $this->faker->numberBetween(5000, 200000),
'buy_price' => $this->faker->numberBetween(100000, 3000000),
'seller_contact_id' => $this->faker->numberBetween(1, Contact::count()),
'car_model_id' => $this->faker->numberBetween(1, CarModel::count()),
];
}

View File

@ -5,7 +5,7 @@ namespace Database\Factories;
use App\Models\CarPayment;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Enums\PaymentType;
use App\Models\Contract;
use App\Models\SellContract;
class CarPaymentFactory extends Factory
{
@ -25,9 +25,9 @@ class CarPaymentFactory extends Factory
{
return [
'amount' => $this->faker->numberBetween(1000, 10000),
'paid_at' => $this->faker->date(),
'payment_type' => (string)PaymentType::getRandomValue(),
'contract_id' => $this->faker->numberBetween(1, Contract::count()),
'date' => $this->faker->date(),
'type' => (string)PaymentType::getRandomValue(),
'sell_contract_id' => $this->faker->numberBetween(1, SellContract::count()),
];
}
}

View File

@ -33,7 +33,7 @@ class ContactFactory extends Factory
'address' => $this->faker->streetName() . ' ' . $this->faker->buildingNumber(),
'zip' => $this->faker->randomNumber(4, true),
'city' => $this->faker->city(),
'country' => $this->faker->countryCode(),
'country' => 'CH',
'company' => $this->faker->company(),
'notes' => $this->faker->text(),
];

View File

@ -2,20 +2,20 @@
namespace Database\Factories;
use App\Models\Contract;
use App\Models\SellContract;
use App\Models\Car;
use App\Models\Contact;
use Illuminate\Database\Eloquent\Factories\Factory;
use App\Enums\InsuranceType;
class ContractFactory extends Factory
class SellContractFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Contract::class;
protected $model = SellContract::class;
/**
* Define the model's default state.
@ -25,10 +25,10 @@ class ContractFactory extends Factory
public function definition()
{
return [
'sold_at' => $this->faker->date(),
'sell_price' => $this->faker->numberBetween(150000, 3500000),
'date' => $this->faker->date(),
'price' => $this->faker->numberBetween(150000, 3500000),
'contact_id' => $this->faker->numberBetween(1, Contact::count()),
'car_id' => $this->faker->unique()->numberBetween(1, Car::count()),
'car_id' => $this->faker->numberBetween(1, Car::count()),
'insurance_type' => (string)InsuranceType::getRandomValue(),
];
}

View File

@ -22,17 +22,13 @@ class CreateCarsTable extends Migration
$table->text('known_damage')->nullable();
$table->date('initial_date');
$table->date('last_check_date');
$table->date('bought_at');
$table->integer('buy_price');
$table->integer('kilometers');
$table->unsignedBigInteger('seller_contact_id');
$table->foreignId('car_model_id')
->onUpdate('cascade')
->onDelete('cascade')
->constrained('car_models');
$table->timestamps();
$table->softDeletes();
$table->foreign('seller_contact_id')->references('id')->on('contacts');
});
}

View File

@ -5,7 +5,7 @@ use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Enums\InsuranceType;
class CreateContractsTable extends Migration
class CreateSellContractsTable extends Migration
{
/**
* Run the migrations.
@ -14,10 +14,10 @@ class CreateContractsTable extends Migration
*/
public function up()
{
Schema::create('contracts', function (Blueprint $table) {
Schema::create('sell_contracts', function (Blueprint $table) {
$table->id();
$table->date('sold_at');
$table->integer('sell_price');
$table->date('date');
$table->integer('price');
$table->foreignId('contact_id')
->onUpdate('cascade')
->onDelete('cascade')
@ -40,6 +40,6 @@ class CreateContractsTable extends Migration
*/
public function down()
{
Schema::dropIfExists('contracts');
Schema::dropIfExists('sell_contracts');
}
}

View File

@ -17,13 +17,13 @@ class CreateCarPaymentsTable extends Migration
Schema::create('car_payments', function (Blueprint $table) {
$table->id();
$table->integer('amount');
$table->date('paid_at');
$table->enum('payment_type', PaymentType::getValues())
$table->date('date');
$table->enum('type', PaymentType::getValues())
->default(PaymentType::Transaction);
$table->foreignId('contract_id')
$table->foreignId('sell_contract_id')
->onUpdate('cascade')
->onDelete('cascade')
->constrained('contracts');
->constrained('sell_contracts');
$table->timestamps();
});
}

View File

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateBuyContractsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('buy_contracts', function (Blueprint $table) {
$table->id();
$table->date('date');
$table->integer('price');
$table->foreignId('contact_id')
->onUpdate('cascade')
->onDelete('cascade')
->constrained('contacts');
$table->foreignId('car_id')
->onUpdate('cascade')
->onDelete('cascade')
->constrained('cars');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('buy_contracts');
}
}

View File

@ -9,7 +9,8 @@ use App\Models\Car;
use App\Models\CarModel;
use App\Models\Brand;
use App\Models\CarPayment;
use App\Models\Contract;
use App\Models\BuyContract;
use App\Models\SellContract;
use App\Models\Contact;
use App\Models\Document;
use Illuminate\Support\Facades\DB;
@ -26,7 +27,8 @@ class DatabaseSeeder extends Seeder
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
User::truncate();
CarPayment::truncate();
Contract::truncate();
BuyContract::truncate();
SellContract::truncate();
Document::truncate();
Car::truncate();
Contact::truncate();
@ -55,23 +57,28 @@ class DatabaseSeeder extends Seeder
->create();
$contacts = Contact::factory()
->count(100)
->count(60)
->create();
$nOfCars = 75;
$cars = Car::factory()
->count($nOfCars)
->create();
$buyContracts = BuyContract::factory()
->count($nOfCars)
->create();
$sellContracts = SellContract::factory()
->count(40)
->create();
$contracts = Contract::factory()
->count(20)
->create();
$carPayments = CarPayment::factory()
->count(15)
->count(60)
->create();
$documents = Document::factory()
->count(10)
->count(40)
->create();
}

23
public/js/app.js vendored
View File

@ -18156,10 +18156,10 @@ __webpack_require__.r(__webpack_exports__);
key: 'name',
value: 'Auto'
}, {
key: 'sold_at',
key: 'date',
value: 'Verkaufsdatum'
}, {
key: 'sell_price',
key: 'price',
value: 'Verkaufspreis'
}, {
key: 'insurance_type',
@ -18169,10 +18169,10 @@ __webpack_require__.r(__webpack_exports__);
key: 'name',
value: 'Auto'
}, {
key: 'bought_at',
key: 'date',
value: 'Kaufdatum'
}, {
key: 'buy_price',
key: 'price',
value: 'Kaufpreis'
}]
};
@ -18195,8 +18195,7 @@ __webpack_require__.r(__webpack_exports__);
address: this.form.address,
zip: this.form.zip,
city: this.form.city,
country: this.form.country,
link: route('contacts.update', this.contact)
country: this.form.country
};
}
},
@ -18242,6 +18241,7 @@ __webpack_require__.r(__webpack_exports__);
},
props: {
filters: Object,
sort: Object,
contacts: Object
},
data: function data() {
@ -19360,7 +19360,7 @@ var _hoisted_7 = {
};
var _hoisted_8 = {
key: 1,
"class": "px-4 flex items-center"
"class": "flex items-center"
};
var _hoisted_9 = {
key: 1,
@ -19402,7 +19402,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
onClick: function onClick($event) {
return $options.sortTable(col.key);
},
"class": "px-4 flex items-center"
"class": "flex items-center"
}, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createTextVNode)((0,vue__WEBPACK_IMPORTED_MODULE_0__.toDisplayString)(col.value) + " ", 1
/* TEXT */
), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)("div", _hoisted_7, [(0,vue__WEBPACK_IMPORTED_MODULE_0__.createVNode)(_component_unicon, {
@ -23134,13 +23134,10 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
title: $props.contacts.total + ' Kontakte',
data: $props.contacts,
columns: $data.columns,
defaultSort: {
by: 'name',
direction: 'asc'
}
defaultSort: $props.sort
}, null, 8
/* PROPS */
, ["title", "data", "columns"])])])];
, ["title", "data", "columns", "defaultSort"])])])];
}),
_: 1
/* STABLE */

View File

@ -8,14 +8,14 @@
<table class="w-full whitespace-nowrap">
<tr class="text-left font-bold">
<th v-for="(col, index) in columns" :key="col.key" class="px-6 pt-4 pb-4" :colspan="[index == (columns.length - 1) ? 2 : 1]">
<a v-if="col.sortable" href="#" @click="sortTable(col.key)" class="px-4 flex items-center">
<a v-if="col.sortable" href="#" @click="sortTable(col.key)" class="flex items-center">
{{ col.value }}
<div class="grid grid-cols-1 place-items-center ml-1">
<unicon :fill="getIconColor(col.key, 'asc')" height="22" width="22" name="angle-up"></unicon>
<unicon :fill="getIconColor(col.key, 'desc')" height="22" width="22" name="angle-down"></unicon>
</div>
</a>
<span v-else class="px-4 flex items-center">
<span v-else class="flex items-center">
{{ col.value }}
</span>
</th>

View File

@ -159,14 +159,14 @@ export default {
}),
boughtCarColumns: [
{key: 'name', value: 'Auto'},
{key: 'sold_at', value: 'Verkaufsdatum'},
{key: 'sell_price', value: 'Verkaufspreis'},
{key: 'date', value: 'Verkaufsdatum'},
{key: 'price', value: 'Verkaufspreis'},
{key: 'insurance_type', value: 'Versicherungstyp'},
],
soldCarColumns: [
{key: 'name', value: 'Auto'},
{key: 'bought_at', value: 'Kaufdatum'},
{key: 'buy_price', value: 'Kaufpreis'},
{key: 'date', value: 'Kaufdatum'},
{key: 'price', value: 'Kaufpreis'},
]
}
},
@ -189,7 +189,6 @@ export default {
zip: this.form.zip,
city: this.form.city,
country: this.form.country,
link: route('contacts.update', this.contact),
}
}
},

View File

@ -14,7 +14,7 @@
Kontakt erfassen
</jet-button>
</div>
<simple-table :title="contacts.total + ' Kontakte'" :data="contacts" :columns="columns" :defaultSort="{ by: 'name', direction:'asc'}" />
<simple-table :title="contacts.total + ' Kontakte'" :data="contacts" :columns="columns" :defaultSort="sort" />
</div>
</div>
</app-layout>
@ -36,6 +36,7 @@ export default {
},
props: {
filters: Object,
sort: Object,
contacts: Object,
},
data() {