new: add channel management
This commit is contained in:
@@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Shop;
|
namespace App\Http\Controllers\Shop;
|
||||||
|
|
||||||
|
use App\Repositories\Core\User\ShopCart;
|
||||||
|
use App\Repositories\Shop\Baskets;
|
||||||
use App\Repositories\Shop\CustomerAddresses;
|
use App\Repositories\Shop\CustomerAddresses;
|
||||||
use App\Repositories\Shop\Customers;
|
use App\Repositories\Shop\Customers;
|
||||||
|
use App\Repositories\Shop\Offers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
@@ -44,8 +47,50 @@ class CustomerController extends Controller
|
|||||||
public function storeProfileAjax(Request $request)
|
public function storeProfileAjax(Request $request)
|
||||||
{
|
{
|
||||||
$data = $request->all();
|
$data = $request->all();
|
||||||
|
|
||||||
|
if (array_key_exists('default_sale_channel_id', $data)) {
|
||||||
|
$customer = Customers::get(Customers::getId());
|
||||||
|
$newSaleChannelId = (int) $data['default_sale_channel_id'];
|
||||||
|
$currentSaleChannelId = (int) ($customer->default_sale_channel_id ?? 0);
|
||||||
|
|
||||||
|
if ($newSaleChannelId && $newSaleChannelId !== $currentSaleChannelId && ShopCart::count() > 0) {
|
||||||
|
$cartItems = ShopCart::getContent();
|
||||||
|
$unavailable = [];
|
||||||
|
|
||||||
|
foreach ($cartItems as $item) {
|
||||||
|
$offerId = (int) $item->id;
|
||||||
|
|
||||||
|
if (! Offers::getPrice($offerId, 1, $newSaleChannelId)) {
|
||||||
|
$offer = Offers::get($offerId, ['article']);
|
||||||
|
$unavailable[] = $offer->article->name ?? $item->name;
|
||||||
|
|
||||||
|
if (count($unavailable) >= 3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($unavailable)) {
|
||||||
|
$list = implode(', ', $unavailable);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'error' => 1,
|
||||||
|
'message' => __('Certains articles de votre panier ne sont pas disponibles dans ce canal : :products. Merci de finaliser votre commande ou de retirer ces articles avant de changer de canal.', ['products' => $list]),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$customer = Customers::store($data);
|
$customer = Customers::store($data);
|
||||||
|
|
||||||
|
if ($customer) {
|
||||||
|
Customers::guard()->setUser($customer->fresh(['sale_channels']));
|
||||||
|
if (array_key_exists('default_sale_channel_id', $data)) {
|
||||||
|
session(['shop.default_sale_channel_id' => $data['default_sale_channel_id']]);
|
||||||
|
Baskets::refreshPrices((int) $data['default_sale_channel_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return response()->json(['error' => 0]);
|
return response()->json(['error' => 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Repositories\Shop;
|
|||||||
use App\Models\Shop\Article;
|
use App\Models\Shop\Article;
|
||||||
use App\Repositories\Botanic\Species;
|
use App\Repositories\Botanic\Species;
|
||||||
use App\Repositories\Botanic\Varieties;
|
use App\Repositories\Botanic\Varieties;
|
||||||
|
use App\Repositories\Shop\SaleChannels;
|
||||||
use App\Repositories\Core\Comments;
|
use App\Repositories\Core\Comments;
|
||||||
use App\Traits\Model\Basic;
|
use App\Traits\Model\Basic;
|
||||||
use App\Traits\Repository\Imageable;
|
use App\Traits\Repository\Imageable;
|
||||||
@@ -70,9 +71,14 @@ class Articles
|
|||||||
|
|
||||||
public static function getArticleToSell($id, $saleChannelId = false)
|
public static function getArticleToSell($id, $saleChannelId = false)
|
||||||
{
|
{
|
||||||
|
$saleChannelId = $saleChannelId ?: SaleChannels::getDefaultID();
|
||||||
$data = self::getArticle($id);
|
$data = self::getArticle($id);
|
||||||
$data['offers'] = self::getOffersGroupedByNature($id, $saleChannelId);
|
$data['offers'] = self::getOffersGroupedByNature($id, $saleChannelId);
|
||||||
|
|
||||||
|
$currentSaleChannel = $saleChannelId ? SaleChannels::get($saleChannelId) : null;
|
||||||
|
$data['current_sale_channel'] = $currentSaleChannel ? $currentSaleChannel->toArray() : null;
|
||||||
|
$data['available_sale_channels'] = Offers::getSaleChannelsForArticle($id);
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,12 +100,19 @@ class Baskets
|
|||||||
$offers = Offers::getWithPricesByIds(self::getIds(), $saleChannelId);
|
$offers = Offers::getWithPricesByIds(self::getIds(), $saleChannelId);
|
||||||
foreach ($basket as $item) {
|
foreach ($basket as $item) {
|
||||||
$offer = $offers->where('id', $item->id)->first();
|
$offer = $offers->where('id', $item->id)->first();
|
||||||
|
if (! $offer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$priceValue = Offers::getPrice($item->id, $item->quantity, $saleChannelId);
|
||||||
|
$unitPrice = $priceValue ? (float) $priceValue->price_taxed : (float) $item->price;
|
||||||
|
|
||||||
$article_nature = strtolower($offer->article->article_nature->name);
|
$article_nature = strtolower($offer->article->article_nature->name);
|
||||||
$data[$article_nature][] = [
|
$data[$article_nature][] = [
|
||||||
'id' => (int) $item->id,
|
'id' => (int) $item->id,
|
||||||
'name' => $item->name,
|
'name' => $item->name,
|
||||||
'quantity' => (int) $item->quantity,
|
'quantity' => (int) $item->quantity,
|
||||||
'price' => $item->price,
|
'price' => $unitPrice,
|
||||||
'variation' => $offer->variation->name,
|
'variation' => $offer->variation->name,
|
||||||
'image' => Articles::getPreviewSrc(ArticleImages::getFullImageByArticle($offer->article)),
|
'image' => Articles::getPreviewSrc(ArticleImages::getFullImageByArticle($offer->article)),
|
||||||
'latin' => $offer->article->product->specie->latin ?? false,
|
'latin' => $offer->article->product->specie->latin ?? false,
|
||||||
@@ -115,6 +122,24 @@ class Baskets
|
|||||||
return $data ?? false;
|
return $data ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function refreshPrices($saleChannelId = false)
|
||||||
|
{
|
||||||
|
$saleChannelId = $saleChannelId ? $saleChannelId : SaleChannels::getDefaultID();
|
||||||
|
$basket = ShopCart::getContent();
|
||||||
|
|
||||||
|
foreach ($basket as $item) {
|
||||||
|
$priceValue = Offers::getPrice($item->id, $item->quantity, $saleChannelId);
|
||||||
|
|
||||||
|
if (! $priceValue) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShopCart::get()->update($item->id, [
|
||||||
|
'price' => $priceValue->price_taxed,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function getBasketData($id, $quantity = 1)
|
public static function getBasketData($id, $quantity = 1)
|
||||||
{
|
{
|
||||||
$offer = Offers::get($id, ['article', 'variation']);
|
$offer = Offers::get($id, ['article', 'variation']);
|
||||||
|
|||||||
@@ -31,18 +31,33 @@ class Customers
|
|||||||
public static function getSaleChannels($customerId = false)
|
public static function getSaleChannels($customerId = false)
|
||||||
{
|
{
|
||||||
$customer = $customerId ? self::get($customerId) : self::getAuth();
|
$customer = $customerId ? self::get($customerId) : self::getAuth();
|
||||||
$saleChannels = $customer ? $customer->sale_channels : false;
|
$saleChannels = collect();
|
||||||
|
|
||||||
return $saleChannels ? $saleChannels : SaleChannels::getDefault();
|
if ($customer) {
|
||||||
|
$customer->loadMissing('sale_channels');
|
||||||
|
$saleChannels = $customer->sale_channels ?? collect();
|
||||||
|
|
||||||
|
if ($saleChannels instanceof \Illuminate\Support\Collection && $saleChannels->isNotEmpty()) {
|
||||||
|
return $saleChannels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$default = SaleChannels::getDefault($customerId);
|
||||||
|
|
||||||
|
return $default ? collect([$default]) : collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSaleChannel($customerId = false)
|
public static function getSaleChannel($customerId = false)
|
||||||
{
|
{
|
||||||
$saleChannels = self::getSaleChannels($customerId);
|
$saleChannels = self::getSaleChannels($customerId);
|
||||||
|
|
||||||
|
if ($saleChannels instanceof \Illuminate\Support\Collection) {
|
||||||
return $saleChannels->first();
|
return $saleChannels->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $saleChannels;
|
||||||
|
}
|
||||||
|
|
||||||
public static function getDeliveries()
|
public static function getDeliveries()
|
||||||
{
|
{
|
||||||
$customer = self::getAuth();
|
$customer = self::getAuth();
|
||||||
@@ -58,12 +73,22 @@ class Customers
|
|||||||
|
|
||||||
public static function editProfile($id = false)
|
public static function editProfile($id = false)
|
||||||
{
|
{
|
||||||
return $id ? [
|
if (! $id) {
|
||||||
'customer' => self::get($id, ['addresses', 'deliveries'])->toArray(),
|
abort('403');
|
||||||
'deliveries' => Deliveries::getAllWithSaleChannel()->toArray(),
|
}
|
||||||
|
|
||||||
|
$customer = self::get($id, ['addresses', 'deliveries', 'sale_channels']);
|
||||||
|
|
||||||
|
$saleChannels = self::getSaleChannels($id);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'customer' => $customer->toArray(),
|
||||||
|
'sale_channels' => $saleChannels->toArray(),
|
||||||
|
'deliveries' => Deliveries::getByCustomer($id)->toArray(),
|
||||||
|
'sale_channel_checks' => Shop::getSaleChannelAvailabilitySummary($saleChannels->pluck('id')->toArray()),
|
||||||
'orders' => (new CustomerOrdersDataTable())->html(),
|
'orders' => (new CustomerOrdersDataTable())->html(),
|
||||||
'invoices' => (new CustomerInvoicesDataTable())->html(),
|
'invoices' => (new CustomerInvoicesDataTable())->html(),
|
||||||
] : abort('403');
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getAddresses($id = false)
|
public static function getAddresses($id = false)
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ class Deliveries
|
|||||||
$customer = $customerId ? Customers::get($customerId) : Customers::getAuth();
|
$customer = $customerId ? Customers::get($customerId) : Customers::getAuth();
|
||||||
$saleChannels = $customer ? $customer->sale_channels->pluck('id')->toArray() : [SaleChannels::getDefaultID()];
|
$saleChannels = $customer ? $customer->sale_channels->pluck('id')->toArray() : [SaleChannels::getDefaultID()];
|
||||||
|
|
||||||
return $saleChannels ? self::getBySaleChannels($saleChannels) : false;
|
return $saleChannels ? self::getBySaleChannels($saleChannels) : collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getBySaleChannels($saleChannels)
|
public static function getBySaleChannels($saleChannels)
|
||||||
{
|
{
|
||||||
return Delivery::bySaleChannels($saleChannels)->with('sale_channel')->get();
|
return Delivery::bySaleChannels($saleChannels)->active()->with('sale_channel')->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSaleChannelId($deliveryId)
|
public static function getSaleChannelId($deliveryId)
|
||||||
@@ -41,7 +41,7 @@ class Deliveries
|
|||||||
|
|
||||||
public static function getAllWithSaleChannel()
|
public static function getAllWithSaleChannel()
|
||||||
{
|
{
|
||||||
return Delivery::orderBy('name', 'asc')->active()->public()->with('sale_channel')->get();
|
return Delivery::orderBy('name', 'asc')->active()->with('sale_channel')->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function toggleActive($id, $active)
|
public static function toggleActive($id, $active)
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace App\Repositories\Shop;
|
namespace App\Repositories\Shop;
|
||||||
|
|
||||||
use App\Models\Shop\Offer;
|
use App\Models\Shop\Offer;
|
||||||
|
use App\Models\Shop\PriceListValue;
|
||||||
|
use App\Models\Shop\SaleChannel;
|
||||||
use App\Traits\Model\Basic;
|
use App\Traits\Model\Basic;
|
||||||
|
|
||||||
class Offers
|
class Offers
|
||||||
@@ -166,4 +168,42 @@ class Offers
|
|||||||
{
|
{
|
||||||
return Offer::query();
|
return Offer::query();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getSaleChannelsForArticle($articleId)
|
||||||
|
{
|
||||||
|
$channels = SaleChannel::query()
|
||||||
|
->whereHas('price_lists', function ($query) use ($articleId) {
|
||||||
|
$query->whereHas('tariff.offers', function ($subQuery) use ($articleId) {
|
||||||
|
$subQuery->byArticle($articleId);
|
||||||
|
})->whereHas('price_list_values');
|
||||||
|
})
|
||||||
|
->orderBy('name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$offers = Offer::query()
|
||||||
|
->byArticle($articleId)
|
||||||
|
->with('article')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $channels->map(function ($channel) use ($offers) {
|
||||||
|
$priceValue = null;
|
||||||
|
|
||||||
|
foreach ($offers as $offer) {
|
||||||
|
$priceCandidate = self::getPrice($offer->id, 1, $channel->id);
|
||||||
|
|
||||||
|
if ($priceCandidate && (float) $priceCandidate->price_taxed > 0) {
|
||||||
|
$priceValue = $priceCandidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $channel->id,
|
||||||
|
'name' => $channel->name,
|
||||||
|
'code' => $channel->code,
|
||||||
|
'price_taxed' => $priceValue ? (float) $priceValue->price_taxed : null,
|
||||||
|
'quantity' => $priceValue ? (int) $priceValue->quantity : null,
|
||||||
|
];
|
||||||
|
})->toArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,52 @@
|
|||||||
namespace App\Repositories\Shop;
|
namespace App\Repositories\Shop;
|
||||||
|
|
||||||
use App\Models\Shop\SaleChannel;
|
use App\Models\Shop\SaleChannel;
|
||||||
|
use App\Repositories\Shop\Customers;
|
||||||
use App\Traits\Model\Basic;
|
use App\Traits\Model\Basic;
|
||||||
|
|
||||||
class SaleChannels
|
class SaleChannels
|
||||||
{
|
{
|
||||||
use Basic;
|
use Basic;
|
||||||
|
|
||||||
public static function getDefaultID()
|
public static function getDefaultID($customerId = false)
|
||||||
{
|
{
|
||||||
$default = self::getDefault();
|
$default = self::getDefault($customerId);
|
||||||
|
|
||||||
return $default ? self::getDefault()->id : false;
|
return $default ? $default->id : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getDefault()
|
public static function getDefault($customerId = false)
|
||||||
{
|
{
|
||||||
|
$sessionChannelId = session('shop.default_sale_channel_id');
|
||||||
|
if ($sessionChannelId) {
|
||||||
|
$sessionChannel = SaleChannel::find($sessionChannelId);
|
||||||
|
if ($sessionChannel) {
|
||||||
|
return $sessionChannel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$customer = $customerId ? Customers::get($customerId) : Customers::getAuth();
|
||||||
|
|
||||||
|
if ($customer) {
|
||||||
|
$customer->loadMissing('sale_channels');
|
||||||
|
|
||||||
|
if ($customer->default_sale_channel_id) {
|
||||||
|
$preferred = $customer->sale_channels->firstWhere('id', $customer->default_sale_channel_id);
|
||||||
|
if (! $preferred) {
|
||||||
|
$preferred = SaleChannel::find($customer->default_sale_channel_id);
|
||||||
|
}
|
||||||
|
if ($preferred) {
|
||||||
|
session(['shop.default_sale_channel_id' => $preferred->id]);
|
||||||
|
return $preferred;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($customer->sale_channels->isNotEmpty()) {
|
||||||
|
session(['shop.default_sale_channel_id' => $customer->sale_channels->first()->id]);
|
||||||
|
return $customer->sale_channels->first();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return self::getByCode('EXP');
|
return self::getByCode('EXP');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
app/Repositories/Shop/Shop.php
Normal file
47
app/Repositories/Shop/Shop.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repositories\Shop;
|
||||||
|
|
||||||
|
use App\Repositories\Core\User\ShopCart;
|
||||||
|
|
||||||
|
class Shop
|
||||||
|
{
|
||||||
|
public static function getSaleChannelAvailabilitySummary(array $saleChannelIds): array
|
||||||
|
{
|
||||||
|
if (empty($saleChannelIds) || ShopCart::count() === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$cartItems = ShopCart::getContent();
|
||||||
|
$summary = [];
|
||||||
|
|
||||||
|
foreach ($saleChannelIds as $saleChannelId) {
|
||||||
|
$saleChannelId = (int) $saleChannelId;
|
||||||
|
$issues = [];
|
||||||
|
$issueCount = 0;
|
||||||
|
|
||||||
|
foreach ($cartItems as $item) {
|
||||||
|
$offerId = (int) $item->id;
|
||||||
|
|
||||||
|
if (! Offers::getPrice($offerId, 1, $saleChannelId)) {
|
||||||
|
$offer = Offers::get($offerId, ['article']);
|
||||||
|
$issues[] = $offer->article->name ?? $item->name;
|
||||||
|
$issueCount++;
|
||||||
|
|
||||||
|
if (count($issues) >= 3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($issues)) {
|
||||||
|
$summary[$saleChannelId] = [
|
||||||
|
'full_count' => $issueCount,
|
||||||
|
'names' => array_slice($issues, 0, 3),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $summary;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('shop_customers', function (Blueprint $table) {
|
||||||
|
if (! Schema::hasColumn('shop_customers', 'default_sale_channel_id')) {
|
||||||
|
$table->unsignedInteger('default_sale_channel_id')->nullable()->after('settings');
|
||||||
|
$table->index('default_sale_channel_id', 'shop_customers_default_sale_channel_id_index');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('shop_customers', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('shop_customers', 'default_sale_channel_id')) {
|
||||||
|
$table->dropIndex('shop_customers_default_sale_channel_id_index');
|
||||||
|
$table->dropColumn('default_sale_channel_id');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -223,6 +223,9 @@ return [
|
|||||||
'successmod' => 'Le canal de vente a été correctement modifié',
|
'successmod' => 'Le canal de vente a été correctement modifié',
|
||||||
'successdel' => 'Le canal de vente a été correctement effacé',
|
'successdel' => 'Le canal de vente a été correctement effacé',
|
||||||
'confirmdelete' => 'Confirmez-vous la suppression du canal de vente ?',
|
'confirmdelete' => 'Confirmez-vous la suppression du canal de vente ?',
|
||||||
|
'missing_offers' => '{1} Ce canal de vente n\'a pas d\'offre pour :count produit.|[2,*] Ce canal de vente n\'a pas d\'offre pour :count produits.',
|
||||||
|
'missing_offers_all' => 'Ce canal de vente n\'a aucune offre pour tous les produits de votre panier.',
|
||||||
|
'cannot_select_with_cart' => 'Vous ne pouvez pas sélectionner ce mode d\'achat tant que votre panier contient des produits non disponibles dans ce mode.',
|
||||||
],
|
],
|
||||||
'shelves' => [
|
'shelves' => [
|
||||||
'title' => 'Rayons',
|
'title' => 'Rayons',
|
||||||
|
|||||||
@@ -48,6 +48,37 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3 col-xs-12">
|
<div class="col-lg-3 col-xs-12">
|
||||||
|
@if (config('app.debug') && ($article['current_sale_channel'] ?? false))
|
||||||
|
<div class="alert alert-info p-2 mb-3">
|
||||||
|
<strong>Canal actif :</strong>
|
||||||
|
{{ $article['current_sale_channel']['name'] ?? 'N/A' }}
|
||||||
|
<span class="d-block small text-muted">
|
||||||
|
ID {{ $article['current_sale_channel']['id'] ?? '–' }} · Code {{ $article['current_sale_channel']['code'] ?? '–' }}
|
||||||
|
</span>
|
||||||
|
@if (!empty($article['available_sale_channels']))
|
||||||
|
<hr class="my-2">
|
||||||
|
<strong class="d-block">Offres disponibles dans :</strong>
|
||||||
|
<ul class="list-unstyled mb-0 small">
|
||||||
|
@foreach ($article['available_sale_channels'] as $channel)
|
||||||
|
<li class="d-flex justify-content-between align-items-start">
|
||||||
|
<span>
|
||||||
|
• {{ $channel['name'] }}
|
||||||
|
<span class="d-block text-muted" style="font-size: 0.85em; padding-left: 0.9em;">code {{ $channel['code'] }}</span>
|
||||||
|
</span>
|
||||||
|
@if (isset($channel['price_taxed']))
|
||||||
|
<span class="ml-2 text-nowrap text-right">
|
||||||
|
{{ number_format($channel['price_taxed'], 2, ',', ' ') }} € TTC
|
||||||
|
@if (! empty($channel['quantity']))
|
||||||
|
<span class="d-block text-muted" style="font-size: 0.85em;">Qté min. {{ $channel['quantity'] }}</span>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@include('Shop.Articles.partials.ArticleAddBasket')
|
@include('Shop.Articles.partials.ArticleAddBasket')
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
updateBasket(offer_id, quantity, function() {
|
updateBasket(offer_id, quantity, function() {
|
||||||
calculatePrice($row);
|
calculatePrice($row);
|
||||||
calculateTotal();
|
calculateTotal();
|
||||||
});
|
}, $row);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.basket-delete').click(function() {
|
$('.basket-delete').click(function() {
|
||||||
@@ -70,13 +70,20 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBasket(offer_id, quantity, callback) {
|
function updateBasket(offer_id, quantity, callback, $row) {
|
||||||
var data = {
|
var data = {
|
||||||
offer_id: offer_id,
|
offer_id: offer_id,
|
||||||
quantity: quantity,
|
quantity: quantity,
|
||||||
update: true
|
update: true
|
||||||
};
|
};
|
||||||
$.post("{{ route('Shop.Basket.addBasket') }}", data, callback);
|
$.post("{{ route('Shop.Basket.addBasket') }}", data, function(response) {
|
||||||
|
if ($row && response && response.added && typeof response.added.price !== 'undefined') {
|
||||||
|
$row.find('.basket-price').text(fixNumber(response.added.price));
|
||||||
|
$row.find('.basket-total-row').text(fixNumber(response.added.price * $row.find('.basket-quantity').val()));
|
||||||
|
}
|
||||||
|
callback(response);
|
||||||
|
refreshBasketTop();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculatePrice($that) {
|
function calculatePrice($that) {
|
||||||
|
|||||||
@@ -1,25 +1,168 @@
|
|||||||
@foreach ($deliveries as $delivery)
|
@push('styles')
|
||||||
<div class="row">
|
<style>
|
||||||
<div class="col-1 text-right pt-1">
|
.sale-channel-wrapper {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper:not(.blocked) .card-body {
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: .75rem;
|
||||||
|
background-color: #ffffff;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper:not(.blocked) .card-body:hover {
|
||||||
|
border-color: #3b82f6;
|
||||||
|
box-shadow: 0 0.35rem 0.8rem rgba(37, 99, 235, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper.blocked .card-body {
|
||||||
|
border: 1px solid #d1d5db;
|
||||||
|
border-radius: .75rem;
|
||||||
|
background-color: #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper .card-body {
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-toggle {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-content strong {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-warning {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper .icheck-success > input:first-child + label::before,
|
||||||
|
.sale-channel-wrapper .icheck-primary > input:first-child + label::before,
|
||||||
|
.sale-channel-wrapper .icheck-danger > input:first-child + label::before {
|
||||||
|
opacity: 1;
|
||||||
|
border-width: 2px;
|
||||||
|
border-color: #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper.blocked .icheck-success > input:first-child + label::before,
|
||||||
|
.sale-channel-wrapper.blocked .icheck-primary > input:first-child + label::before,
|
||||||
|
.sale-channel-wrapper.blocked .icheck-danger > input:first-child + label::before {
|
||||||
|
border-color: #cbd5f5;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper .icheck-success > input:first-child + label,
|
||||||
|
.sale-channel-wrapper .icheck-primary > input:first-child + label,
|
||||||
|
.sale-channel-wrapper .icheck-danger > input:first-child + label {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sale-channel-wrapper [class*="icheck-"] > input:first-child:disabled + label {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@endpush
|
||||||
|
|
||||||
|
@php
|
||||||
|
$saleChannelsCollection = collect($sale_channels);
|
||||||
|
$firstSaleChannel = $saleChannelsCollection->first();
|
||||||
|
$selectedSaleChannelId = $customer['default_sale_channel_id'] ?? ($firstSaleChannel['id'] ?? null);
|
||||||
|
$cartCount = app('App\\Repositories\\Core\\User\\ShopCart')::count();
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@if ($cartCount > 0)
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<strong>Note :</strong> en changeant votre mode d'achat, les articles de votre panier seront transférés sur la liste de prix correspondant au nouveau canal de vente sélectionné.
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@foreach ($saleChannelsCollection as $saleChannel)
|
||||||
|
@php
|
||||||
|
$check = $sale_channel_checks[$saleChannel['id']] ?? null;
|
||||||
|
$isBlocked = $check && $saleChannel['id'] !== $selectedSaleChannelId;
|
||||||
|
@endphp
|
||||||
|
<div class="card sale-channel-wrapper mb-3 @if($isBlocked) blocked @endif">
|
||||||
|
<div class="card-body py-3">
|
||||||
|
<div class="row align-items-start">
|
||||||
|
<div class="col-1 sale-channel-toggle">
|
||||||
|
|
||||||
@include('components.form.radios.icheck', [
|
@include('components.form.radios.icheck', [
|
||||||
'name' => 'delivery_id',
|
'name' => 'sale_channel_id',
|
||||||
'id_name' => 'delivery_id_' . $delivery['id'],
|
'id_name' => 'sale_channel_id_' . $saleChannel['id'],
|
||||||
'value' => $delivery['id'],
|
'val' => $saleChannel['id'],
|
||||||
'checked' => $customer['sale_delivery_id'] ?? false,
|
'value' => $selectedSaleChannelId,
|
||||||
'class' => 'delivery',
|
'class' => 'sale-channel',
|
||||||
|
'disabled' => $isBlocked,
|
||||||
])
|
])
|
||||||
</div>
|
</div>
|
||||||
<div class="col-11 pt-3">
|
<div class="col-11 sale-channel-content @if($isBlocked) text-muted @endif">
|
||||||
<strong>{{ $delivery['name'] }} - {{ $delivery['sale_channel']['name'] }}</strong><br />
|
<strong>{{ $saleChannel['name'] }}</strong><br />
|
||||||
<p>{{ $delivery['description'] }}</p>
|
<p class="mb-2">{!! $saleChannel['description'] ?? '' !!}</p>
|
||||||
|
@if ($check)
|
||||||
|
<div class="text-danger small mb-0 sale-channel-warning">
|
||||||
|
@php $missingCount = $check['full_count'] ?? count($check['names']); @endphp
|
||||||
|
@if ($cartCount > 0 && $missingCount >= $cartCount)
|
||||||
|
{{ __('shop.sale_channels.missing_offers_all') }}
|
||||||
|
@else
|
||||||
|
{{ trans_choice('shop.sale_channels.missing_offers', $missingCount, ['count' => $missingCount]) }}
|
||||||
|
<br>
|
||||||
|
@if ($missingCount > 3)
|
||||||
|
<span class="d-block">{{ implode(', ', array_slice($check['names'], 0, 3)) }}, …</span>
|
||||||
|
@else
|
||||||
|
<span class="d-block">{{ implode(', ', $check['names']) }}</span>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
<div class="d-flex align-items-start mt-1">
|
||||||
|
<span class="mr-1">⚠️</span>
|
||||||
|
<span>{{ __('shop.sale_channels.cannot_select_with_cart') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|
||||||
@push('js')
|
@push('js')
|
||||||
<script>
|
<script>
|
||||||
$('.delivery').off().change(function() {
|
const $saleChannels = $('.sale-channel');
|
||||||
console.log($(this).val());
|
const updateUrl = '{{ route('Shop.Customers.storeProfileAjax') }}';
|
||||||
|
const token = '{{ csrf_token() }}';
|
||||||
|
const customerId = {{ $customer['id'] ?? 'null' }};
|
||||||
|
let currentSaleChannelId = '{{ $selectedSaleChannelId }}';
|
||||||
|
|
||||||
|
$saleChannels.off().change(function() {
|
||||||
|
if (!customerId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedSaleChannel = $(this).val();
|
||||||
|
|
||||||
|
$.post(updateUrl, {
|
||||||
|
_token: token,
|
||||||
|
id: customerId,
|
||||||
|
default_sale_channel_id: selectedSaleChannel,
|
||||||
|
}).done(function() {
|
||||||
|
currentSaleChannelId = selectedSaleChannel;
|
||||||
|
window.location.reload();
|
||||||
|
}).fail(function(xhr) {
|
||||||
|
const message = xhr.responseJSON && xhr.responseJSON.message
|
||||||
|
? xhr.responseJSON.message
|
||||||
|
: "{{ __('Une erreur est survenue lors de l\'enregistrement du canal de vente préféré.') }}";
|
||||||
|
|
||||||
|
alert(message);
|
||||||
|
|
||||||
|
if (currentSaleChannelId) {
|
||||||
|
$saleChannels.filter('[value="' + currentSaleChannelId + '"]').prop('checked', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
<nav>
|
@php
|
||||||
|
$saleChannels = $sale_channels ?? [];
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
@if (count($saleChannels) > 1)
|
||||||
|
<nav>
|
||||||
<div class="nav nav-tabs pl-2">
|
<div class="nav nav-tabs pl-2">
|
||||||
<a href="#deliveriesTab" data-toggle="tab" class="nav-item nav-link active" role="tab" aria-selected="true">
|
<a href="#deliveriesTab" data-toggle="tab" class="nav-item nav-link active" role="tab" aria-selected="true">
|
||||||
MON MODE D'ACHAT
|
MON MODE D'ACHAT
|
||||||
@@ -10,9 +15,9 @@
|
|||||||
FACTURES
|
FACTURES
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane fade show active pt-0 pb-0" id="deliveriesTab">
|
<div class="tab-pane fade show active pt-0 pb-0" id="deliveriesTab">
|
||||||
<x-card classBody="bg-light">
|
<x-card classBody="bg-light">
|
||||||
@include('Shop.Customers.partials.deliveries')
|
@include('Shop.Customers.partials.deliveries')
|
||||||
@@ -32,4 +37,33 @@
|
|||||||
])
|
])
|
||||||
</x-card>
|
</x-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@else
|
||||||
|
<nav>
|
||||||
|
<div class="nav nav-tabs pl-2">
|
||||||
|
<a href="#ordersTab" data-toggle="tab" class="nav-item nav-link active" role="tab" aria-selected="true">
|
||||||
|
SUIVI DE COMMANDES
|
||||||
|
</a>
|
||||||
|
<a href="#invoicesTab" data-toggle="tab" class="nav-item nav-link" role="tab" aria-selected="false">
|
||||||
|
FACTURES
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane fade show active pt-0 pb-0" id="ordersTab">
|
||||||
|
<x-card classBody="bg-light">
|
||||||
|
@include('Shop.Orders.partials.list', [
|
||||||
|
'dataTable' => $orders,
|
||||||
|
])
|
||||||
|
</x-card>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade show pt-0 pb-0" id="invoicesTab">
|
||||||
|
<x-card classBody="bg-light">
|
||||||
|
@include('Shop.Invoices.partials.list', [
|
||||||
|
'dataTable' => $invoices,
|
||||||
|
])
|
||||||
|
</x-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
|
@php
|
||||||
|
$defaultSaleChannelId = $customer['default_sale_channel_id'] ?? null;
|
||||||
|
$preselectedDeliveryId = old('delivery_id');
|
||||||
|
|
||||||
|
if (! $preselectedDeliveryId && $defaultSaleChannelId) {
|
||||||
|
$match = collect($deliveries)->firstWhere('sale_channel_id', $defaultSaleChannelId);
|
||||||
|
$preselectedDeliveryId = $match['id'] ?? null;
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
@foreach ($deliveries as $delivery)
|
@foreach ($deliveries as $delivery)
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
@include('components.form.radios.icheck', [
|
@include('components.form.radios.icheck', [
|
||||||
'name' => 'delivery_id',
|
'name' => 'delivery_id',
|
||||||
'val' => $delivery['id'],
|
'val' => $delivery['id'],
|
||||||
'value' => (int) old('delivery_id') === $delivery['id'] ? $delivery['id'] : null,
|
'value' => $preselectedDeliveryId,
|
||||||
'id' => 'delivery_' . $delivery['id'],
|
'id' => 'delivery_' . $delivery['id'],
|
||||||
'class' => 'delivery_mode' . ($delivery['at_house'] ? ' at_house' : ''),
|
'class' => 'delivery_mode' . ($delivery['at_house'] ? ' at_house' : ''),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<link rel="shortcut icon" type="image/x-icon" href="{{ asset('img/favicon.ico') }}">
|
<link rel="shortcut icon" type="image/x-icon" href="{{ asset('img/favicon.ico') }}">
|
||||||
<link rel="stylesheet" href="/css/site.min.css?{{ date('Ymd') }}" type="text/css" media="all">
|
<link rel="stylesheet" href="/css/site.min.css?{{ date('Ymd') }}" type="text/css" media="all">
|
||||||
|
|
||||||
|
@stack('styles')
|
||||||
@stack('css')
|
@stack('css')
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
Reference in New Issue
Block a user