<?php
namespace App\Controller\Admin;
use App\Service\ActivityService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
use App\Entity\ProduitDeclinationValue;
use App\Entity\GroupDeclinationValue;
use App\Entity\Produit;
use App\Repository\ProduitRepository;
use App\Repository\DeclinationRepository;
use App\Repository\ValueDeclinationRepository;
use App\Repository\ProduitDeclinationValueRepository;
use App\Repository\GroupDeclinationValueRepository;
use App\Repository\FileRepository;
use App\Entity\Stock;
use App\Entity\File;
use App\Form\UploadFileProduitDecType;
use App\Form\FilterProduitType;
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
use App\Form\FilterDocumentType;
use App\Service\RightService;
/**
* @Route("/produit/declination/value")
*/
class ProduitDeclinationValueController extends AbstractController {
use ImageControllerTrait;
use AccessTrait;
private $produitRepository;
private $declinationRepository;
private $valueDeclinationRepository;
private $produitDeclinationValueRepository;
private $groupDeclinationValueRepository;
private $fileRepository;
private $uploaderHelper;
private $rightService;
private $activityService;
public function __construct(
ProduitRepository $produitRepository,
ValueDeclinationRepository $valueDeclinationRepository,
DeclinationRepository $declinationRepository,
ProduitDeclinationValueRepository $produitDeclinationValueRepository,
FileRepository $fileRepository,
GroupDeclinationValueRepository $groupDeclinationValueRepository,
UploaderHelper $uploaderHelper,
RightService $rightService,
ActivityService $activityService
) {
$this->produitRepository = $produitRepository;
$this->valueDeclinationRepository = $valueDeclinationRepository;
$this->declinationRepository = $declinationRepository;
$this->produitDeclinationValueRepository = $produitDeclinationValueRepository;
$this->groupDeclinationValueRepository = $groupDeclinationValueRepository;
$this->fileRepository = $fileRepository;
$this->uploaderHelper = $uploaderHelper;
$this->rightService = $rightService;
$this->activityService = $activityService;
}
/**
* @Route("/add", name="add_produit_declination_value", methods={"GET","POST"}, options={"expose"=true})
*/
public function add(Request $request,EntityManagerInterface $em): JsonResponse {
$listDeclinations = [];
foreach ($request->get('value_declination') as $item) {
$listDeclinations[] = $item['declinations'];
}
$duplicate = $this->checkDuplicate($listDeclinations);
if( $duplicate) {
return new JsonResponse(array("success" => false, "message" => $duplicate));
} else {
$this->createEntity($request,$em);
$request->getSession()->getFlashBag()->add('success', "les changements ont été enregistré");
return new JsonResponse(array("success" => true, "path" => $this->generateUrl('produit_show', ['id' => $request->get('id_produit')])));
}
}
public function createEntity($request,EntityManagerInterface $em) {
foreach ($request->get('value_declination') as $item) {
if( $item['status'] === 'new') {
$entity = new ProduitDeclinationValue();
$entity->setBuyingPriceTtc($item['buyingPriceTtc'])
->setDescription($item['description'])
->setName($item['name'])
->setPriceHt($item['price_ht'])
->setReference($item['reference'])
->setCreatedAt(new \DateTime('now'));
$stock = new Stock();
if( $request->get('id_produit')) {
$produit = $this->produitRepository->find($request->get('id_produit'));
$entity->setProduit($produit);
$stock->setProduit($produit);
}
$stock->setQtReserved(0)
->setQtStock(0)
->setDeclinationProduit($entity)
->setStorehouse('Principal');
$em->persist($stock);
$em->persist($entity);
if( $item['declinations']) {
foreach ($item['declinations'] as $key => $value) {
$group = new GroupDeclinationValue();
if( $value && $key) {
$valueDeclination = $this->valueDeclinationRepository->find($value);
$declination = $this->declinationRepository->find($key);
$group->setValue($valueDeclination);
$group->setDeclination($declination);
}
$group->setProduitDeclination($entity);
$em->persist($group);
}
}
} elseif( $item['status'] === 'update') {
$entity = $this->produitDeclinationValueRepository->find($item['id']);
$entity->setBuyingPriceTtc($item['buyingPriceTtc'])
->setDescription($item['description'])
->setName($item['name'])
->setPriceHt($item['price_ht'])
->setReference($item['reference']);
} elseif( isset($item['id']) && $item['status'] === 'delete') {
$entity = $this->produitDeclinationValueRepository->find($item['id']);
foreach ($entity->getGroupDeclinationValues() as $group) {
$em->remove($group);
}
foreach ($entity->getStocks() as $stock) {
$em->remove($stock);
}
$em->remove($entity);
$em->flush();
}
}
$em->flush();
}
public function checkDuplicate($listDeclinations) {
$result = $this->return_dup($listDeclinations);
if( empty($result)) {
return null;
} else {
$text = "";
foreach ($result as $group) {
foreach ($group as $value) {
$valueDeclination = $this->valueDeclinationRepository->find($value);
$text = $text . ' ' . $valueDeclination->getName();
}
}
$text = $text . " existe déjà, veuillez sélectionner une combinaison différente.";
return $text;
}
}
public function return_dup($arr) {
$dups = array();
$temp = $arr;
foreach ($arr as $key => $item) {
unset($temp[$key]);
if( in_array($item, $temp)) {
$dups[] = $item;
}
}
return $dups;
}
/**
* @Route("/listData/{idProduit}", name="list_produit_declination_value", methods="GET|POST", options = { "expose" = true})
*/
/**
* @Route("/listData/{idProduit}", name="list_produit_declination_value", methods={"GET","POST"}, options={"expose"=true})
*/
public function listData($idProduit, Request $request) {
$pagination = (array) $request->get('pagination', []);
$page = max(0, (int)($pagination['page'] ?? 1) - 1);
$limit = (int)($pagination['perpage'] ?? 10);
$reference = $request->get('reference');
// Nouveau: filtres multi-déclinaisons
$declinations = $request->get('declinations', []);
// Normalisation types: clés => int, valeurs => int|int[]
if (is_string($declinations)) {
// si le client envoie un JSON string
$decoded = json_decode($declinations, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
$declinations = $decoded;
} else {
$declinations = [];
}
}
$declinations = array_combine(
array_map('intval', array_keys($declinations)),
array_map(function ($v) {
return is_array($v) ? array_map('intval', $v) : (int)$v;
}, array_values($declinations))
);
$entities = $this->produitDeclinationValueRepository->findProduitGroup(
$page, $limit, (int)$idProduit, $reference, $declinations
);
$count = $this->produitDeclinationValueRepository->countProduitGroup(
(int)$idProduit, $reference, $declinations
);
$output = [
'data' => [],
'meta' => [
'page' => $page + 1,
'perpage' => $limit,
'pages' => (int)ceil($count / max(1, $limit)),
'total' => (int)$count,
],
];
foreach ($entities as $entity) {
// Prix promo éventuel
$priceHt = $entity->getPriceHt();
if ($entity->getProduit()->getPromotion()) {
$promotion = $entity->getProduit()->getPromotion();
$now = new \DateTimeImmutable('now');
if ($promotion->getStartAt() <= $now && $promotion->getEndAt() >= $now) {
$priceHt = ($promotion->getDiscountType() === 'percent')
? $entity->getPriceHt() - (($entity->getPriceHt() / 100) * $promotion->getDiscountValue())
: $entity->getPriceHt() - $promotion->getDiscountValue();
}
}
$data = [
'id' => $entity->getId(),
'name' => $entity->getName(),
'reference' => $entity->getReference(),
'buyingPriceTtc' => ($this->isGranted('ROLE_SUPER_ADMIN')) ? number_format($entity->getBuyingPriceTtc(), 3) : '',
'price_ht' => number_format($priceHt, 3),
'actions' =>
'<button class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" id="updateDeclination" data-id="' . $entity->getId() . '" title="Modifier"><i class="fas fa-edit"></i></button>'
. '<a href="' . $this->generateUrl('produit_declination_value_show', ['id' => $entity->getId()]) . '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" title="Afficher" target="_blank"><i class="fa fa-clipboard"></i></a>',
];
// Aplatit les paires declination -> value pour l’affichage tableau
foreach ($entity->getGroupDeclinationValues() as $group) {
$key = strtolower($group->getDeclination()->getName());
$data[$key] = $group->getValue()->getName();
}
$output['data'][] = $data;
}
return new JsonResponse($output);
}
/*
public function listData($idProduit, Request $request) {
$pagination = $request->get('pagination');
$page = $pagination['page'] - 1;
$limit = $pagination['perpage'];
$entities = $this->produitDeclinationValueRepository->findProduitGroup($page, $limit, $idProduit, $request->get('reference'), $request->get('couleur'));
$count = $this->produitDeclinationValueRepository->countProduitGroup($idProduit, $request->get('reference'), $request->get('couleur'));
$output = array(
'data' => array(),
'meta' => array(
'page' => $pagination['page'],
'perpage' => $limit,
"pages" => ceil($count / $limit),
"total" => $count,
)
);
foreach ($entities as $entity) {
//vérifier s'il ya une promotion
$priceHt = $entity->getPriceHt();
if( $entity->getProduit()->getPromotion()) {
$promotion = $entity->getProduit()->getPromotion();
$date = new \DateTime('now');
if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date)
$priceHt =($promotion->getDiscountType() == 'percent')?
$entity->getPriceHt() - ((($entity->getPriceHt() / 100) * $promotion->getDiscountValue()))
: $entity->getPriceHt() - $promotion->getDiscountValue();
}
$data = [
'id' => $entity->getId(),
'name' => $entity->getName(),
'reference' => $entity->getReference(),
'buyingPriceTtc' => ($this->isGranted('ROLE_SUPER_ADMIN')) ? number_format($entity->getBuyingPriceTtc(), 3) : '',
'price_ht' => number_format($priceHt, 3),
//'createdAt' => $produit->getCreatedAt()?->format('Y-m-d H:i:s'),
'actions' => '<button class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" id="updateDeclination" data-id="' . $entity->getId() . '" title="Modifier"><i class="fa fa-pencil-square"></i></button><a href="' . $this->generateUrl('produit_declination_value_show', ['id' => $entity->getId()]) . '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" title="Afficher" target="_blank"><i class="fa fa-clipboard"></i></a>',
];
foreach ($entity->getGroupDeclinationValues() as $group) {
$key = strtolower($group->getDeclination()->getName());
$value = $group->getValue()->getName();
$data[$key] = $value;
}
$output['data'][] = $data;
}
return new JsonResponse($output);
}
*/
/**
* @Route("/columns/{idProduit}", name="columns_produit_declination_value", methods="GET|POST", options = { "expose" = true})
*/
public function listColumns($idProduit, Request $request) {
$produit = $this->produitRepository->find($idProduit);
$output = [
[
'field' => 'reference',
'sortable' => "asc",
'width' => 140,
'title' => 'Réference'
],
[
'field' => 'name',
'sortable' => "asc",
'title' => 'Nom commercial',
'width' => 250,
]
];
foreach ($produit->getDeclinations() as $entity) {
$output[] = [
'field' => strtolower($entity->getName()),
//'sortable' => "asc",
'width' => 60,
'textAlign' => 'center',
'title' => $entity->getName()
];
}
if( $this->isGranted('ROLE_SUPER_ADMIN')) {
$output[] = [
'field' => 'buyingPriceTtc',
//'sortable' => "asc",
'width' => 80,
'textAlign' => 'center',
'title' => 'Prix Achat'
];
}
$output[] = [
'field' => 'price_ht',
//'sortable' => "asc",
'width' => 80,
'textAlign' => 'center',
'title' => 'Prix Vente'
];
$output[] = [
'field' => 'actions',
//'sortable' => "asc",
'width' => '70',
'textAlign' => 'center',
'title' => 'Actions'
];
return new JsonResponse($output);
}
/**
* @Route("/edit/{id}", name="produit_declination_value_edit", methods={"GET","POST"}, options={"expose"=true})
*/
public function edit(Request $request, ProduitDeclinationValue $produitDeclinationValue,EntityManagerInterface $em): Response {
if( $request->isMethod('POST')) {
$item = $request->get('produit_declination_value');
$produitDeclinationValue->setBuyingPriceTtc($item['BuyingPriceTtc'])
->setDescription($item['description'])
->setName($item['name'])
->setPriceHt($item['PriceHt'])
->setReference($item['reference']);
$pictures = $request->files->get('produit_declination_value')['picture'];
if( $request->request->get('selectFile')) {
$imageSelected = $this->fileRepository->find($request->request->get('selectFile'));
$imageSelected->setIsSelected(true);
$em->flush();
}
foreach ($pictures as $key => $picture) {
if( !$request->request->get('selectFile') && $key == 0) {
$file = new File();
$file->setFile($picture);
$file->SetIsSelected(true);
$em->persist($file);
$produitDeclinationValue->addPicture($file);
} else {
$file = new File();
$file->setFile($picture);
$em->persist($file);
$produitDeclinationValue->addPicture($file);
}
}
$em->flush();
$request->getSession()->getFlashBag()->add('success', "Déclinaison modifié avec succès");
return $this->redirect($this->generateUrl('produit_show', ['id' => $produitDeclinationValue->getProduit()->getId()]));
// return $this->redirectToRoute('produit_declination_value_show', ['id' => $produitDeclinationValue->getId(),'tab' => 'content_decli']);
}
return $this->render('@admin/produit_declination_value/_formModal.html.twig', [
'produitDeclinationValue' => $produitDeclinationValue
]);
}
/**
* @Route("/multi/add/{id}", name="produit_declination_value_multi_add", methods={"GET","POST"}, options={"expose"=true})
*/
public function multiAdd(Request $request, Produit $produit,EntityManagerInterface $em) {
if( $request->isMethod('POST')) {
$declinations = $produit->getProduitDeclinationValues();
$list = $this->listGroupDeclination($declinations);
if( $request->get('multi_add')) {
$newList = [];
$keys = array_keys($request->get('multi_add')['declinationMulti']);
if( count($keys) == 1) {
$declinationFixe = $request->get('multi_add')['declinationFixe'];
$declinationMulti = $request->get('multi_add')['declinationMulti'];
$newList = [];
foreach ($declinationMulti as $multiKey => $multiValues) {
foreach ($multiValues as $multiValue) {
foreach ($declinationFixe as $fixeKey => $fixeValues) {
foreach ($fixeValues as $fixeValue) {
// Vérifie que les deux sont valides
if (!empty($multiKey) && !empty($multiValue) && !empty($fixeKey) && !empty($fixeValue)) {
$group = [];
$group[$fixeKey] = $fixeValue;
$group[$multiKey] = $multiValue;
$newList[] = $group;
}
}
}
}
}
$all = array_merge($list, $newList);
$duplicate = $this->checkDuplicate($all);
if( $duplicate) {
$request->getSession()->getFlashBag()->add('danger', $duplicate);
return $this->redirect($this->generateUrl('produit_show', ['id' => $produit->getId()]));
} else {
foreach ($newList as $item) {
$entity = new ProduitDeclinationValue();
$entity->setBuyingPriceTtc($produit->getBuyingPriceTtc())
->setDescription($produit->getDescription())
->setPriceHt($produit->getPriceHt())
->setCreatedAt(new \DateTime('now'))
->setProduit($produit);
$array = [];
foreach ($item as $key => $value) {
$group = new GroupDeclinationValue();
if( $value && $key) {
$valueDeclination = $this->valueDeclinationRepository->find($value);
$declination = $this->declinationRepository->find($key);
$group->setValue($valueDeclination);
$group->setDeclination($declination);
$array[] = ['position' => $declination->getPosition(), 'name' => $valueDeclination->getName()];
}
$group->setProduitDeclination($entity);
$em->persist($group);
}
$refrenece = $produit->getReference();
$name = $produit->getName();
foreach ($array as $item) {
$refrenece = $refrenece . "-" . strtolower($item['name']);
$name = $name . " " . strtolower($item['name']);
}
$entity->setReference($refrenece)
->setName($name);
if( isset($request->get('multi_add')['image']) && $request->get('multi_add')['image']) {
$imageSelected = $this->fileRepository->find($request->get('multi_add')['image']);
$file = new File();
$file->setFile($imageSelected->getFile());
$file->setImageName($imageSelected->getFile()->getFilename());
$file->setImageSize($imageSelected->getFile()->getSize());
$file->SetIsSelected(true);
$em->persist($file);
$entity->addPicture($file);
}
$stock = new Stock();
$stock->setQtReserved(0)
->setProduit($produit)
->setQtStock(0)
->setDeclinationProduit($entity)
->setStorehouse('Principal');
$em->persist($stock);
$em->persist($entity);
}
$em->flush();
$declinaisonNames = [];
foreach ($request->get('multi_add')['declinationFixe'] as $key => $valueIds) {
foreach ($valueIds as $id) {
if (!empty($id)) {
$dec = $this->valueDeclinationRepository->find($id);
if ($dec && !in_array($dec->getName(), $declinaisonNames)) {
$declinaisonNames[] = $dec->getName();
}
}
}
}
return new JsonResponse(["success" => true,"message" => "Déclinaisons créées pour les déclinaisons : " . implode(', ', $declinaisonNames)]);
}
}
}
}
/*
return $this->render('@admin/produit_declination_value/multi-add.html.twig', [
'produit' => $produit
]);
*/
$fixe = [];
$multi = [];
foreach ($produit->getDeclinations() as $declination) {
if (stripos($declination->getName(), 'couleur') !== false) {
$fixe[] = $declination;
}
if (stripos($declination->getName(), 'taille') !== false) {
$multi[] = $declination;
}
}
return $this->render('admin/produit_declination_value/multi-add.html.twig', [
'produit' => $produit,
'fixe' => $fixe,
'multi' => $multi,
]);
}
/**
* @Route("/multi/select", name="select_multi_add", methods={"GET","POST"}, options={"expose"=true})
*/
public function selectMulti(Request $request): Response {
$fixe = [];
$multi = [];
if( $request->get('multi_add')) {
foreach ($request->get('multi_add')['fixe'] as $id) {
$declination = $this->declinationRepository->find($id);
$fixe[] = $declination;
}
foreach ($request->get('multi_add')['multiple'] as $id) {
$declination = $this->declinationRepository->find($id);
$multi[] = $declination;
}
}
return $this->render('@admin/produit_declination_value/formule.html.twig', [
'fixe' => $fixe,
'multi' => $multi
]);
}
public function listGroupDeclination($declinations) {
$list = [];
foreach ($declinations as $declination) {
$listGroup = [];
foreach ($declination->getGroupDeclinationValues() as $group) {
$listGroup[$group->getDeclination()->getId()] = (string) $group->getValue()->getId();
}
$list[] = $listGroup;
}
return $list;
}
public function sortAssociativeArrayByKey($array, $key, $direction) {
switch ($direction) {
case "ASC":
usort($array, function ($first, $second) use ($key) {
return $first[$key] <=> $second[$key];
});
break;
case "DESC":
usort($array, function ($first, $second) use ($key) {
return $second[$key] <=> $first[$key];
});
break;
default:
break;
}
return $array;
}
/**
* @Route("/search", name="search_item", methods="GET|POST", options = { "expose" = true})
*/
public function searchItem(Request $request,EntityManagerInterface $em) {
$types=$request->get("type")??["declinaison"];
$entitiesproduit=$entities=[];
if($types){
if(in_array("declinaison",$types)){
$entities = $this->produitDeclinationValueRepository->searchItem($request->get('query'),$request->get('is_available'), count($types)==2?6:12);
foreach ($entities as &$entity) {
//dd($entity);
$qtReserved=$quantity = 0;
$entity['type'] = 'produitDeclination';
$stocks = $em->getRepository(Stock::class)->findByDeclinationProduit($entity['id']);
foreach ($stocks as $stock) {
$quantity = $quantity + ($stock->getQtStock() - $stock->getQtReserved());
$qtReserved+=$stock->getQtReserved();
}
$entity['quantity'] =( $quantity<=3 or $this->isGranted('ROLE_SUPER_ADMIN') or $this->currentUserHasRight('STOCK_SEE_REAL_STOCK') )?$quantity:"3<sup>+</sup>";
$entity['qtReserved'] =$qtReserved;
$produit = $this->produitDeclinationValueRepository->find($entity['id'])->getProduit();
$promotion=$produit->getPromotion();
$entity['value'] = null;
$entity['promotionType'] = null;
$in_promo=false;
$entity['price_ttc'] =$entity['price_ttc_without_promo'] = $produit->getPriceTtc();
if( $promotion) {
$date = new \DateTime('now');
if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date) {
$entity['value'] = $promotion->getDiscountValue();
$entity['promotionType'] = $promotion->getDiscountType();
$entity['price_ttc'] =($promotion->getDiscountType() == 'percent')?
$produit->getPriceTtc() - ((($produit->getPriceTtc() / 100) * $promotion->getDiscountValue()))
: $produit->getPriceTtc() - $promotion->getDiscountValue();
$in_promo=true;
}
}
$entity['in_promo'] = $in_promo;
$picture = $this->getPicture($this->produitDeclinationValueRepository->find($entity['id']), $request);
$entity['picture'] = $picture;
}
}
if(in_array("product",$types)) {
$entitiesproduit = $this->produitRepository->searchItem($request->get('query'),count($types)==2?6:12);
foreach ($entitiesproduit as &$entity) {
$qtReserved = $quantity = 0;
$stocks = $em->getRepository(Stock::class)->findByProduit($entity['id']);
foreach ($stocks as $stock) {
$quantity = $quantity + ($stock->getQtStock() - $stock->getQtReserved());
$qtReserved += $stock->getQtReserved();
}
$entity['quantity'] =( $quantity<=3 or $this->isGranted('ROLE_SUPER_ADMIN') or $this->currentUserHasRight('STOCK_SEE_REAL_STOCK') )?$quantity:"3<sup>+</sup>";
$entity['qtReserved'] = $qtReserved;
$produit = $this->produitRepository->find($entity['id']);
$promotion = $produit->getPromotion();
$in_promo = false;
$entity['value'] = null;
$entity['promotionType'] = null;
$entity['price_ttc'] = $entity['price_ttc_without_promo'] = $produit->getPriceTtc();
if( $promotion) {
$date = new \DateTime('now');
if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date) {
$entity['value'] = $promotion->getDiscountValue();
$entity['promotionType'] = $promotion->getDiscountType();
$entity['price_ttc'] = ($promotion->getDiscountType() == 'percent') ?
$produit->getPriceTtc() - ((($produit->getPriceTtc() / 100) * $promotion->getDiscountValue()))
: $produit->getPriceTtc() - $promotion->getDiscountValue();
$in_promo = true;
}
}
$entity['in_promo'] = $in_promo;
$entity['type'] = 'produit';
$picture = $this->getPicture($this->produitRepository->find($entity['id']), $request);
$entity['picture'] = $picture;
}
}
}
$data = array_merge($entitiesproduit, $entities);
return new JsonResponse($data);
}
/**
* @Route("/list", name="list_produit_declination_table", methods="GET|POST", options = { "expose" = true})
*/
public function listDatatable(Request $request, ProduitDeclinationValueRepository $produitDecRepository): JsonResponse
{
// ---------- Pagination ----------
$pagination = $request->get('pagination');
$page = max(0, (int)($pagination['page'] ?? 1) - 1);
$limit = (int)($pagination['perpage'] ?? 20);
// ---------- Champs de tri autorisés ----------
$allowedFields = ['reference', 'name', 'createdAt'];
$sortField = 'createdAt';
$sortType = 'DESC';
if (is_array($request->get('sort'))) {
$sort = $request->get('sort');
if (isset($sort['field'], $sort['sort']) && in_array($sort['field'], $allowedFields, true)) {
$sortField = $sort['field'];
$sortType = strtoupper($sort['sort']) === 'DESC' ? 'DESC' : 'ASC';
}
}
/* === Filtres déclinaisons dynamiques (nouveau modèle)=== */
$declinationFilters = $request->get('declinations', []);
// Cas où le JS envoie une string JSON
if (is_string($declinationFilters)) {
$decoded = json_decode($declinationFilters, true);
if (json_last_error() === JSON_ERROR_NONE) {
$declinationFilters = $decoded;
} else {
$declinationFilters = [];
}
}
// Sécurisation types (int => int)
$declinationFilters = array_combine(
array_map('intval', array_keys($declinationFilters)),
array_map('intval', array_values($declinationFilters))
);
// ---------- Recherche + Count ----------
$result = $produitDecRepository->searchAndCountProduitDeclinations(
$page,
$limit,
$request->get('reference'),
$request->get('name'),
$request->get('categories'),
$declinationFilters,
$request->get('isAvailable'),
$request->get('inPromo'),
$request->get('qtMin'),
$request->get('qtMax'),
$request->get('buyingPriceMin'),
$request->get('buyingPriceMax'),
$request->get('priceMin'),
$request->get('priceMax'),
$sortField,
$sortType,
$request->get('withDeleted') == 'true'
);
$entities = $result['data'];
$count = $result['total'];
$entities = $produitDecRepository->searchProduitDeclinations(
$page,
$limit,
$request->get('reference'),
$request->get('name'),
$request->get('categories'),
$declinationFilters,
$request->get('isAvailable'),
$request->get('inPromo'),
$request->get('qtMin'),
$request->get('qtMax'),
$request->get('buyingPriceMin'),
$request->get('buyingPriceMax'),
$request->get('priceMin'),
$request->get('priceMax'),
$sortField,
$sortType,
$request->get('withDeleted') == 'true'
);
$count = $produitDecRepository->countProduitDeclinations(
$request->get('reference'),
$request->get('name'),
$request->get('categories'),
$declinationFilters,
$request->get('isAvailable'),
$request->get('inPromo'),
$request->get('qtMin'),
$request->get('qtMax'),
$request->get('buyingPriceMin'),
$request->get('buyingPriceMax'),
$request->get('priceMin'),
$request->get('priceMax'),
$request->get('withDeleted') == 'true'
);
// ---------- Payload ----------
$output = [
'data' => [],
'meta' => [
'page' => (int)($pagination['page'] ?? 1),
'perpage' => $limit,
'pages' => (int)ceil(($limit ?: 1) ? $count / max(1, $limit) : 1),
'total' => (int)$count,
],
];
// ---------- Build rows ----------
foreach ($entities as $entity) {
// Quantités (réel = dispo - réservé)
$quantity = 0;
$qtReserved = 0;
foreach ($entity->getStocks() as $stock) {
$quantity += max(0, $stock->getQtStock() - $stock->getQtReserved());
$qtReserved += (int)$stock->getQtReserved();
}
// === Nouvelle logique pour les deux 1ères déclinaisons dynamiques ===
$declinationGroups = $entity->getGroupDeclinationValues()->toArray();
usort($declinationGroups, function($a, $b) {
return $a->getDeclination()->getPosition() <=> $b->getDeclination()->getPosition();
});
$decli1 = isset($declinationGroups[0]) ? $declinationGroups[0] : null;
$decli2 = isset($declinationGroups[1]) ? $declinationGroups[1] : null;
$decli1Name = $decli1 ? $decli1->getDeclination()->getName() : null;
$decli1Val = $decli1 ? $decli1->getValue()->getName() : null;
$decli2Name = $decli2 ? $decli2->getDeclination()->getName() : null;
$decli2Val = $decli2 ? $decli2->getValue()->getName() : null;
// -------- Prix & Promo (sans HTML) --------
$base = (float)$entity->getProduit()->getPriceTtc();
$final = $base;
$promoActive = false;
$promoType = null;
$promoValue = null;
$promoPercent = null;
$promotion = $entity->getProduit()->getPromotion();
if ($promotion) {
$now = new \DateTime('now');
if ($promotion->getStartAt() <= $now && $promotion->getEndAt() >= $now) {
$promoActive = true;
$promoType = $promotion->getDiscountType();
$promoValue = (float)$promotion->getDiscountValue();
if ($promoType === 'percent') {
$final = round($base * (1 - $promoValue / 100), 3);
$promoPercent = (int)round($promoValue);
} else {
$final = round(max(0, $base - $promoValue), 3);
$promoPercent = $base > 0 ? (int)round((($base - $final) / $base) * 100) : 0;
}
}
}
// -------- Image --------
$pictures = $entity->getPicture()->filter(fn($p) => $p->getIsSelected() == 1);
$pictureName = $pictures->isEmpty() ? null: $pictures->first()->getImageName();
if (
!$pictures->isEmpty()
&& file_exists($this->getParameter('kernel.project_dir') . "/public/images/" . $pictureName)
&& !file_exists($this->getParameter('kernel.project_dir') . "/public/images/thumbs/" . $pictureName)
) {
$this->resizeImage($pictureName);
}
// -------- Masquage stock réel si non autorisé --------
$canSeeRealStock = $this->isGranted('ROLE_SUPER_ADMIN') || $this->currentUserHasRight('STOCK_SEE_REAL_STOCK');
$quantityDisplay = ($quantity <= 3 || $canSeeRealStock) ? $quantity : "3<sup>+</sup>";
// -------- Ligne --------
$output['data'][] = [
'id' => $entity->getId(),
'image' => $pictureName,
'name' => $entity->getName(),
'reference' => $entity->getReference(),
'parent' => $entity->getProduit()->getReference(),
'parent_id' => $entity->getProduit()->getId(),
'parent_name' => $entity->getProduit()->getName(),
'buyingPriceTtc' => $this->isGranted('ROLE_SUPER_ADMIN') ? (float)$entity->getBuyingPriceTtc() : null,
'price_ht' => (float)$entity->getPriceHt(),
// >>> PRIX
'price_ttc' => $final,
'price_ttc_final' => $final,
'price_ttc_original' => $base,
'promo_active' => $promoActive,
'promo_type' => $promoType,
'promo_value' => $promoValue,
'promo_percent' => $promoPercent,
// >>> NOUVEAUX CHAMPS DECLINAISONS
'decli1_name' => $decli1Name,
'decli1_value' => $decli1Val,
'decli2_name' => $decli2Name,
'decli2_value' => $decli2Val,
// Pour compat héritage (peut servir pour les filtres fixes)
//'taille' => $taille ?? null,
//'couleur' => $couleur ?? null,
'quantity' => $quantityDisplay,
'qtReserved' => $qtReserved,
'createdAt' => $entity->getCreatedAt()?->format('Y-m-d\TH:i:s'),
'inPromo' => $promoActive,
];
}
return new JsonResponse($output);
}
/**
* @Route("/show/{id}", name="produit_declination_value_show", methods={"GET"}, options={"expose"=true})
*/
public function show(ProduitDeclinationValue $declinaison, Request $request): Response {
$rights = $this->rightService->getAllRights($this->getUser());
if( !in_array('PRODUIT', $rights)) {
$request->getSession()->getFlashBag()->add('danger', "Accès refusé");
return $this->redirect($this->generateUrl('index'));
}
$formFile = $this->createForm(UploadFileProduitDecType::class, $declinaison);
$form = $this->createForm(FilterDocumentType::class);
return $this->render('@admin/produit_declination_value/fiche_declinaison_produit.html.twig', [
'produit' => $declinaison,
'formFile' => $formFile->createView(),
'form' => $form->createView(),
'rights' => $rights
]);
}
/**
* @Route("/", name="produit_declination_value_index", methods={"GET"},options = { "expose" = true})
*/
public function index(Request $request): Response {
$rights = $this->rightService->getAllRights($this->getUser());
if (!in_array('PRODUIT', $rights)) {
$request->getSession()->getFlashBag()->add('danger', "Accès refusé");
return $this->redirect($this->generateUrl('index'));
}
$form = $this->createForm(FilterProduitType::class);
// ⚠️ on ne touche pas à l’existant
$declinations = $this->declinationRepository->findAll();
// ✅ nouvelle liste pour le filtre uniquement
$declinationsFilter = $this->declinationRepository->findAllForFilter();
return $this->render('@admin/produit_declination_value/list_declinaisons_produits.html.twig', [
'form' => $form->createView(),
'declinations' => $declinations, // reste disponible ailleurs
'declinationsFilter' => $declinationsFilter, // utilisé par le filtre UI
'rights' => $rights,
]);
}
/**
* @Route("/stock/edit/{id}", name="stock_edit", methods="GET|POST", options = { "expose" = true})
*/
public function editStock(Request $request, Stock $stock,EntityManagerInterface $em) {
if( $request->isMethod('POST')) {
$stock->setQtStock((int) $request->get('edit_stock')['qt_total']);
$em->flush();
$request->getSession()->getFlashBag()->add('success', "Stock modifié avec succès");
}
return $this->redirectToRoute('produit_declination_value_show', ['id' => $stock->getDeclinationProduit()->getId()]);
}
public function getPicture($entity, $request): string
{
$baseurl = $request->getScheme() . '://' . $request->getHttpHost() . $request->getBasePath();
if ($entity->getPicture()) {
foreach ($entity->getPicture() as $item) {
if ($item->getIsSelected()) {
$path = $this->uploaderHelper->asset($item, 'file');
if ($path) {
return $baseurl . $path;
}
}
}
}
// Aucune image sélectionnée
return '';
}
/**
* @Route("/file/{id}", name="produit_declination_value_file", methods={"GET","POST"}, options={"expose"=true})
*/
public function addFile(Request $request, ProduitDeclinationValue $produit,EntityManagerInterface $em): Response {
$form = $this->createForm(UploadFileProduitDecType::class, $produit);
$form->handleRequest($request);
if( $form->isSubmitted() && $form->isValid()) {
$pictures = $request->files->get('upload_file_produit_dec')['picture'];
if( $request->request->get('selectFile')) {
foreach ($produit->getPicture() as $picture) {
if( $picture->getIsSelected() == true) {
$picture->setIsSelected(null);
break;
}
}
$imageSelected = $this->fileRepository->find($request->request->get('selectFile'));
$imageSelected->setIsSelected(true);
$em->flush();
}
foreach ($pictures as $key => $picture) {
if( !$request->request->get('selectFile') && $key == 0) {
$file = new File();
$file->setFile($picture);
$file->SetIsSelected(true);
$em->persist($file);
$produit->addPicture($file);
$em->flush();
} else {
$file = new File();
$file->setFile($picture);
$em->persist($file);
$produit->addPicture($file);
$em->flush();
}
}
$request->getSession()->getFlashBag()->add('success', "Image ajouté avec succès");
return $this->redirect($this->generateUrl('produit_declination_value_show', ['id' => $produit->getId()]));
}
return false;
}
/**
* @Route("/searchgroup", name="get_group_produit", methods="GET|POST", options = { "expose" = true})
*/
public function searchGroupProduit(Request $request,EntityManagerInterface $em) {
$result = [];
$entities = $this->produitDeclinationValueRepository->findDeclinationValueWithDeclination($request->get('color'), $request->get('produit'));
$entities = $this->orderWithSise($entities);
foreach ($entities as $entity) {
$quantity = 0;
$array['type'] = 'produitDeclination';
$stocks = $em->getRepository(Stock::class)->findByDeclinationProduit($entity->getId());
foreach ($stocks as $stock) {
$quantity = $quantity + ($stock->getQtStock() - $stock->getQtReserved());
}
$promotion = $this->produitDeclinationValueRepository->find($entity->getId())->getProduit()->getPromotion();
$array['value'] = null;
$array['promotionType'] = null;
if( $promotion) {
$date = new \DateTime('now');
if( $promotion->getStartAt() <= $date && $promotion->getEndAt() >= $date) {
$array['value'] = $promotion->getDiscountValue();
$array['promotionType'] = $promotion->getDiscountType();
}
}
$picture = $this->getPicture($this->produitDeclinationValueRepository->find($entity->getId()), $request);
$array['picture'] = $picture;
$array['quantity'] = $quantity;
$array['id'] = $entity->getId();
$array['name'] = $entity->getName();
$array['reference'] = $entity->getReference();
$array['description'] = $entity->getDescription();
$array['price_ht'] = $entity->getPriceHt();
$array['price_ttc'] = $entity->getProduit()->getPriceTtc();
$array['unit'] = $entity->getProduit()->getUnit();
$array['buyingPriceTtc'] = $entity->getBuyingPriceTtc();
$array['tva'] = $entity->getProduit()->getTva() ? $entity->getProduit()->getTva()->getNumber() : 0;
$array['tva_id'] = $entity->getProduit()->getTva() ? $entity->getProduit()->getTva()->getId() : 1;
$result[] = $array;
}
return new JsonResponse($result);
}
public function orderWithSise($entities) {
$allSize = ["XS", "S", "M", "L", "XL", "XXL", "XXXL", "XXXXL"];
$isLetter = false;
if($entities)
foreach ($entities[0]->getGroupDeclinationValues() as $group) {
if( $group->getDeclination()->getName() == "Taille") {
if( in_array($group->getValue()->getName(), $allSize)) $isLetter = true;
}
}
if( $isLetter == true) {
usort($entities, function ($a, $b) use ($allSize) {
foreach ($a->getGroupDeclinationValues() as $group) {
if( $group->getDeclination()->getName() == "Taille")
$pos_a = array_search($group->getValue()->getName(), $allSize);
}
foreach ($b->getGroupDeclinationValues() as $group) {
if( $group->getDeclination()->getName() == "Taille")
$pos_b = array_search($group->getValue()->getName(), $allSize);
}
return $pos_a - $pos_b;
});
}
return $entities;
}
/**
* @Route("/info/edit/{id}", name="produit_dec_info_edit", methods={"GET","POST"}, options={"expose"=true})
*/
public function editInfo(Request $request, ProduitDeclinationValue $produit,EntityManagerInterface $em): Response {
$this->hasRight($request,'PRODUIT_UPDATE');
if( $request->isMethod('POST') && $request->get('form_info')) {
$produit->setDescription($request->get('form_info')['description']);
$produit->setName($request->get('form_info')['name']);
$message = $this->getUser()->getFirstName() . " a modifié l'info de Déclinaison " . $produit->getReference();
$this->activityService->addActivity('info', $message, $produit->getProduit(), $this->getUser(), 'produit');
$em->flush();
return $this->redirectToRoute('produit_declination_value_show', ['id' => $produit->getId(),"tab"=>"content_informations"]);
}
return false;
}
}