Skip to content

Commit

Permalink
Создание заказов
Browse files Browse the repository at this point in the history
  • Loading branch information
bezumkin committed Jul 7, 2022
1 parent c4424aa commit d6e5c74
Show file tree
Hide file tree
Showing 20 changed files with 544 additions and 19 deletions.
49 changes: 49 additions & 0 deletions core/db/migrations/20220705002400_orders.php
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Schema\Blueprint;
use Vesp\Services\Migration;

final class Orders extends Migration
{
public function up(): void
{
$this->schema->create(
'orders',
function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->char('post', 6)->nullable();
$table->string('city')->nullable();
$table->string('address')->nullable();
$table->boolean('paid')->default(false);
$table->unsignedDecimal('total')->nullable();
$table->timestamps();
$table->timestamp('paid_at')->nullable();
}
);

$this->schema->create(
'order_products',
function (Blueprint $table) {
$table->id();
$table->foreignId('order_id')
->constrained('orders')->cascadeOnDelete();
$table->foreignId('product_id')->nullable()
->constrained('products')->nullOnDelete();
$table->string('title');
$table->unsignedDecimal('price');
$table->unsignedInteger('amount')->default(1);
$table->unsignedDecimal('total');
}
);
}

public function down(): void
{
$this->schema->drop('order_products');
$this->schema->drop('orders');
}
}
18 changes: 9 additions & 9 deletions core/db/seeds/UserRoles.php
@@ -1,20 +1,20 @@
<?php

use Phinx\Seed\AbstractSeed;
use App\Models\UserRole;
use Phinx\Seed\AbstractSeed;

class UserRoles extends AbstractSeed
{
public function run(): void
{
$roles = [
'Administrator' => [
'scope' => ['profile', 'users', 'products'],
],
'User' => [
'scope' => ['profile'],
],
];
$roles = [
'Administrator' => [
'scope' => ['profile', 'users', 'products', 'orders'],
],
'User' => [
'scope' => ['profile'],
],
];

foreach ($roles as $title => $data) {
if (!$group = UserRole::query()->where('title', $title)->first()) {
Expand Down
2 changes: 2 additions & 0 deletions core/routes.php
Expand Up @@ -25,6 +25,7 @@ static function (RouteCollectorProxy $group) {
$group->any('/categories[/{id}]', App\Controllers\Admin\Categories::class);
$group->any('/products[/{id}]', App\Controllers\Admin\Products::class);
$group->any('/product/{product_id}/files[/{file_id}]', App\Controllers\Admin\Product\Files::class);
$group->any('/orders[/{id}]', App\Controllers\Admin\Orders::class);
}
);

Expand All @@ -34,6 +35,7 @@ static function (RouteCollectorProxy $group) {
$group->any('/categories[/{alias}]', App\Controllers\Web\Categories::class);
$group->any('/categories/{category}/products[/{alias}]', App\Controllers\Web\Category\Products::class);
$group->any('/products', App\Controllers\Web\Products::class);
$group->any('/orders', App\Controllers\Web\Orders::class);
}
);
}
Expand Down
43 changes: 43 additions & 0 deletions core/src/Controllers/Admin/Orders.php
@@ -0,0 +1,43 @@
<?php

namespace App\Controllers\Admin;

use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Psr\Http\Message\ResponseInterface;
use Vesp\Controllers\ModelController;

class Orders extends ModelController
{
protected $scope = 'orders';
protected $model = Order::class;

protected function beforeGet(Builder $c): Builder
{
$c->with('orderProducts');

return $c;
}

protected function beforeSave(Model $record): ?ResponseInterface
{
/** @var Order $record */
if ($record->paid && !$record->paid_at) {
$record->paid_at = time();
} elseif ($record->paid_at && !$record->paid) {
$record->paid_at = null;
}

return null;
}

protected function beforeCount(Builder $c): Builder
{
if ($query = $this->getProperty('query')) {
$c->where('title', 'LIKE', "%$query%");
}

return $c;
}
}
44 changes: 44 additions & 0 deletions core/src/Controllers/Web/Orders.php
@@ -0,0 +1,44 @@
<?php

namespace App\Controllers\Web;

use App\Models\Order;
use App\Models\Product;
use Psr\Http\Message\ResponseInterface;
use Vesp\Controllers\Controller;

class Orders extends Controller
{
public function put(): ResponseInterface
{
if (!$ordered = $this->getProperty('products')) {
return $this->failure();
}

$orderTotal = 0;
$orderProducts = [];
foreach ($ordered as $id => $amount) {
/** @var Product $product */
if ($product = Product::query()->where('active', true)->find($id)) {
$total = $product->price * $amount;
$orderTotal += $total;
$orderProducts[] = [
'product_id' => $id,
'title' => $product->title,
'amount' => $amount,
'price' => $product->price,
'total' => $total,
];
}
}

$order = new Order();
$order->fill($this->getProperties());
$order->total = $orderTotal;
if ($order->save()) {
$order->orderProducts()->createMany($orderProducts);
}

return $this->success();
}
}
34 changes: 34 additions & 0 deletions core/src/Models/Order.php
@@ -0,0 +1,34 @@
<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
* @property int $id
* @property string $name
* @property string $email
* @property string $post
* @property string $city
* @property string $address
* @property bool $paid
* @property int $total
* @property Carbon $created_at
* @property Carbon $updated_at
* @property Carbon $paid_at
*
* @property-read OrderProduct[] $orderProducts
*/
class Order extends Model
{
protected $casts = ['paid' => 'boolean'];
protected $guarded = ['id', 'created_at', 'updated_at', 'paid_at'];
protected $dates = ['paid_at'];

public function orderProducts(): HasMany
{
return $this->hasMany(OrderProduct::class);
}
}
34 changes: 34 additions & 0 deletions core/src/Models/OrderProduct.php
@@ -0,0 +1,34 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
* @property int $id
* @property int $order_id
* @property int $product_id
* @property string $title
* @property float $price
* @property int $amount
* @property float $total
*
* @property-read Order $order
* @property-read Product $product
*/
class OrderProduct extends Model
{
public $timestamps = false;
protected $guarded = ['id'];

public function order(): BelongsTo
{
return $this->belongsTo(Order::class);
}

public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
}
76 changes: 76 additions & 0 deletions frontend/src/admin/components/forms/order.vue
@@ -0,0 +1,76 @@
<template>
<div>
<b-form-group :label="$t('models.order.name')">
<b-form-input v-model.trim="record.name" required autofocus />
</b-form-group>

<b-form-group :label="$t('models.order.email')">
<b-form-input v-model.trim="record.email" required />
</b-form-group>

<b-row>
<b-col md="4">
<b-form-group :label="$t('models.order.post')">
<b-form-input v-model.trim="record.post" type="number" min="0" max="999999" />
</b-form-group>
</b-col>
<b-col md="8">
<b-form-group :label="$t('models.order.city')">
<b-form-input v-model.trim="record.city" />
</b-form-group>
</b-col>
</b-row>

<b-form-group :label="$t('models.order.address')">
<b-form-input v-model.trim="record.address" />
</b-form-group>

<b-row align-v="center">
<b-col cols="8">
<b-form-group :label="$t('models.order.total')">
<b-form-input v-model.trim="record.total" readonly />
</b-form-group>
</b-col>
<b-col cols="4" class="text-right">
<b-form-checkbox v-model.trim="record.paid">
{{ $t('models.order.paid') }}
</b-form-checkbox>
</b-col>
</b-row>

<div class="mt-2 pt-2 border-top">
<b-row v-for="product in record.order_products" :key="product.id" class="mt-3" align-v="center">
<b-col cols="6">
<b-link v-if="product.product_id" :to="{name: 'products-edit-id', params: {id: product.product_id}}">
{{ product.title }}
</b-link>
<div v-else>{{ product.title }}</div>
<div class="small text-muted">{{ product.price }} руб. x {{ product.amount }}</div>
</b-col>
<b-col cols="6" class="text-right">{{ product.total }} руб.</b-col>
</b-row>
</div>
</div>
</template>

<script>
export default {
name: 'FormProduct',
props: {
value: {
type: Object,
required: true,
},
},
computed: {
record: {
get() {
return this.value
},
set(newValue) {
this.$emit('input', newValue)
},
},
},
}
</script>
11 changes: 11 additions & 0 deletions frontend/src/admin/lexicons/de.js
Expand Up @@ -56,6 +56,17 @@ export default {
category: 'Kategorie',
active: 'Aktiv',
},
order: {
title_one: 'Befehl',
title_many: 'Aufträge',
name: 'Kunde',
email: 'Email',
post: 'Post',
city: 'Stadt',
address: 'Adresse',
paid: 'Bezahlt',
total: 'Gesamt',
},
},
errors: {
security: {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/admin/lexicons/en.js
Expand Up @@ -56,6 +56,17 @@ export default {
category: 'Category',
active: 'Active',
},
order: {
title_one: 'Order',
title_many: 'Orders',
name: 'Customer',
email: 'Email',
post: 'Post',
city: 'City',
address: 'Address',
paid: 'Paid',
total: 'Total',
},
},
errors: {
security: {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/admin/lexicons/ru.js
Expand Up @@ -56,6 +56,17 @@ export default {
category: 'Категория',
active: 'Включено',
},
order: {
title_one: 'Заказ',
title_many: 'Заказы',
name: 'Имя покупателя',
email: 'Email',
post: 'Индекс',
city: 'Город',
address: 'Адрес',
paid: 'Оплачено',
total: 'Стоимость',
},
},
errors: {
security: {
Expand Down

0 comments on commit d6e5c74

Please sign in to comment.