Stage 12 — Online Store: public catalog + cart + orders
Schema: - online_orders (token-tracked, status workflow, delivery method/fee) - online_order_items (price snapshot, fulfilled flag) - part_cross_refs (OEM/equivalent codes for search) - parts.is_published (shop visibility) Storefront (ShopController, tenant subdomain, /shop): - Catalog with search across name/article/brand/cross-refs, category + in-stock filters, live stock, white-label themed layout - Part detail page with cross-ref codes - VIN search → VinDecoder → guided catalog search - Session cart (per-tenant key), guest checkout, order confirmation page - Respects settings.shop.enabled (404 when off); tenant-guarded Part::searchPublished matches cross-ref articles via whereHas. Order notifications (ShopOrderNotifier, best-effort): - Staff: Web Push to active users - Customer: Telegram if phone matches a linked client Filament (tenant): - OnlineOrderResource under "Magazin" nav group, status workflow, items relation, "Onorează" action issues stock via WarehouseService (FIFO) - PartResource: is_published toggle + column + bulk publish/unpublish + CrossRefsRelationManager - Settings: shop section (enable, delivery methods, fee, free-over) - Landing page: shop button when enabled Tests (6 new): - catalog 404 when disabled; lists published only; cross-ref search; order placement (token + items + total); fulfill issues stock; cross-tenant token isolation Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Tenant;
|
||||
|
||||
use App\Models\Concerns\BelongsToTenant;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class OnlineOrderItem extends Model
|
||||
{
|
||||
use BelongsToTenant;
|
||||
|
||||
protected $fillable = [
|
||||
'company_id', 'online_order_id', 'part_id',
|
||||
'name', 'article', 'qty', 'price', 'total', 'fulfilled',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'qty' => 'decimal:2',
|
||||
'price' => 'decimal:2',
|
||||
'total' => 'decimal:2',
|
||||
'fulfilled' => 'boolean',
|
||||
];
|
||||
|
||||
public function order(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(OnlineOrder::class, 'online_order_id');
|
||||
}
|
||||
|
||||
public function part(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Part::class);
|
||||
}
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::saving(function (self $row) {
|
||||
$row->total = round((float) $row->qty * (float) $row->price, 2);
|
||||
});
|
||||
static::saved(fn (self $row) => $row->order?->recalcTotal());
|
||||
static::deleted(fn (self $row) => $row->order?->recalcTotal());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user