Compare commits
17 Commits
de1d7fbed8
...
1880a167b3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1880a167b3 | ||
|
|
ec33006984 | ||
|
|
147e7b9516 | ||
|
|
e70ec773f9 | ||
|
|
e2adf5e9da | ||
|
|
d3ac6ff8d9 | ||
|
|
b0c6a4236c | ||
|
|
9fac84a882 | ||
|
|
8f49fe0124 | ||
|
|
43272bb1b1 | ||
|
|
c48ad83548 | ||
|
|
b4a05e3f8c | ||
|
|
bac1579285 | ||
|
|
41d6ddc346 | ||
|
|
522ebdae34 | ||
|
|
683fa5e0d9 | ||
|
|
d83bc03258 |
@@ -18,6 +18,9 @@ class Kernel extends ConsoleKernel
|
||||
|
||||
// Remove non saved attachments...
|
||||
$schedule->call(new PruneStaleAttachments)->daily();
|
||||
|
||||
// IF any warnings unresolved warnings exists, send notify me
|
||||
$schedule->call(new WarnDev)->dailyAt('16:00');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
25
app/Console/WarnDev.php
Normal file
25
app/Console/WarnDev.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Models\System\Warning;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class WarnDev extends Command
|
||||
{
|
||||
/**
|
||||
* Notify me if any unresolved warnings exists
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
$warnings = Warning::whereNull('resolved_at')->get();
|
||||
|
||||
if ($warnings->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendSMS('61929248', 'Warnings: '.$warnings->count());
|
||||
}
|
||||
}
|
||||
@@ -81,11 +81,10 @@ class ProductFilterer
|
||||
$this->queryBuilder->whereIn(
|
||||
column: 'id',
|
||||
values: (
|
||||
fn ($query) => $query->from('product_has_relations')
|
||||
fn ($query) => $query->from('category_product')
|
||||
->select('product_id')
|
||||
->distinct('product_id')
|
||||
->where('productable_type', '=', 'category')
|
||||
->whereIn('productable_id', explode(',', $this->request->categories))
|
||||
->whereIn('category_id', explode(',', $this->request->categories))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use App\Models\Auth\Verification;
|
||||
use App\Models\Ecommerce\Channel\Channel;
|
||||
use App\Models\Ecommerce\Product\Inventory\Inventory;
|
||||
use App\Models\System\Warning;
|
||||
use App\Repositories\Ecommerce\Product\Barcode\BarcodeRepository;
|
||||
use Illuminate\Http\Client\PendingRequest;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -176,9 +177,7 @@ if (! function_exists('tmpostChannel')) {
|
||||
*/
|
||||
function tmpostChannel(): Channel
|
||||
{
|
||||
Cache::rememberForever('tmpostChannel', fn () => Channel::tmpostDefault());
|
||||
|
||||
return Cache::get('tmpostChannel');
|
||||
return Channel::tmpostDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,9 +187,7 @@ if (! function_exists('tmpostDefaultInventory')) {
|
||||
*/
|
||||
function tmpostDefaultInventory(): mixed
|
||||
{
|
||||
Cache::rememberForever('tmpostDefaultInventory', fn () => Inventory::tmpostDefault());
|
||||
|
||||
return Cache::get('tmpostDefaultInventory');
|
||||
return Inventory::tmpostDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,3 +389,16 @@ function createHalkbankOrder($price = 123): array
|
||||
'url' => $paymentResponse['formUrl'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn brother
|
||||
*/
|
||||
function warn(string $message, string $content = '', string $where = '', string $notes = ''): void
|
||||
{
|
||||
Warning::forceCreate([
|
||||
'name' => $message,
|
||||
'content' => $content,
|
||||
'where' => $where,
|
||||
'notes' => $notes,
|
||||
]);
|
||||
}
|
||||
@@ -38,10 +38,9 @@ class FilterParamsController extends Controller
|
||||
$category_id = (int) $request->category_id;
|
||||
|
||||
$brand_ids = DB::table('products')
|
||||
->join('product_has_relations', function ($join) use ($category_id) {
|
||||
$join->on('products.id', '=', 'product_has_relations.product_id')
|
||||
->where('product_has_relations.productable_id', '=', $category_id)
|
||||
->where('product_has_relations.productable_type', '=', 'category');
|
||||
->join('category_product', function ($join) use ($category_id) {
|
||||
$join->on('products.id', '=', 'category_product.product_id')
|
||||
->where('category_product.category_id', '=', $category_id);
|
||||
})
|
||||
->select(['id', 'brand_id'])
|
||||
->pluck('brand_id');
|
||||
|
||||
@@ -97,9 +97,8 @@ class FilterController extends Controller
|
||||
->distinct('products.id')
|
||||
->pluck('products.id');
|
||||
|
||||
return Category::where('is_visible', true)->ordered()->join('product_has_relations', 'categories.id', '=', 'product_has_relations.productable_id')
|
||||
->where('product_has_relations.productable_type', '=', 'category')
|
||||
->whereIntegerInRaw('product_has_relations.product_id', $products)
|
||||
return Category::where('is_visible', true)->ordered()->join('category_product', 'categories.id', '=', 'category_product.category_id')
|
||||
->whereIntegerInRaw('category_product.product_id', $products)
|
||||
->get(['id', 'parent_id', 'name'])
|
||||
->unique('categories.id');
|
||||
}
|
||||
|
||||
@@ -87,9 +87,8 @@ class CategoriesFilter
|
||||
->distinct('products.id')
|
||||
->pluck('products.id');
|
||||
|
||||
return $this->queryBuilder->join('product_has_relations', 'categories.id', '=', 'product_has_relations.productable_id')
|
||||
->where('product_has_relations.productable_type', '=', 'category')
|
||||
->whereIntegerInRaw('product_has_relations.product_id', $products)
|
||||
return $this->queryBuilder->join('category_product', 'categories.id', '=', 'category_product.category_id')
|
||||
->whereIntegerInRaw('category_product.product_id', $products)
|
||||
->get($this->columns)
|
||||
->unique('categories.id');
|
||||
}
|
||||
|
||||
@@ -16,18 +16,17 @@ class ProductRelatedController extends Controller
|
||||
*/
|
||||
public function index(Product $product): JsonResponse
|
||||
{
|
||||
$products = DB::table('product_has_relations')
|
||||
->select('product_has_relations.product_id')
|
||||
->whereIn('product_has_relations.productable_id', (function ($query) use ($product) {
|
||||
$query->from('product_has_relations')
|
||||
->select('productable_id')
|
||||
->distinct('productable_id')
|
||||
->where('productable_type', '=', 'category')
|
||||
$products = DB::table('category_product')
|
||||
->select('category_product.product_id')
|
||||
->whereIn('category_product.category_id', (function ($query) use ($product) {
|
||||
$query->from('category_product')
|
||||
->select('category_id')
|
||||
->distinct('category_id')
|
||||
->where('product_id', '=', $product->id);
|
||||
}))
|
||||
->limit(12)
|
||||
->orderByRaw('RANDOM()')
|
||||
->pluck('product_has_relations.product_id')
|
||||
->pluck('category_product.product_id')
|
||||
->unique();
|
||||
|
||||
return response()->rest(
|
||||
|
||||
@@ -59,6 +59,11 @@ class UpdateProductRelations implements ShouldQueue
|
||||
'product_custom_value' => in_array($attribute->type, ['text', 'number']) ? $this->properties[$attribute->slug] : null,
|
||||
]);
|
||||
});
|
||||
|
||||
// sync properties json
|
||||
if (method_exists($this->model, 'syncPropertiesJson')) {
|
||||
$this->model->syncPropertiesJson();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ use App\Repositories\System\Cache\CacheRepository;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Laravel\Nova\Nova;
|
||||
use Spatie\EloquentSortable\Sortable;
|
||||
use Spatie\EloquentSortable\SortableTrait;
|
||||
@@ -179,9 +179,9 @@ class Channel extends Model implements HasMedia, Sortable
|
||||
/**
|
||||
* Products
|
||||
*/
|
||||
public function products(): MorphToMany
|
||||
public function products(): BelongsToMany
|
||||
{
|
||||
return $this->morphToMany(Product::class, 'productable', 'product_has_relations');
|
||||
return $this->belongsToMany(Product::class, 'channel_product');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Spatie\EloquentSortable\Sortable;
|
||||
use Spatie\EloquentSortable\SortableTrait;
|
||||
use Spatie\Image\Manipulations;
|
||||
@@ -152,9 +151,9 @@ class Category extends Model implements HasMedia, Sortable
|
||||
/**
|
||||
* Category Products
|
||||
*/
|
||||
public function products(): MorphToMany
|
||||
public function products(): BelongsToMany
|
||||
{
|
||||
return $this->morphToMany(Product::class, 'productable', 'product_has_relations');
|
||||
return $this->belongsToMany(Product::class, 'category_product');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Models\Ecommerce\Product\Collection;
|
||||
use App\Models\Ecommerce\Product\Product\Product;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Spatie\EloquentSortable\Sortable;
|
||||
use Spatie\EloquentSortable\SortableTrait;
|
||||
use Spatie\Image\Manipulations;
|
||||
@@ -109,9 +109,9 @@ class Collection extends Model implements HasMedia, Sortable
|
||||
/**
|
||||
* Products
|
||||
*/
|
||||
public function products(): MorphToMany
|
||||
public function products(): BelongsToMany
|
||||
{
|
||||
return $this->morphToMany(Product::class, 'productable', 'product_has_relations');
|
||||
return $this->belongsToMany(Product::class, 'collection_product');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Ecommerce\Product\Order\Concerns\HasPayments;
|
||||
use App\Models\Ecommerce\Product\Order\Concerns\HasShipping;
|
||||
use App\Models\Ecommerce\Product\Order\Concerns\HasStatus;
|
||||
use App\Models\Ecommerce\Product\Order\Status\OrderStatus;
|
||||
use App\Models\Concerns\HasSchemalessAttributes;
|
||||
use App\Models\System\Settings\Location\Province;
|
||||
use App\Models\System\Settings\Payments\PaymentType;
|
||||
use App\Models\User;
|
||||
@@ -22,6 +23,7 @@ class Order extends Model
|
||||
use HasShipping;
|
||||
use HasStatus;
|
||||
use SoftDeletes;
|
||||
use HasSchemalessAttributes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
||||
@@ -15,7 +15,6 @@ use App\Models\Ecommerce\Product\Review\Review;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
|
||||
trait ProductRelationships
|
||||
{
|
||||
@@ -38,9 +37,9 @@ trait ProductRelationships
|
||||
/**
|
||||
* Related Channels
|
||||
*/
|
||||
public function channels(): MorphToMany
|
||||
public function channels(): BelongsToMany
|
||||
{
|
||||
return $this->morphedByMany(Channel::class, 'productable', 'product_has_relations');
|
||||
return $this->belongsToMany(Channel::class, 'channel_product');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,25 +55,25 @@ trait ProductRelationships
|
||||
/**
|
||||
* Related products (similar)
|
||||
*/
|
||||
public function relatedProducts(): MorphToMany
|
||||
public function relatedProducts(): BelongsToMany
|
||||
{
|
||||
return $this->morphedByMany(Product::class, 'productable', 'product_has_relations');
|
||||
return $this->belongsToMany(Product::class, 'product_related', 'product_id', 'related_product_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Related categories
|
||||
*/
|
||||
public function categories(): MorphToMany
|
||||
public function categories(): BelongsToMany
|
||||
{
|
||||
return $this->morphedByMany(Category::class, 'productable', 'product_has_relations');
|
||||
return $this->belongsToMany(Category::class, 'category_product');
|
||||
}
|
||||
|
||||
/**
|
||||
* Related Collections
|
||||
*/
|
||||
public function collections(): MorphToMany
|
||||
public function collections(): BelongsToMany
|
||||
{
|
||||
return $this->morphedByMany(Collection::class, 'productable', 'product_has_relations');
|
||||
return $this->belongsToMany(Collection::class, 'collection_product');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -134,8 +134,8 @@ class ProductFieldsForDetail
|
||||
'name' => $property->attribute->slug,
|
||||
'type' => $property->attribute->type,
|
||||
'default' => $property->attribute->type === 'select'
|
||||
? $property->values->first()->value?->id
|
||||
: $property->values->first()->product_custom_value,
|
||||
? $property->values->first()->value?->id ?? '-'
|
||||
: $property->values->first()->product_custom_value ?? '-',
|
||||
'options' => $property->attribute->values->map(fn ($value) => [
|
||||
'label' => $value->value,
|
||||
'value' => $value->id,
|
||||
|
||||
@@ -33,9 +33,8 @@ class ProductEntrepreneurFilter extends Filter
|
||||
*/
|
||||
public function apply(NovaRequest $request, $query, $value)
|
||||
{
|
||||
$vendorProducts = DB::table('product_has_relations')
|
||||
->where('productable_type', 'channel')
|
||||
->where('productable_id', $value)
|
||||
$vendorProducts = DB::table('channel_product')
|
||||
->where('channel_id', $value)
|
||||
->pluck('product_id');
|
||||
|
||||
$query->whereIntegerInRaw('id', $vendorProducts);
|
||||
|
||||
@@ -123,9 +123,8 @@ class Product extends Resource
|
||||
$user = $request->user();
|
||||
|
||||
if ($user->hasRole('vendor')) {
|
||||
$vendorProducts = DB::table('product_has_relations')
|
||||
->where('productable_type', 'channel')
|
||||
->where('productable_id', $user->channel()->id)
|
||||
$vendorProducts = DB::table('channel_product')
|
||||
->where('channel_id', $user->channel()->id)
|
||||
->pluck('product_id');
|
||||
|
||||
$query->whereIntegerInRaw('id', $vendorProducts);
|
||||
|
||||
@@ -124,16 +124,13 @@ class CategoryRepository
|
||||
/**
|
||||
* Names with taxes (usefull for nova)
|
||||
*/
|
||||
public static function namesWithTaxes(): array
|
||||
public static function namesWithTaxes(): mixed
|
||||
{
|
||||
return CacheRepository::make(
|
||||
name: 'cs-nova-models-categories',
|
||||
value: fn () => static::maskParentName(
|
||||
return static::maskParentName(
|
||||
Category::tree()
|
||||
->where('is_visible', true)
|
||||
->get(['id', 'slug', 'name', 'tax_percentage', 'parent_id', 'is_visible'])
|
||||
->toTree()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -181,9 +178,8 @@ class CategoryRepository
|
||||
])) {
|
||||
$products = $resource->products()->distinct('products.id')->pluck('products.id');
|
||||
|
||||
return $this->queryBuilder->join('product_has_relations', 'categories.id', '=', 'product_has_relations.productable_id')
|
||||
->where('product_has_relations.productable_type', '=', 'category')
|
||||
->whereIntegerInRaw('product_has_relations.product_id', $products)
|
||||
return $this->queryBuilder->join('category_product', 'categories.id', '=', 'category_product.category_id')
|
||||
->whereIntegerInRaw('category_product.product_id', $products)
|
||||
->distinct('categories.id')
|
||||
->get(['id', 'slug', 'name']);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ class CreateOrderService
|
||||
{
|
||||
return DB::transaction(function () use ($user, $data) {
|
||||
// 1. Create the order
|
||||
info(['service' => $data]);
|
||||
$order = Order::create($data);
|
||||
|
||||
// 2. Process Cart Items
|
||||
@@ -41,10 +40,24 @@ class CreateOrderService
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
// Update Stock
|
||||
// Update Stock directly in products table, also relationship
|
||||
$stock = $cart->product->stock - $cart->product_quantity;
|
||||
$cart->product->update([
|
||||
'stock' => $cart->product->stock - $cart->product_quantity,
|
||||
'stock' => $stock,
|
||||
]);
|
||||
|
||||
$data = DB::table('inventory_product')->where('product_id', $cart->product_id)->first();
|
||||
if ($data) {
|
||||
DB::table('inventory_product')->where('id', $data->id)->update([
|
||||
'stock' => $stock,
|
||||
]);
|
||||
} else {
|
||||
warn('Product has no inventory record', json_encode([
|
||||
'product_id' => $cart->product_id,
|
||||
'order_id' => $order->id,
|
||||
]));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// 3. Clear User Cart
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// 1. Create new pivot tables
|
||||
Schema::create('category_product', function (Blueprint $table) {
|
||||
$table->foreignId('product_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
|
||||
// No primary key on pivot usually, but maybe composite unique?
|
||||
// product_has_relations didn't have one explicitly shown but usually pivots do.
|
||||
// Let's add an index for performance.
|
||||
$table->unique(['product_id', 'category_id']);
|
||||
});
|
||||
|
||||
Schema::create('collection_product', function (Blueprint $table) {
|
||||
$table->foreignId('product_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('collection_id')->constrained()->cascadeOnDelete();
|
||||
$table->unique(['product_id', 'collection_id']);
|
||||
});
|
||||
|
||||
Schema::create('channel_product', function (Blueprint $table) {
|
||||
$table->foreignId('product_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('channel_id')->constrained()->cascadeOnDelete();
|
||||
$table->unique(['product_id', 'channel_id']);
|
||||
});
|
||||
|
||||
Schema::create('product_related', function (Blueprint $table) {
|
||||
$table->foreignId('product_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('related_product_id')->constrained('products')->cascadeOnDelete();
|
||||
$table->unique(['product_id', 'related_product_id']);
|
||||
});
|
||||
|
||||
// 2. Migrate data
|
||||
// Using raw SQL for efficiency and simplicity
|
||||
DB::statement("INSERT INTO category_product (product_id, category_id) SELECT product_id, productable_id FROM product_has_relations WHERE productable_type = 'category'");
|
||||
DB::statement("INSERT INTO collection_product (product_id, collection_id) SELECT product_id, productable_id FROM product_has_relations WHERE productable_type = 'collection'");
|
||||
DB::statement("INSERT INTO channel_product (product_id, channel_id) SELECT product_id, productable_id FROM product_has_relations WHERE productable_type = 'channel'");
|
||||
DB::statement("INSERT INTO product_related (product_id, related_product_id) SELECT product_id, productable_id FROM product_has_relations WHERE productable_type = 'product'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('category_product');
|
||||
Schema::dropIfExists('collection_product');
|
||||
Schema::dropIfExists('channel_product');
|
||||
Schema::dropIfExists('product_related');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('user_addresses', function (Blueprint $table) {
|
||||
$table->string('title')->nullable();
|
||||
$table->string('building')->nullable();
|
||||
$table->string('floor')->nullable();
|
||||
$table->string('door')->nullable();
|
||||
$table->string('location')->nullable();
|
||||
|
||||
$table->dropColumn('first_name');
|
||||
$table->dropColumn('last_name');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('user_addresses', function (Blueprint $table) {
|
||||
$table->dropColumn('title');
|
||||
$table->dropColumn('building');
|
||||
$table->dropColumn('floor');
|
||||
$table->dropColumn('door');
|
||||
$table->dropColumn('location');
|
||||
$table->string('first_name')->nullable();
|
||||
$table->string('last_name')->nullable();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('orders', function (Blueprint $table) {
|
||||
$table->jsonb('options')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('orders', function (Blueprint $table) {
|
||||
$table->dropColumn('options');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -2,8 +2,23 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Database\Seeders\New\AddressSeeder;
|
||||
use Database\Seeders\New\BrandsSeeder;
|
||||
use Database\Seeders\New\CategoriesTableSeeder;
|
||||
use Database\Seeders\New\CustomersTableSeeder;
|
||||
use Database\Seeders\New\FavoritesSeeder;
|
||||
use Database\Seeders\New\MediaSeeder;
|
||||
use Database\Seeders\New\OrderAddressSeeder;
|
||||
use Database\Seeders\New\OrderSeeder;
|
||||
use Database\Seeders\New\ProductBarcodesSeeder;
|
||||
use Database\Seeders\New\ProductCategoryRelationshipsSeeder;
|
||||
use Database\Seeders\New\ProductPricesSeeder;
|
||||
use Database\Seeders\New\ProductPropertiesSeeder;
|
||||
use Database\Seeders\New\ProductPropertyValuesSeeder;
|
||||
use Database\Seeders\New\ProductsTableSeeder;
|
||||
use Database\Seeders\New\ProductStocksSeeder;
|
||||
use Database\Seeders\New\PropertiesTableSeeder;
|
||||
use Database\Seeders\New\SectionsSeeder;
|
||||
use Database\Seeders\New\SellersTableSeeder;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
@@ -18,11 +33,26 @@ class DatabaseSeeder extends Seeder
|
||||
public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
// UsersTableSeeder::class,
|
||||
// BrandsSeeder::class,
|
||||
// CustomersTableSeeder::class,
|
||||
PaymentTypeSeeder::class,
|
||||
UsersTableSeeder::class,
|
||||
BrandsSeeder::class,
|
||||
CustomersTableSeeder::class,
|
||||
SellersTableSeeder::class,
|
||||
// InventoriesTableSeeder::class,
|
||||
ProductsTableSeeder::class,
|
||||
ProductPricesSeeder::class,
|
||||
CategoriesTableSeeder::class,
|
||||
AddressSeeder::class,
|
||||
PropertiesTableSeeder::class,
|
||||
FavoritesSeeder::class,
|
||||
SectionsSeeder::class,
|
||||
ProductCategoryRelationshipsSeeder::class,
|
||||
ProductBarcodesSeeder::class,
|
||||
ProductPropertiesSeeder::class,
|
||||
ProductPropertyValuesSeeder::class,
|
||||
ProductStocksSeeder::class,
|
||||
MediaSeeder::class,
|
||||
OrderSeeder::class,
|
||||
OrderAddressSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
43
database/seeders/PaymentTypeSeeder.php
Normal file
43
database/seeders/PaymentTypeSeeder.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\System\Settings\Payments\PaymentType;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
|
||||
class PaymentTypeSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
collect([
|
||||
[
|
||||
'id' => 1,
|
||||
'code' => 'cash',
|
||||
'name' => ['en' => 'Cash', 'tk' => 'Nagt', 'ru' => 'Näliçni'],
|
||||
'tax' => 0,
|
||||
'is_enabled' => true,
|
||||
'options' => null,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'code' => 'atm',
|
||||
'name' => ['en' => 'ATM', 'tk' => 'Terminal', 'ru' => 'ATM'],
|
||||
'tax' => 0,
|
||||
'is_enabled' => true,
|
||||
'options' => null,
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'code' => 'online_halkbank',
|
||||
'name' => ['en' => 'Online (halkbank)', 'tk' => 'Onlaýn (halkbank)', 'ru' => 'Onlaýn (halkbank)'],
|
||||
'tax' => 0,
|
||||
'is_enabled' => true,
|
||||
'options' => null,
|
||||
],
|
||||
])->each(fn($data) => PaymentType::create($data));
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Ecommerce\Channel\Channel;
|
||||
use App\Models\Ecommerce\Product\Inventory\Inventory;
|
||||
use App\Models\System\Roles\Role;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
@@ -36,6 +38,8 @@ class UsersTableSeeder extends Seeder
|
||||
|
||||
$user->assignRole('admin');
|
||||
});
|
||||
|
||||
$this->createChannels();
|
||||
}
|
||||
|
||||
public function seedRoles(): void
|
||||
@@ -76,4 +80,31 @@ class UsersTableSeeder extends Seeder
|
||||
SELECT setval('roles_id_seq', (SELECT MAX(id) from roles))
|
||||
");
|
||||
}
|
||||
|
||||
public function createChannels(): void
|
||||
{
|
||||
collect([
|
||||
[
|
||||
'name' => 'Tmpost',
|
||||
'slug' => 'tmpost',
|
||||
'description' => 'Tmpost default',
|
||||
'timezone' => 'Asia/Ashgabat',
|
||||
'url' => 'http://shop.post.tm',
|
||||
'is_default' => true,
|
||||
],
|
||||
])->each(fn ($data) => Channel::create($data));
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('channels_id_seq', (SELECT MAX(id) from channels))
|
||||
");
|
||||
|
||||
Inventory::create([
|
||||
'id' => 14,
|
||||
'code' => 'tmpost',
|
||||
'name' => 'Tmpost inventory',
|
||||
'region' => 'ag',
|
||||
'phone_number' => '99361099310',
|
||||
'channel_id' => Channel::where('slug', 'tmpost')->first()->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
56
database/seeders/new/AddressSeeder.php
Normal file
56
database/seeders/new/AddressSeeder.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class AddressSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'user_addresses';
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/addresses.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
// First we need to fill user_addresses table with json data, then find user id matching addressable_id and fill user options with {"address": "$data"}
|
||||
|
||||
foreach ($items as $data) {
|
||||
try {
|
||||
DB::table($table)->insert([
|
||||
'id' => $data['id'],
|
||||
'user_id' => $data['addressable_id'],
|
||||
'street_address' => $data['address'],
|
||||
'title' => $data['title'],
|
||||
'building' => $data['building'],
|
||||
'floor' => $data['floor'],
|
||||
'door' => $data['door'],
|
||||
'location' => $data['location'],
|
||||
'type' => $data['type'],
|
||||
'created_at' => $data['created_at'],
|
||||
'updated_at' => $data['updated_at'],
|
||||
'is_default' => false,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = User::where('id', $data['addressable_id'])->first();
|
||||
if ($user) {
|
||||
info($user);
|
||||
$user->options->set('address', $data['address']);
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,12 @@ class BrandsSeeder extends Seeder
|
||||
DB::table($table)->truncate();
|
||||
|
||||
foreach ($datas as $data) {
|
||||
DB::table($table)->insertOrIgnore([
|
||||
$name = json_decode($data->name);
|
||||
|
||||
DB::table($table)->insert([
|
||||
'id' => $data->id,
|
||||
'slug' => Str::slug(json_decode($data->name)->en ?? $data->code),
|
||||
'name' => $data->name,
|
||||
'slug' => Str::slug($name->en ?? $data->code).'_'.$data->id,
|
||||
'name' => $name->en,
|
||||
'is_visible' => $data->is_blocked,
|
||||
'sort_order' => $data->priority,
|
||||
'created_at' => $data->created_at,
|
||||
|
||||
41
database/seeders/new/CategoriesTableSeeder.php
Normal file
41
database/seeders/new/CategoriesTableSeeder.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class CategoriesTableSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'categories';
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/categories.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
DB::table($table)->insert([
|
||||
'id' => $data['id'],
|
||||
'parent_id' => $data['parent_id'],
|
||||
'slug' => $data['slug'],
|
||||
'name' => str_replace('"tm"', '"tk"', $data['name']),
|
||||
'is_visible' => $data['is_blocked'],
|
||||
'sort_order' => $data['priority'],
|
||||
'tax_percentage' => $data['percentage'],
|
||||
'created_at' => $data['created_at'],
|
||||
'updated_at' => $data['updated_at'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
}
|
||||
}
|
||||
29
database/seeders/new/FavoritesSeeder.php
Normal file
29
database/seeders/new/FavoritesSeeder.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class FavoritesSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$datas = json_decode(File::get('database/data/favorite_products.json'));
|
||||
$table = 'favorites';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
foreach ($datas as $data) {
|
||||
DB::table($table)->insert([
|
||||
'user_id' => $data->customer_id,
|
||||
'product_id' => $data->item_id,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
}
|
||||
}
|
||||
64
database/seeders/new/MediaSeeder.php
Normal file
64
database/seeders/new/MediaSeeder.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class MediaSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'media';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/media.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
$modelType = match ($data['model_type']) {
|
||||
'Domain\Brand\Models\Brand' => 'brand',
|
||||
'Domain\Category\Models\Category' => 'category',
|
||||
'Domain\Item\Models\Item' => 'product',
|
||||
'Domain\Promo\Models\Banner' => 'banner',
|
||||
'Domain\Promo\Models\Carousel' => 'channel',
|
||||
};
|
||||
|
||||
if ($modelType === 'banner') {
|
||||
continue;
|
||||
}
|
||||
|
||||
DB::table($table)->insert([
|
||||
"id" => $data['id'],
|
||||
'model_type' => $modelType,
|
||||
'model_id' => $data['model_id'],
|
||||
'uuid' => $data['uuid'],
|
||||
'collection_name' => 'uploads',
|
||||
'name' => $data['name'],
|
||||
'file_name' => $data['file_name'],
|
||||
'mime_type' => $data['mime_type'],
|
||||
'disk' => $data['disk'],
|
||||
'conversions_disk' => $data['conversions_disk'],
|
||||
'size' => $data['size'],
|
||||
'manipulations' => $data['manipulations'],
|
||||
'custom_properties' => $data['custom_properties'],
|
||||
'generated_conversions' => $data['generated_conversions'],
|
||||
'responsive_images' => $data['responsive_images'],
|
||||
'order_column' => $data['order_column'],
|
||||
'created_at' => $data['created_at'],
|
||||
'updated_at' => $data['updated_at'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
}
|
||||
}
|
||||
31
database/seeders/new/OrderAddressSeeder.php
Normal file
31
database/seeders/new/OrderAddressSeeder.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class OrderAddressSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$items = Items::fromFile(
|
||||
database_path('data/order_address.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
DB::table('orders')->where('id', $data['order_id'])->update([
|
||||
'customer_address' => $data['address'],
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
120
database/seeders/new/OrderSeeder.php
Normal file
120
database/seeders/new/OrderSeeder.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use App\Models\Ecommerce\Product\Order\Payment\OrderPayment;
|
||||
use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping;
|
||||
use App\Models\Ecommerce\Product\Order\Status\OrderStatus;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class OrderSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$this->fillOrders();
|
||||
$this->fillOrderItems();
|
||||
}
|
||||
|
||||
private function fillOrderItems()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'order_items';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/order_items.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
$channel_id = DB::table('channel_product')->where('product_id', $data['item_id'])->first()->channel_id ?? tmpostChannel()->id;
|
||||
|
||||
DB::table($table)->insert([
|
||||
'id' => $data['id'],
|
||||
'order_id' => $data['order_id'],
|
||||
'product_id' => $data['item_id'],
|
||||
'quantity' => $data['quantity'],
|
||||
'unit_price_amount' => $data['price'],
|
||||
|
||||
'channel_id' => $channel_id,
|
||||
|
||||
'created_at' => $data['created_at'],
|
||||
'updated_at' => $data['updated_at'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
private function fillOrders()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'orders';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/orders.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
DB::table($table)->insert([
|
||||
'id' => $data['id'],
|
||||
'number' => Str::random(30),
|
||||
'user_id' => $data['customer_id'],
|
||||
'options' => sprintf('{"seller_id":"%s","warehouse_id":"%s"}', $data['seller_id'], $data['warehouse_id']),
|
||||
'status' => $this->formatStatus($data['status']),
|
||||
'shipping_method' => $this->formatShippingMethod($data['delivery_type']),
|
||||
'payment_type_id' => $this->formatPaymentType($data['payment_type']),
|
||||
'notes' => $data['note'],
|
||||
'created_at' => $data['created_at'],
|
||||
'updated_at' => $data['updated_at'],
|
||||
'deleted_at' => $data['deleted_at'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
}
|
||||
|
||||
private function formatStatus(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
'DRAFT' => OrderStatus::PENDING,
|
||||
'COMPLETED' => OrderStatus::COMPLETED,
|
||||
'DELIVERED' => OrderStatus::COMPLETED,
|
||||
'CANCELLED' => OrderStatus::CANCELLED,
|
||||
default => OrderStatus::default(),
|
||||
};
|
||||
}
|
||||
|
||||
private function formatShippingMethod($shippingMethod): string
|
||||
{
|
||||
return match ($shippingMethod) {
|
||||
'PICK_UP' => OrderShipping::SELF_PICKUP,
|
||||
'EXPRESS_DELIVERY' => OrderShipping::EXPRESS,
|
||||
'SELECTED_DELIVERY' => OrderShipping::STANDART,
|
||||
default => OrderShipping::default(),
|
||||
};
|
||||
}
|
||||
|
||||
private function formatPaymentType($paymentType): int
|
||||
{
|
||||
return match ($paymentType) {
|
||||
'CASH' => 1,
|
||||
'CARD' => 2,
|
||||
default => 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
30
database/seeders/new/ProductBarcodesSeeder.php
Normal file
30
database/seeders/new/ProductBarcodesSeeder.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class ProductBarcodesSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'products';
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/item_barcodes.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
// item_id is product id, I need to find product and update price_amount to $data['value'] and cost_amount to $data['cost_value']
|
||||
DB::table($table)->where('id', $data['item_id'])->update([
|
||||
'barcode' => $data['barcode'],
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
35
database/seeders/new/ProductCategoryRelationshipsSeeder.php
Normal file
35
database/seeders/new/ProductCategoryRelationshipsSeeder.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class ProductCategoryRelationshipsSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'category_product';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/categorizables.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
if ($data['categorizable_type'] == 'Domain\\Item\\Models\\Item') {
|
||||
DB::table($table)->insert([
|
||||
'product_id' => $data['categorizable_id'],
|
||||
'category_id' => $data['category_id'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
31
database/seeders/new/ProductPricesSeeder.php
Normal file
31
database/seeders/new/ProductPricesSeeder.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class ProductPricesSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'products';
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/prices.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
// item_id is product id, I need to find product and update price_amount to $data['value'] and cost_amount to $data['cost_value']
|
||||
DB::table($table)->where('id', $data['item_id'])->update([
|
||||
'price_amount' => $data['value'],
|
||||
'cost_amount' => $data['cost_value'],
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
32
database/seeders/new/ProductPropertiesSeeder.php
Normal file
32
database/seeders/new/ProductPropertiesSeeder.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class ProductPropertiesSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'product_attributes';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/item_properties.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
DB::table($table)->insert([
|
||||
'product_id' => $data['item_id'],
|
||||
'attribute_id' => $data['property_id'],
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
80
database/seeders/new/ProductPropertyValuesSeeder.php
Normal file
80
database/seeders/new/ProductPropertyValuesSeeder.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class ProductPropertyValuesSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$propertyWithValues = $this->fillPropertyValues();
|
||||
$this->fillProductPropertyValues($propertyWithValues);
|
||||
}
|
||||
|
||||
private function fillPropertyValues()
|
||||
{
|
||||
$propertyWithValues = collect();
|
||||
|
||||
DB::transaction(function () use (&$propertyWithValues) {
|
||||
$table = 'attribute_values';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/property_values.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
DB::table($table)->insert([
|
||||
'id' => $data['id'],
|
||||
'key' => $data['id'],
|
||||
'attribute_id' => $data['property_id'],
|
||||
'value' => str_replace('"tm"', '"tk"', $data['name']),
|
||||
'created_at' => $data['created_at'],
|
||||
'updated_at' => $data['updated_at'],
|
||||
]);
|
||||
|
||||
$propertyWithValues->push([
|
||||
'attribute_id' => $data['property_id'],
|
||||
'value_id' => $data['id'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
|
||||
return $propertyWithValues;
|
||||
}
|
||||
|
||||
private function fillProductPropertyValues($propertyWithValues)
|
||||
{
|
||||
DB::transaction(function () use ($propertyWithValues) {
|
||||
$table = 'attribute_value_product_attribute';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/item_property_values.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
DB::table($table)->insert([
|
||||
'product_attribute_id' => $propertyWithValues->firstWhere('value_id', $data['property_value_id'])['attribute_id'],
|
||||
'attribute_value_id' => $data['property_value_id'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
}
|
||||
}
|
||||
39
database/seeders/new/ProductStocksSeeder.php
Normal file
39
database/seeders/new/ProductStocksSeeder.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class ProductStocksSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$items = Items::fromFile(
|
||||
database_path('data/stocks.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
// First we update stock in products table, then in inventory_product table
|
||||
foreach ($items as $data) {
|
||||
DB::table('products')->where('id', $data['item_id'])->update([
|
||||
'stock' => $data['quantity'],
|
||||
]);
|
||||
|
||||
DB::table('inventory_product')
|
||||
->insert([
|
||||
'product_id' => $data['item_id'],
|
||||
'inventory_id' => $data['warehouse_id'],
|
||||
'stock' => $data['quantity'],
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('inventory_product_id_seq', (SELECT MAX(id) from inventory_product))
|
||||
");
|
||||
});
|
||||
}
|
||||
}
|
||||
59
database/seeders/new/ProductsTableSeeder.php
Normal file
59
database/seeders/new/ProductsTableSeeder.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use JsonMachine\Items;
|
||||
use JsonMachine\JsonDecoder\ExtJsonDecoder;
|
||||
|
||||
class ProductsTableSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$table = 'products';
|
||||
$users = User::whereNotNull('options->old_customer_id')->get();
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
$items = Items::fromFile(
|
||||
database_path('data/items.json'),
|
||||
['decoder' => new ExtJsonDecoder(true)]
|
||||
);
|
||||
|
||||
foreach ($items as $data) {
|
||||
$name = json_decode($data['name']);
|
||||
$description = json_decode($data['description']);
|
||||
|
||||
DB::table($table)->insert([
|
||||
'id' => $data['id'],
|
||||
'sku' => $data['sku'],
|
||||
'name' => $name->tm ?? $name->en ?? '',
|
||||
'slug' => $data['slug'],
|
||||
'description' => $description->tm ?? $description->en ?? '',
|
||||
'brand_id' => $data['brand_id'],
|
||||
'is_visible' => $data['is_blocked'],
|
||||
'created_at' => $data['created_at'],
|
||||
'updated_at' => $data['updated_at'],
|
||||
]);
|
||||
|
||||
// We find user with $data->seller_id matching old_customer_id in options, then we get users channel and attach product to it
|
||||
$user = $users->firstWhere(function ($user) use ($data) {
|
||||
return $user->options['old_customer_id'] == $data['seller_id'];
|
||||
});
|
||||
|
||||
$channel = $user ? $user->channel() : tmpostChannel();
|
||||
DB::table('channel_product')->insert([
|
||||
'product_id' => $data['id'],
|
||||
'channel_id' => $channel->id,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
}
|
||||
}
|
||||
34
database/seeders/new/PropertiesTableSeeder.php
Normal file
34
database/seeders/new/PropertiesTableSeeder.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class PropertiesTableSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$datas = json_decode(File::get('database/data/properties.json'));
|
||||
$table = 'attributes';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
foreach ($datas as $data) {
|
||||
DB::table($table)->insertOrIgnore([
|
||||
'id' => $data->id,
|
||||
'slug' => $data->code,
|
||||
'name' => str_replace('"tm"', '"tk"', $data->name),
|
||||
'description' => $data->description,
|
||||
'type' => 'text',
|
||||
'created_at' => $data->created_at,
|
||||
'updated_at' => $data->updated_at,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
}
|
||||
}
|
||||
37
database/seeders/new/SectionsSeeder.php
Normal file
37
database/seeders/new/SectionsSeeder.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders\New;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SectionsSeeder extends Seeder
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
DB::transaction(function () {
|
||||
$datas = json_decode(File::get('database/data/sections.json'));
|
||||
$table = 'collections';
|
||||
|
||||
DB::table($table)->truncate();
|
||||
|
||||
foreach ($datas as $data) {
|
||||
DB::table($table)->insert([
|
||||
'id' => $data->id,
|
||||
'name' => str_replace('"tm"', '"tk"', $data->title),
|
||||
'slug' => Str::slug($data->title).'_'.$data->id,
|
||||
'is_visible' => ! $data->is_blocked,
|
||||
'sort_order' => $data->priority,
|
||||
'created_at' => $data->created_at,
|
||||
'updated_at' => $data->updated_at,
|
||||
]);
|
||||
}
|
||||
|
||||
DB::statement("
|
||||
SELECT setval('{$table}_id_seq', (SELECT MAX(id) from {$table}))
|
||||
");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ class SellersTableSeeder extends Seeder
|
||||
'email' => $data->email,
|
||||
'phone_number' => Str::after($data->phone, '993'),
|
||||
'password' => Hash::make('12345678'),
|
||||
'options' => sprintf('{"old_customer_id":"%s"}', $data->id),
|
||||
]);
|
||||
|
||||
$user->assignRole('vendor');
|
||||
|
||||
@@ -397,5 +397,6 @@
|
||||
"Inventory exit": "Çykyş",
|
||||
"Total count": "Jemi sany",
|
||||
"Verification code is incorrect": "Tassyklaýyş belgi ýalňyş",
|
||||
"User with this phone number does not exist": "GIrizilen telefon belgili ulanyjy tapylmady"
|
||||
"User with this phone number does not exist": "GIrizilen telefon belgili ulanyjy tapylmady",
|
||||
"Page not found": "Sahypa tapylmady"
|
||||
}
|
||||
|
||||
219
resources/views/errors/404.blade.php
Normal file
219
resources/views/errors/404.blade.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>404 - Page Not Found</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #0a1821;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
font-family: Roboto, Arial, sans-serif;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.not-found {
|
||||
width: 560px;
|
||||
height: 225px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
|
||||
.starry-sky {
|
||||
display: block;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.notfound-copy {
|
||||
color: #fff;
|
||||
position: fixed;
|
||||
top: 25px;
|
||||
right: 10%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 700;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 300;
|
||||
color: #fff;
|
||||
border-bottom: 1.5px solid #5581d4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
font-weight: 300;
|
||||
color: #fff;
|
||||
border-bottom: 2px solid #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* change to alternating star opacities */
|
||||
.all-stars {
|
||||
animation: blinkblink 4s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinkblink {
|
||||
50% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-25px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.floating-object {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
transform-box: fill-box;
|
||||
}
|
||||
|
||||
.moon {}
|
||||
|
||||
input[type=text] {
|
||||
color: #fff;
|
||||
background-color: #0a1821;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
border-bottom: 2px solid #ccc;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
input[type=text]:focus {
|
||||
border-color: none;
|
||||
border-bottom: 2px solid #ccc;
|
||||
}
|
||||
|
||||
@media (max-width: 647px) {
|
||||
.moon {
|
||||
padding-top: -500px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="notfound-copy">
|
||||
<svg aria-labelledby="404" alt="404 Page not found" class="not-found">
|
||||
<title id="svgtitle1">404 Page not found</title>
|
||||
<g class="404-text floating-object">
|
||||
<g opacity=".5" fill="#3B3D3D">
|
||||
<path
|
||||
d="M320.1 209.5c0 7.2-6 12.5-13.7 12.5s-13.7-5.4-13.7-12.5v-16.3h-43.8c-8.4 0-14.1-6.4-14.1-13.5 0-.8.8-4.4 2-7L275 84.5c2-4.8 7.2-7.8 12.3-7.8 6.8 0 13.7 6 13.7 12.9 0 1.2 0 2.6-.8 4.4-11.5 26.7-20.9 47.6-32.4 74.2h24.9v-30.4c0-7.2 6-12.5 13.7-12.5 7.4 0 13.7 5.4 13.7 12.5v30.4h2.6c7.6 0 13.3 5.8 13.3 12.3 0 7-5.8 12.5-13.3 12.5h-2.6v16.5zM436.9 123.9v54.7c0 24.3-16.3 43.6-46 43.6s-46-19.3-46-43.6v-54.7c0-26.3 13.9-47.4 46-47.4 32.1.1 46 21.2 46 47.4zm-64.3-1.4v56.1c0 11.3 6.4 19.1 18.3 19.1s18.3-7.8 18.3-19.1v-56.1c0-12.7-5.2-21.7-18.3-21.7s-18.3 9-18.3 21.7zM535 209.5c0 7.2-6 12.5-13.7 12.5s-13.7-5.4-13.7-12.5v-16.3h-43.8c-8.4 0-14.1-6.4-14.1-13.5 0-.8.8-4.4 2-7l38.2-88.2c2-4.8 7.2-7.8 12.3-7.8 6.8 0 13.7 6 13.7 12.9 0 1.2 0 2.6-.8 4.4-11.5 26.7-20.9 47.6-32.4 74.2h24.9v-30.4c0-7.2 6-12.5 13.7-12.5 7.4 0 13.7 5.4 13.7 12.5v30.4h2.6c7.6 0 13.3 5.8 13.3 12.3 0 7-5.8 12.5-13.3 12.5H535v16.5z" />
|
||||
</g>
|
||||
<g fill="#FFF">
|
||||
<path
|
||||
d="M326.4 197.5c0 7.2-6 12.5-13.7 12.5s-13.7-5.4-13.7-12.5v-16.3h-43.8c-8.4 0-14.1-6.4-14.1-13.5 0-.8.8-4.4 2-7l38.2-88.2c2-4.8 7.2-7.8 12.3-7.8 6.8 0 13.7 6 13.7 12.9 0 1.2 0 2.6-.8 4.4-11.5 26.7-20.9 47.6-32.4 74.2H299v-30.4c0-7.2 6-12.5 13.7-12.5 7.4 0 13.7 5.4 13.7 12.5v30.4h2.6c7.6 0 13.3 5.8 13.3 12.3 0 7-5.8 12.5-13.3 12.5h-2.6v16.5zM443.2 111.9v54.7c0 24.3-16.3 43.6-46 43.6s-46-19.3-46-43.6v-54.7c0-26.3 13.9-47.4 46-47.4 32.1.1 46 21.2 46 47.4zm-64.2-1.4v56.1c0 11.3 6.4 19.1 18.3 19.1s18.3-7.8 18.3-19.1v-56.1c0-12.7-5.2-21.7-18.3-21.7s-18.3 9-18.3 21.7zM541.4 197.5c0 7.2-6 12.5-13.7 12.5-7.8 0-13.7-5.4-13.7-12.5v-16.3h-43.8c-8.4 0-14.1-6.4-14.1-13.5 0-.8.8-4.4 2-7l38.2-88.2c2-4.8 7.2-7.8 12.3-7.8 6.8 0 13.7 6 13.7 12.9 0 1.2 0 2.6-.8 4.4-11.5 26.7-20.9 47.6-32.4 74.2H514v-30.4c0-7.2 6-12.5 13.7-12.5 7.4 0 13.7 5.4 13.7 12.5v30.4h2.6c7.6 0 13.3 5.8 13.3 12.3 0 7-5.8 12.5-13.3 12.5h-2.6v16.5z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<h1>{{ __('Page not found') }}</h1>
|
||||
<p><a href="/">{{ __('Home') }}</a></p>
|
||||
</div>
|
||||
|
||||
<svg aria-labelledby="Starry sky" alt="Starry sky" class="starry-sky">
|
||||
<title id="svgtitle2">Starry sky</title>
|
||||
<!-- STARS START -->
|
||||
<g class="all-stars" fill="#F6F5BC">
|
||||
<path class="stars-one"
|
||||
d="M148.9 151.5c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM93.6 " />
|
||||
<path class="stars-two"
|
||||
d="M148.9 540.6c-4.9-1.6-6.2-6.2-6.2-6.2-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.8-.4-1zM526.3 551.7c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM617.4 291.9c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.6-6.2-6.2-6.2-6.2zM681.9 42.7c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM51.1 83.9c-.3-.3-.6-.4-1-.4s-.7.1-1 .4C47.5 88.8 43 90 43 90c-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5.1-1.6-6.3-6.1-6.3-6.1z" />
|
||||
<path class="all-stars"
|
||||
d="M148.9 151.5c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM93.6 318.5c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM132.3 169c-.2-.2-.4-.3-.6-.3s-.4.1-.6.3c-1 3.1-3.8 3.8-3.8 3.8-.2.2-.3.4-.3.6 0 .2.1.4.3.6 3.1 1 3.8 3.8 3.8 3.8.2.2.4.3.6.3s.4-.1.6-.3c1-3.1 3.8-3.8 3.8-3.8.2-.2.3-.4.3-.6 0-.2-.1-.4-.3-.6-3-.9-3.8-3.8-3.8-3.8zM585.9 269.5c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM723.4 540.6c-4.9-1.6-6.2-6.2-6.2-6.2-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.8-.4-1zM526.3 551.7c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM617.4 291.9c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.6-6.2-6.2-6.2-6.2zM681.9 42.7c-.3-.3-.6-.4-1-.4s-.7.1-1 .4c-1.6 4.9-6.2 6.2-6.2 6.2-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5-1.7-6.2-6.2-6.2-6.2zM51.1 83.9c-.3-.3-.6-.4-1-.4s-.7.1-1 .4C47.5 88.8 43 90 43 90c-.3.3-.4.6-.4 1s.1.7.4 1c4.9 1.6 6.2 6.2 6.2 6.2.3.3.6.4 1 .4s.7-.1 1-.4c1.6-4.9 6.2-6.2 6.2-6.2.3-.3.4-.6.4-1s-.1-.7-.4-1c-5.1-1.6-6.3-6.1-6.3-6.1z" />
|
||||
</g>
|
||||
<!-- Moon shadow -->
|
||||
<g class="moon">
|
||||
<g class="floating-object">
|
||||
<path opacity=".5" fill="#3B3D3D" d="M122.7 373.9L-237.1 744l283.8 269.4 279.9-492.8z" />
|
||||
<!-- Moon color base -->
|
||||
<g fill="#D1D5D6">
|
||||
<path
|
||||
d="M209 340.6c-70.9 0-128.4 57.5-128.4 128.4S138.1 597.4 209 597.4 337.4 539.9 337.4 469 279.9 340.6 209 340.6zm-41.9 176.1c0 10.9-8.9 19.8-19.8 19.8s-19.8-8.9-19.8-19.8c0-1.4.1-2.7.4-4-18.3-1.7-32.6-17-32.6-35.7 0-19.8 16.1-35.9 35.9-35.9 19.8 0 35.9 16.1 35.9 35.9 0 8.9-3.3 17.1-8.6 23.3 5.1 3.7 8.6 9.7 8.6 16.4zm20.2 3.9c0-8.8 7.1-16 16-16 8.8 0 16 7.1 16 16 0 8.8-7.1 16-16 16-8.9-.1-16-7.2-16-16zm5.8-43.5c0-13.3 10.7-24 24-24s24 10.7 24 24-10.7 24-24 24-24-10.8-24-24zm72.7 86.4c-4.2 0-8.2-.7-11.9-2-.5 12.8-11 23.1-24 23.1-13.3 0-24-10.7-24-24s10.7-24 24-24h1.1c-.7-2.9-1.1-5.8-1.1-8.9 0-19.8 16.1-35.9 35.9-35.9 19.8 0 35.9 16.1 35.9 35.9s-16.1 35.8-35.9 35.8zm10.9-91.1c0 2.6-2.1 4.6-4.6 4.6-2.6 0-4.6-2.1-4.6-4.6 0-2.6 2.1-4.6 4.6-4.6 2.5 0 4.6 2.1 4.6 4.6zm27.7 13.1c-2.1 2-4.9 3.2-8.1 3.2-6.5 0-11.7-5.2-11.7-11.7 0-6.5 5.2-11.7 11.7-11.7.5 0 1.1 0 1.6.1-1.5-3.6-3.2-7.1-5.1-10.5 2.5-4 3.9-8.7 3.9-13.8 0-14.6-11.8-26.4-26.4-26.4-3.6 0-7 .7-10.2 2-4.9-3.8-10.2-7.2-15.7-10.2.1 1 .2 2 .2 3.1 0 17.4-14.1 31.5-31.5 31.5-16.7 0-30.4-13-31.4-29.5 0-.7-.1-1.3-.1-2 0-3.4.5-6.6 1.5-9.6.9-2.8 2.2-5.5 3.8-7.9-2.2 0-4.3.1-6.5.2-3.8-6.7-11-11.2-19.2-11.2-11.8 0-21.5 9.3-22.1 21-17.1 7.5-32.1 18.8-43.9 32.8 14.3-50.3 60.6-87.2 115.6-87.2 66.3 0 120.1 53.8 120.1 120.1 0 29.5-10.6 56.4-28.2 77.3 3-10.5 4.6-21.5 4.6-32.9.1-9.1-.9-18.1-2.9-26.7z" />
|
||||
<circle cx="256.3" cy="384.3" r="14.1" />
|
||||
</g>
|
||||
<g fill="#FFF">
|
||||
<circle cx="217.1" cy="477.1" r="24" />
|
||||
<path
|
||||
d="M265.8 491.7c-19.8 0-35.9 16.1-35.9 35.9 0 3.1.4 6.1 1.1 8.9h-1.1c-13.3 0-24 10.7-24 24s10.7 24 24 24c12.9 0 23.5-10.2 24-23.1 3.7 1.3 7.7 2 11.9 2 19.8 0 35.9-16.1 35.9-35.9s-16.1-35.8-35.9-35.8zM131.2 441.2c-19.8 0-35.9 16.1-35.9 35.9 0 18.7 14.3 34.1 32.6 35.7-.3 1.3-.4 2.6-.4 4 0 10.9 8.9 19.8 19.8 19.8s19.8-8.9 19.8-19.8c0-6.8-3.4-12.8-8.6-16.3 5.4-6.3 8.6-14.4 8.6-23.3 0-20-16.1-36-35.9-36z" />
|
||||
<path
|
||||
d="M331 467.9c0-66.3-53.8-120.1-120.1-120.1-54.9 0-101.3 36.9-115.6 87.2 11.8-14.1 26.9-25.4 43.9-32.8.6-11.7 10.2-21 22.1-21 8.2 0 15.4 4.5 19.2 11.2 2.2-.1 4.3-.2 6.5-.2-1.6 2.4-2.9 5.1-3.8 7.9-1 3-1.5 6.3-1.5 9.6 0 .7 0 1.3.1 2 1 16.5 14.7 29.5 31.4 29.5 17.4 0 31.5-14.1 31.5-31.5 0-1-.1-2.1-.2-3.1 5.5 3 10.7 6.4 15.7 10.2 3.1-1.3 6.6-2 10.2-2 14.6 0 26.4 11.8 26.4 26.4 0 5-1.4 9.8-3.9 13.8 1.9 3.4 3.6 6.9 5.1 10.5-.5-.1-1.1-.1-1.6-.1-6.5 0-11.7 5.2-11.7 11.7 0 6.5 5.2 11.7 11.7 11.7 3.1 0 6-1.2 8.1-3.2 2 8.6 3 17.6 3 26.8 0 11.4-1.6 22.5-4.6 32.9 17.5-21 28.1-48 28.1-77.4z" />
|
||||
<circle cx="203.2" cy="520.6" r="16" />
|
||||
<circle cx="272.1" cy="472.4" r="4.6" />
|
||||
</g>
|
||||
<path opacity=".1" fill-rule="evenodd" clip-rule="evenodd" fill="#3B3D3D"
|
||||
d="M201.2 368.2l-.4-.8c-.4-.7-1.4-1-2.1-.6l-80.5 46.8c-.7.4-1 1.4-.6 2.1l.4.8c.3.5.8.8 1.4.8.3 0 .5-.1.8-.2l1-.6c0 .1.1.3.2.4l24.2 41.7c.2.4.7.7 1.2.7.2 0 .5-.1.7-.2l34.1-19.8c.3-.2.5-.5.6-.8.1-.4 0-.7-.1-1l-24.2-41.7-.3-.3 43.2-25.1c.4-.2.6-.5.7-1-.1-.4-.1-.8-.3-1.2z" />
|
||||
<!-- Flag stick -->
|
||||
<g>
|
||||
<path fill="#acacac" stroke="#B5B5B6" stroke-miterlimit="10"
|
||||
d="M200.8 369.5c-.2.6-.8 1-1.4.8l-.9-.2c-.6-.2-1-.8-.8-1.4l25.5-89.5c.2-.6.8-1 1.4-.8l.9.2c.6.2 1 .8.8 1.4l-25.5 89.5z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#263563" stroke="#B5B5B6" stroke-miterlimit="10"
|
||||
d="M262.9 334c-.1.5-.7.8-1.2.7l-46.4-13.2c-.5-.1-.8-.7-.7-1.2l10.8-38c.1-.5.7-.8 1.2-.7l46.4 13.2c.5.1.8.7.7 1.2l-10.8 38z" />
|
||||
<g fill-rule="evenodd" clip-rule="evenodd" fill="#263563"> <!-- #F9DA1E -->
|
||||
<path
|
||||
d="M249 311.1c.4.6.8 1.1 1.2 1.6.4.5.9.9.3 1.6.8.2 1.6.3 2.3.5-.1.4-.2.6-.2.9 0 .1.1.2.1.3.1-.1.2-.1.2-.2l.3-.9c.7.3 1.4.5 2.1.7.1-.8.1-.8 1-1.2.7-.3 1.3-.5 2-.8-.5-.7-.5-.7 0-1.3.1-.2.2-.4.4-.5-.5-.5-1.6.1-1.6-1.1-.4.3-.7.5-1 .7-.2.1-.4.1-.6.2 0-.2 0-.4.1-.6l.9-1.8c-.3.1-.6.1-.8.2-.2-.7-.3-1.3-.5-2-.5.5-1 .9-1.5 1.4l-.6-.6c-.1.8-.1 1.5-.2 2.3 0 .1-.1.3-.2.4-.1-.1-.3-.2-.3-.3-.2-.4-.3-.8-.5-1.2-.6.5-.6.5-1.3.2-.2-.1-.4-.1-.6-.2-.4.5.4 1.5-1 1.7z" />
|
||||
<path
|
||||
d="M260.2 298.6c-.6 1.1-1.2 2.2-1.8 3.4-.6 1.3-1.3 2.5-1.9 3.8-.4.8-.5.9-1.4.4.2-.8.4-1.6.5-2.4.1-.5.1-1 .2-1.5.1-1.8-1.4-3.7-3.2-3.9-.1 0-.4.2-.5.3-.9 1.6-1.7 3.3-2.5 5 0 .1-.1.2-.1.2-.2.2-.1.6-.6.4-.4-.2-.1-.4-.1-.7.2-.9.4-1.8.5-2.8 0-.7-.1-1.4-.3-2.1-.1-.4-.3-.5-.8-.4-1.8.7-3.3 1.7-4.5 3.1-2.5 2.6-4.3 5.6-5.2 9.1-.5 1.9-.5 3.8.3 5.6.8 1.7 1.2 1.8 2.8.8.7-.4 1.3-.9 2-1.4.3-.2.6-.3 1 0-1.2 1-2.4 2-3.7 2.9.7.6 1.6.8 2.6 1 2.5.4 4.6-.7 6.4-2.3.7-.6 1.2-.5 1.9-.2v.1c-2 2.2-4.2 4.1-7.3 4.3-2.3.2-4.4-.4-6.4-1.8-.2-.2-.5-.3-.8-.3-5.5.1-9.3-3.9-9-9.4 0-.7.1-1.4.2-2 .2-.6.4-1.3.5-1.9.1-.2.2-.3.3-.5.6-1.2 1.1-2.5 1.9-3.6 2.5-3.8 5.9-6.4 10.5-7.2 1.9-.3 3.9-.3 5.6.6 1.1.6 2.1.7 3.3.7.6 0 1.2.1 1.8.1.5.1.9.3 1.4.4.2.1.5.3.7.4.8.6 1.6 1.1 2.4 1.7.4.3.8.4 1.2.1.4-.2.7-.5 1.1-.7.3.5.7.6 1 .7zm-13.4-2c-.7-.5-2.1-.7-3.4-.6-2.1.2-3.8 1.2-5.3 2.4-2.4 2-4.2 4.6-5.5 7.5-.8 1.7-1.3 3.5-1.3 5.4 0 2.5.8 4.6 3.1 5.9.6.3 1.2.5 1.9.8 0-.1.1-.1.1-.2s-.1-.2-.1-.2c-1-1.5-1.4-3.1-1.4-4.9-.1-2.3.5-4.5 1.5-6.6 1.6-3.3 3.9-6.1 7.1-8 .9-.5 2.1-.9 3.3-1.5z" />
|
||||
<path
|
||||
d="M249 311.1c1.4-.1.5-1.1.8-1.8.2.1.4.1.6.2.7.3.7.3 1.3-.2.2.4.3.8.5 1.2l.3.3c.1-.1.2-.3.2-.4.1-.7.1-1.5.2-2.3l.6.6c.5-.4 1-.9 1.5-1.4.2.7.4 1.3.5 2 .3-.1.5-.1.8-.2-.3.7-.6 1.2-.9 1.8-.1.2-.1.4-.1.6.2-.1.4-.1.6-.2.3-.2.6-.4 1-.7 0 1.2 1.1.7 1.6 1.1-.1.2-.2.3-.4.5-.5.7-.5.7 0 1.3-.7.3-1.3.5-2 .8-.8.3-.8.3-1 1.2-.7-.2-1.3-.5-2.1-.7l-.3.9c0 .1-.2.2-.2.2 0-.1-.1-.2-.1-.3 0-.3.1-.5.2-.9-.7-.2-1.5-.3-2.3-.5.6-.7.1-1.2-.3-1.6-.1-.5-.5-1-1-1.5z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<script>
|
||||
// Use requestAnimationFrame for smooth animation performance
|
||||
let rafId = null;
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (rafId) return;
|
||||
|
||||
rafId = requestAnimationFrame(() => {
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
const mouseX = e.clientX;
|
||||
const mouseY = e.clientY;
|
||||
|
||||
const xVal = (mouseX - width / 2) / width;
|
||||
const yVal = (mouseY - height / 2) / height;
|
||||
|
||||
const moon = document.querySelector('.moon');
|
||||
const stars = document.querySelector('.all-stars');
|
||||
const notFound = document.querySelector('.notfound-copy');
|
||||
|
||||
// Parallax factors - adjust multipliers to control speed/depth
|
||||
if (moon) moon.style.transform = `translate(${xVal * 40}px, ${yVal * 40}px)`;
|
||||
if (stars) stars.style.transform = `translate(${xVal * 20}px, ${yVal * 20}px)`;
|
||||
// Inverse movement for foreground to create depth
|
||||
if (notFound) notFound.style.transform = `translate(${xVal * -30}px, ${yVal * -30}px)`;
|
||||
|
||||
rafId = null;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -217,7 +217,7 @@ test('can list related products', function () {
|
||||
// Then it selects product_ids from those categories.
|
||||
// It doesn't explicitly exclude the main product ID in the query shown in tool output?
|
||||
// Let's check the controller code again.
|
||||
// It selects `product_has_relations.product_id` where `productable_id` IN (categories of main product).
|
||||
// It selects `category_product.product_id` where `category_id` IN (categories of main product).
|
||||
// It does NOT seem to exclude the main product ID in the query.
|
||||
// However, `unique()` is used.
|
||||
// If the main product is returned, count would be 3. If excluded, 2.
|
||||
|
||||
Reference in New Issue
Block a user