@@ -459,7 +459,6 @@ class OrderController extends AdminController | |||
$parameters['form_order_send_payment_link'] = $this->createCustomForm(OrderSendPaymentLink::class, 'orderSendPaymentLink', $parameters)->createView(); | |||
break; | |||
case OrderStatus::ALIAS_ERROR_PAYMENT_ONLINE : | |||
case OrderStatus::ALIAS_WAITING_PAYMENT_CREDIT : | |||
case OrderStatus::ALIAS_WAITING_PAYMENT_ONLINE : | |||
$parameters['form_order_send_payment_link'] = $this->createCustomForm(OrderSendPaymentLink::class, 'orderSendPaymentLink', $parameters)->createView(); | |||
$parameters['form_order_payment'] = $this->createCustomForm(OrderPaymentType::class, 'orderPayment', $parameters, false)->createView(); | |||
@@ -467,8 +466,7 @@ class OrderController extends AdminController | |||
$parameters['form_order_status'] = $this->createCustomForm(OrderStatusType::class, 'orderStatus', $parameters)->createView(); | |||
$parameters['form_order_invoice_address'] = $this->createCustomForm(OrderInvoiceAddressType::class, 'orderInvoiceAddress', $parameters)->createView(); | |||
break; | |||
case OrderStatus::ALIAS_PAID_BY_CREDIT : | |||
case OrderStatus::ALIAS_PAID_ONLINE : | |||
case OrderStatus::ALIAS_PAID : | |||
case OrderStatus::ALIAS_WAITING_DELIVERY : | |||
case OrderStatus::ALIAS_WAITING_BANK_RETURN : | |||
$parameters['form_order_payment'] = $this->createCustomForm(OrderPaymentType::class, 'orderPayment', $parameters, false)->createView(); |
@@ -97,6 +97,17 @@ class ProductFamilyController extends AdminController | |||
'expanded' => true | |||
)); | |||
$formBuilder->add('behaviorDisplaySale', ChoiceType::class, array( | |||
'empty_data' => ProductFamily::BEHAVIOR_DISPLAY_SALE_BY_QUANTITY, | |||
'choices' => array( | |||
'field.ProductFamily.behaviorDisplaySaleOptions.' . ProductFamily::BEHAVIOR_DISPLAY_SALE_BY_MEASURE => ProductFamily::BEHAVIOR_DISPLAY_SALE_BY_MEASURE, | |||
'field.ProductFamily.behaviorDisplaySaleOptions.' . ProductFamily::BEHAVIOR_DISPLAY_SALE_BY_QUANTITY => ProductFamily::BEHAVIOR_DISPLAY_SALE_BY_QUANTITY, | |||
), | |||
'translation_domain' => 'lcshop', | |||
'multiple' => false, | |||
'expanded' => true | |||
)); | |||
$formBuilder->add('behaviorAddToCart', ChoiceType::class, array( | |||
'data' => $entity->getBehaviorAddToCart() ? $entity->getBehaviorAddToCart() : 'simple', | |||
'choices' => array( | |||
@@ -279,7 +290,7 @@ class ProductFamilyController extends AdminController | |||
'entity' => $entity, | |||
'delete_form' => $deleteForm->createView(), | |||
'sortableProductsField' => $sortableProductsField, | |||
'totalProductOrdered' => $this->orderUtils->getTotalProductOrderedLastWeeks($entity) | |||
'productsSalesStatistic' => $this->orderUtils->getProductsSalesStatistic($entity) | |||
]; | |||
return $this->executeDynamicMethod('render<EntityName>Template', ['edit', $this->entity['templates']['edit'], $parameters]); | |||
@@ -335,7 +346,7 @@ class ProductFamilyController extends AdminController | |||
'entity' => $entity, | |||
'categories' => $categories, | |||
'sortableProductsField' => array(), | |||
'totalProductOrdered' => array('total'=>0) | |||
'productsSalesStatistic' => null | |||
]; | |||
return $this->executeDynamicMethod('render<EntityName>Template', ['new', $this->entity['templates']['new'], $parameters]); |
@@ -12,11 +12,13 @@ use Symfony\Component\Form\AbstractType; | |||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; | |||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; | |||
use Symfony\Component\Form\Extension\Core\Type\EmailType; | |||
use Symfony\Component\Form\Extension\Core\Type\FileType; | |||
use Symfony\Component\Form\Extension\Core\Type\TextareaType; | |||
use Symfony\Component\Form\Extension\Core\Type\TextType; | |||
use Symfony\Component\Form\FormBuilderInterface; | |||
use Symfony\Component\OptionsResolver\OptionsResolver; | |||
use Symfony\Component\Security\Core\Security; | |||
use Symfony\Component\Validator\Constraints\File; | |||
class TicketMessageType extends AbstractType | |||
{ | |||
@@ -28,6 +30,23 @@ class TicketMessageType extends AbstractType | |||
'label' => 'field.Ticket.yourAnswer', | |||
'translation_domain' => 'lcshop', | |||
]) | |||
->add('image', FileType::class, [ | |||
'label' => 'Photo', | |||
'mapped' => false, | |||
'required' => false, | |||
'constraints' => [ | |||
new File([ | |||
'maxSize' => '2048k', | |||
'mimeTypes' => [ | |||
'image/png', | |||
'image/jpeg', | |||
'image/jpg', | |||
'image/gif', | |||
], | |||
'mimeTypesMessage' => "Mauvais format d'image (formats acceptés : jpeg, png, gif)", | |||
]) | |||
], | |||
]) | |||
->add('closeTicket', CheckboxType::class, [ | |||
'label' => 'field.Ticket.closeTicket', | |||
'translation_domain' => 'lcshop', |
@@ -11,11 +11,13 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType; | |||
use Symfony\Component\Form\AbstractType; | |||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; | |||
use Symfony\Component\Form\Extension\Core\Type\EmailType; | |||
use Symfony\Component\Form\Extension\Core\Type\FileType; | |||
use Symfony\Component\Form\Extension\Core\Type\TextareaType; | |||
use Symfony\Component\Form\Extension\Core\Type\TextType; | |||
use Symfony\Component\Form\FormBuilderInterface; | |||
use Symfony\Component\OptionsResolver\OptionsResolver; | |||
use Symfony\Component\Security\Core\Security; | |||
use Symfony\Component\Validator\Constraints\File; | |||
class TicketType extends AbstractType | |||
{ | |||
@@ -95,6 +97,23 @@ class TicketType extends AbstractType | |||
]) | |||
->add('message', TextareaType::class, [ | |||
'label' => 'Message' | |||
]) | |||
->add('image', FileType::class, [ | |||
'label' => 'Photo', | |||
'mapped' => false, | |||
'required' => false, | |||
'constraints' => [ | |||
new File([ | |||
'maxSize' => '2048k', | |||
'mimeTypes' => [ | |||
'image/png', | |||
'image/jpeg', | |||
'image/jpg', | |||
'image/gif', | |||
], | |||
'mimeTypesMessage' => "Mauvais format d'image (formats acceptés : jpeg, png, gif)", | |||
]) | |||
], | |||
]); | |||
} | |||
@@ -50,8 +50,8 @@ class EntityManager | |||
if(method_exists($entity, 'setUpdatedBy')) { | |||
$entity->setUpdatedBy($this->getUserSystem()); | |||
} | |||
if(method_exists($entity, 'setCreadtedBy')) { | |||
$entity->setCreadtedBy($this->getUserSystem()); | |||
if(method_exists($entity, 'setCreatedBy')) { | |||
$entity->setCreatedBy($this->getUserSystem()); | |||
} | |||
} | |||
@@ -79,6 +79,13 @@ class EntityManager | |||
return $this; | |||
} | |||
public function remove($entity): self | |||
{ | |||
$this->entityManager->remove($entity) ; | |||
return $this ; | |||
} | |||
public function flush(): self | |||
{ | |||
$this->entityManager->flush(); | |||
@@ -93,18 +100,28 @@ class EntityManager | |||
return $this; | |||
} | |||
protected function persist($entity) | |||
public function persist($entity) | |||
{ | |||
$this->entityManager->persist($entity); | |||
} | |||
public function refresh($entity) | |||
{ | |||
$this->entityManager->refresh($entity); | |||
} | |||
public function getClassMetadata($className) | |||
{ | |||
return $this->entityManager->getClassMetadata($className) ; | |||
} | |||
public function getEntityName($className) | |||
{ | |||
if (substr($className, -9) === 'Interface') { | |||
return $this->entityManager->getClassMetadata($className)->getName(); | |||
} else { | |||
} | |||
else { | |||
return $className; | |||
} | |||
} | |||
} |
@@ -0,0 +1,88 @@ | |||
<?php | |||
namespace Lc\ShopBundle\Model; | |||
use Doctrine\ORM\Mapping as ORM; | |||
use Symfony\Component\HttpFoundation\File\File; | |||
use Vich\UploaderBundle\Mapping\Annotation as Vich; | |||
trait OpenGraphTrait | |||
{ | |||
/** | |||
* @ORM\Column(type="string", nullable=true) | |||
*/ | |||
protected $openGraphTitle; | |||
/** | |||
* @ORM\Column(type="text", nullable=true) | |||
*/ | |||
protected $openGraphDescription; | |||
/** | |||
* @ORM\Column(type="string", length=255, nullable=true) | |||
*/ | |||
protected $openGraphImage; | |||
/** | |||
* @Vich\UploadableField(mapping="images", fileNameProperty="openGraphImage") | |||
* @var File | |||
*/ | |||
protected $openGraphImageFile; | |||
public function getOpenGraphTitle(): ?string | |||
{ | |||
return $this->openGraphTitle; | |||
} | |||
public function setOpenGraphTitle(string $openGraphTitle): self | |||
{ | |||
$this->openGraphTitle = $openGraphTitle; | |||
return $this; | |||
} | |||
public function getOpenGraphDescription(): ?string | |||
{ | |||
return $this->openGraphDescription; | |||
} | |||
public function setOpenGraphDescription(?string $openGraphDescription): self | |||
{ | |||
$this->openGraphDescription = $openGraphDescription; | |||
return $this; | |||
} | |||
public function getOpenGraphImage(): ?string | |||
{ | |||
return $this->openGraphImage; | |||
} | |||
public function setOpenGraphImage(?string $openGraphImage): self | |||
{ | |||
$this->openGraphImage = $openGraphImage; | |||
return $this; | |||
} | |||
public function setOpenGraphImageFile(File $openGraphImageFile = null) | |||
{ | |||
$this->openGraphImageFile = $openGraphImageFile; | |||
// VERY IMPORTANT: | |||
// It is required that at least one field changes if you are using Doctrine, | |||
// otherwise the event listeners won't be called and the file is lost | |||
if ($openGraphImageFile) { | |||
// if 'updatedAt' is not defined in your entity, use another property | |||
$this->updatedAt = new \DateTime('now'); | |||
} | |||
} | |||
public function getOpenGraphImageFile() | |||
{ | |||
return $this->openGraphImageFile ; | |||
} | |||
} |
@@ -73,7 +73,7 @@ abstract class OrderShop extends AbstractEntity implements FilterMerchantInterfa | |||
protected $orderPayments; | |||
/** | |||
* @ORM\OneToMany(targetEntity="Lc\ShopBundle\Context\OrderProductInterface", mappedBy="orderShop", cascade={"persist", "remove"}, orphanRemoval=true) | |||
* @ORM\OneToMany(targetEntity="Lc\ShopBundle\Context\OrderProductInterface", mappedBy="orderShop", cascade={"persist", "remove"}, orphanRemoval=true, fetch="EAGER") | |||
*/ | |||
protected $orderProducts; | |||
@@ -303,8 +303,20 @@ abstract class OrderShop extends AbstractEntity implements FilterMerchantInterfa | |||
/** | |||
* @return Collection|OrderPayment[] | |||
*/ | |||
public function getOrderPayments(): Collection | |||
public function getOrderPayments($meanPayment = null): Collection | |||
{ | |||
if($meanPayment) { | |||
$orderPaymentsReturn = new ArrayCollection() ; | |||
foreach($this->orderPayments as $orderPayment) { | |||
if($orderPayment->getMeanPayment() == $meanPayment) { | |||
$orderPaymentsReturn[] = $orderPayment ; | |||
} | |||
} | |||
return $orderPaymentsReturn ; | |||
} | |||
return $this->orderPayments; | |||
} | |||
@@ -14,11 +14,10 @@ abstract class OrderStatus | |||
const ALIAS_CART = 'cart' ; | |||
const ALIAS_CART_CANCELED = 'cart-canceled' ; | |||
const ALIAS_WAITING_PAYMENT_ONLINE = 'waiting-payment-online' ; | |||
const ALIAS_WAITING_PAYMENT_CREDIT = 'waiting-payment-credit' ; | |||
const ALIAS_WAITING_BANK_RETURN = 'waiting-bank-return' ; | |||
const ALIAS_PAID_ONLINE = 'paid-online' ; | |||
const ALIAS_PARTIAL_PAYMENT = 'partial-payment' ; | |||
const ALIAS_PAID = 'paid' ; | |||
const ALIAS_ERROR_PAYMENT_ONLINE = 'error-payment-online' ; | |||
const ALIAS_PAID_BY_CREDIT = 'paid-by-credit' ; | |||
const ALIAS_WAITING_DELIVERY = 'waiting-delivery' ; | |||
const ALIAS_WAITING_DELIVERY_WITH_PAYMENT = 'waiting-delivery-with-payment' ; | |||
const ALIAS_DELIVERED_WITHOUT_PAYMENT = 'delivered-without-payment' ; | |||
@@ -30,8 +29,7 @@ abstract class OrderStatus | |||
//TODO : AJOUTER un champ valid ds orderSTATUS | |||
static $statusAliasAsValid = [ | |||
self::ALIAS_PAID_ONLINE, | |||
self::ALIAS_PAID_BY_CREDIT, | |||
self::ALIAS_PAID, | |||
self::ALIAS_WAITING_DELIVERY, | |||
self::ALIAS_WAITING_BANK_RETURN, | |||
self::ALIAS_WAITING_DELIVERY_WITH_PAYMENT, | |||
@@ -46,8 +44,8 @@ abstract class OrderStatus | |||
static $statusAliasAsCart = [ | |||
self::ALIAS_CART, | |||
self::ALIAS_PARTIAL_PAYMENT, | |||
self::ALIAS_WAITING_PAYMENT_ONLINE, | |||
self::ALIAS_WAITING_PAYMENT_CREDIT, | |||
self::ALIAS_ERROR_PAYMENT_ONLINE | |||
]; | |||
@@ -66,6 +66,10 @@ abstract class Product extends AbstractEntity implements SortableInterface, Prod | |||
$title .= ' - ' . $this->getTitle(); | |||
} | |||
if($this->getProductFamily()->hasProductsWithVariousWeight()) { | |||
$title .= ' - '.$this->getQuantityLabelInherited() ; | |||
} | |||
return $title; | |||
} | |||
@@ -26,6 +26,9 @@ abstract class ProductFamily extends AbstractDocumentEntity implements ProductPr | |||
const BEHAVIOR_COUNT_STOCK_BY_PRODUCT_FAMILY = 'by-product-family' ; | |||
const BEHAVIOR_COUNT_STOCK_BY_PRODUCT = 'by-product' ; | |||
const BEHAVIOR_DISPLAY_SALE_BY_MEASURE = 'by-measure' ; | |||
const BEHAVIOR_DISPLAY_SALE_BY_QUANTITY = 'by-quantity' ; | |||
const BEHAVIOR_STOCK_WEEK_RENEWABLE = 'renewable'; | |||
const BEHAVIOR_STOCK_WEEK_RENEWABLE_VALIDATION = 'renewable-with-validation'; | |||
const BEHAVIOR_STOCK_WEEK_NON_RENEWABLE = 'non-renewable'; | |||
@@ -141,6 +144,11 @@ abstract class ProductFamily extends AbstractDocumentEntity implements ProductPr | |||
*/ | |||
protected $behaviorCountStock; | |||
/** | |||
* @ORM\Column(type="string", length=255) | |||
*/ | |||
protected $behaviorDisplaySale; | |||
/** | |||
* @ORM\Column(type="date", nullable=true) | |||
*/ | |||
@@ -541,6 +549,17 @@ abstract class ProductFamily extends AbstractDocumentEntity implements ProductPr | |||
return $this; | |||
} | |||
public function getBehaviorDisplaySale(): ?string | |||
{ | |||
return $this->behaviorDisplaySale; | |||
} | |||
public function setBehaviorDisplaySale(string $behaviorDisplaySale): self | |||
{ | |||
$this->behaviorDisplaySale = $behaviorDisplaySale; | |||
return $this; | |||
} | |||
public function isPropertyNoveltyOnline(): ?bool | |||
{ | |||
if ($this->getPropertyNoveltyExpirationDate()) { |
@@ -44,6 +44,11 @@ abstract class TicketMessage extends AbstractEntity | |||
*/ | |||
protected $answerByAdmin; | |||
/** | |||
* @ORM\Column(type="string", length=255, nullable=true) | |||
*/ | |||
protected $imageFilename; | |||
public function __toString() | |||
{ | |||
return $this->message; | |||
@@ -84,4 +89,16 @@ abstract class TicketMessage extends AbstractEntity | |||
return $this; | |||
} | |||
public function getImageFilename(): ?string | |||
{ | |||
return $this->imageFilename; | |||
} | |||
public function setImageFilename(?string $imageFilename): self | |||
{ | |||
$this->imageFilename = $imageFilename; | |||
return $this; | |||
} | |||
} |
@@ -805,7 +805,6 @@ table th .select2-container--default .select2-selection--single { | |||
/* line 296, ../../sass/backend/custom.scss */ | |||
#lc-product-family-edit .btn-add-product { | |||
margin: 20px 0; | |||
float: right; | |||
} | |||
@@ -5,7 +5,8 @@ jQuery(document).ready(function () { | |||
initNotice(); | |||
initBtnEditReminder(); | |||
initBtnWriteToUser(); | |||
initCollectionWidget() | |||
initCollectionWidget(); | |||
initBtnShowTotalOrderProduct(); | |||
$('form').on('focus', 'input[type=number]', function (e) { | |||
$(this).on('wheel.disableScroll', function (e) { | |||
e.preventDefault() | |||
@@ -155,6 +156,11 @@ function initAdminLtePlugin() { | |||
}); | |||
$(document).on('click', '[data-toggle="lightbox"]', function(event) { | |||
event.preventDefault(); | |||
$(this).ekkoLightbox(); | |||
}); | |||
} | |||
function moment() { | |||
@@ -358,3 +364,67 @@ function resetNumItemsCollectionField($collectionWidget){ | |||
resetNumItemsCollectionField($collectionWidget); | |||
}); | |||
} | |||
function initBtnShowTotalOrderProduct(){ | |||
$('.lc-show-products-sales-statistic').unbind('click').on('click', function (){ | |||
$btn = $(this); | |||
var url = $(this).data('url'); | |||
$('#modal-products-sales-statistic').remove(); | |||
$.ajax({ | |||
url: url, | |||
method: "POST", | |||
dataType: "json", | |||
success: function (response) { | |||
$('body').append(response.data); | |||
$('#modal-products-sales-statistic').modal('show'); | |||
initModalProductsSalesStatistic(response.statistics); | |||
} | |||
}); | |||
}); | |||
} | |||
function initModalProductsSalesStatistic(statistics) { | |||
var chart = null; | |||
$('.btn-products-sales-statistic').off('click'); | |||
$('.btn-products-sales-statistic').on('click', function () { | |||
$('.table-products-sales-statistic').hide(); | |||
$('.btn-products-sales-statistic').addClass('btn-secondary').removeClass('btn-primary'); | |||
$(this).removeClass('btn-secondary').addClass('btn-primary'); | |||
$('#table-products-sales-statistic-'+$(this).data('property-name')).show() | |||
if (chart) chart.destroy(); | |||
$(this).removeClass('btn-secondary'); | |||
chart = drawProductsSalesStatistic(statistics,$(this).data('property-name')) | |||
}); | |||
$('.btn-products-sales-statistic').first().click(); | |||
} | |||
function drawProductsSalesStatistic(statictics, propertyName) { | |||
var options = { | |||
bezierCurve : false, | |||
tooltips: { | |||
callbacks: { | |||
label: (item) => item.yLabel , | |||
}, | |||
}, | |||
}; | |||
chart = new Chart(document.getElementById("chart"), { | |||
"type": "line", | |||
"data": { | |||
"labels": Object.values(statictics.label), | |||
"datasets": [{ | |||
"label": "Vente de produits / semaine", | |||
"data": Object.values(statictics.data[propertyName].data), | |||
"fill": false, | |||
"borderColor": "rgb(75, 192, 192)", | |||
"lineTension": 0.1 | |||
}] | |||
}, | |||
"options": options | |||
}); | |||
return chart; | |||
} | |||
@@ -115,7 +115,7 @@ appOrder = new Vue({ | |||
} | |||
}, | |||
preventFormSubmit: function (){ | |||
$(document).keypress(function(e) { | |||
$('#lc-order-edit').keypress(function(e) { | |||
if (e.which == 13) { | |||
e.preventDefault(); | |||
} |
@@ -161,7 +161,11 @@ $(window).on('load', function () { | |||
}, | |||
behaviorPriceValue: function () { | |||
return this.productFamily.behaviorPrice; | |||
} | |||
}, | |||
hasExportInfo: function () { | |||
if((this.exportNote != null && this.exportNote != '') || (this.exportTitle != null && this.exportTitle != ''))return true | |||
else return false; | |||
}, | |||
}, | |||
data() { | |||
return Object.assign( | |||
@@ -193,7 +197,9 @@ $(window).on('load', function () { | |||
expirationDateInherited: false, | |||
availableQuantityInherited: false, | |||
availableQuantityDefaultInherited: false, | |||
propertyExpirationDateInherited: false | |||
propertyExpirationDateInherited: false, | |||
exportTitle: null, | |||
exportNote: null | |||
}, window.productForm[this.keyForm]) | |||
}, | |||
mounted: function () { | |||
@@ -227,7 +233,11 @@ $(window).on('load', function () { | |||
}else{ | |||
this.status = 1; | |||
$(this.$el).removeClass('disabled'); | |||
if(this.hasExportInfo){ | |||
alert('Cette déclinaison contient un titre ou une note à l\'export'); | |||
} | |||
} | |||
}, | |||
decode(str){ | |||
var textArea = document.createElement('textarea'); |
@@ -293,7 +293,7 @@ table th .select2-container--default .select2-selection--single{padding:0.3rem 0 | |||
#lc-product-family-edit .products-collection-table th:last-child{border-right: 1px solid #dee2e6;} | |||
#lc-product-family-edit .products-collection-table td{border-left: 1px solid #dee2e6; text-align: center; font-size: 13px; border-bottom: 1px solid #dee2e6;} | |||
#lc-product-family-edit .products-collection-table td:last-child{border-right: 1px solid #dee2e6; white-space: nowrap; } | |||
#lc-product-family-edit .btn-add-product {margin: 20px 0; float: right;} | |||
#lc-product-family-edit .btn-add-product {float: right;} | |||
#lc-product-family-edit .inherited {color: #888; font-style: italic; font-weight: initial;} | |||
#lc-product-family-edit .products-collection-table td .value {min-width: 80%; margin: auto; min-height: 35px; cursor: pointer;} | |||
#lc-product-family-edit .products-collection-table td .modal {text-align: left;} |
@@ -52,6 +52,7 @@ group: | |||
initReduction: Réduction | |||
export: Note à l'export | |||
stockNegative: Produits stocks négatif | |||
purchaseOrder: Bons de commandes | |||
ReductionCatalog: | |||
info: Informations principal | |||
conditions: Conditions d'application | |||
@@ -79,6 +80,8 @@ group: | |||
waitingBankReturn: Commandes en attente de retour banque | |||
list: Liste des commandes | |||
giftVoucher: Commandes de bons cadeaux | |||
multiplePayment: Paiements multiples | |||
Ticket: | |||
listMessages: Liste des messages | |||
list: Tickets ouverts | |||
@@ -98,6 +101,8 @@ group: | |||
listLoopBesancon: Adresses de Besançon à spécifier (lat / long) | |||
User: | |||
item: Utilisateur | |||
Notification: | |||
main: Notifications | |||
@@ -141,10 +146,11 @@ error: | |||
deliverySlotMissing: Vous n'avez pas de créneau de livraison | |||
productUnavailable: Certains produits ne sont plus disponibles | |||
nextStatusNotAllow: La commande ne peut passer à ce statut | |||
noPayment: Le montant de la commande n'a pas été règlé dans sa totalité | |||
incompletePayment: Le montant de la commande n'a pas été règlé dans sa totalité | |||
otherOrderAlreadyExist: Une autre commande existe déjà pour cet utilisateur, vous avez été redirigé. | |||
minimumAmountZone: Montant minimum de commande non respecté pour cette zone | |||
minimumAmountPointSale: Montant minimum de commande non respecté pour ce point de vente | |||
noPayment: Aucun paiement n'a été enregistré pour cette commande | |||
reductionCart: | |||
conditionsError: Cette réduction ne peut pas être appliqué sur cette commande | |||
date: La réduction n'est plus active | |||
@@ -294,8 +300,8 @@ field: | |||
emailFromPurchaseOrder: "Email (From) : bons de commande" | |||
order: Commande | |||
subject: Sujet | |||
metaTitle: Meta title | |||
metaDescription: Meta description | |||
metaTitle: Titre (meta) | |||
metaDescription: Description (meta) | |||
users: Utilisateurs | |||
total: Total | |||
products: Produits | |||
@@ -308,6 +314,10 @@ field: | |||
groupUsers: Groupes | |||
ticketTypesNotification: Catégorie ticket | |||
newsletter: Newsletter | |||
isEligibleTicketRestaurant: Éligible ticket restaurant | |||
openGraphTitle: Titre (OpenGraph) | |||
openGraphDescription: Description (OpenGraph) | |||
openGraphImageFile: Image (OpenGraph) | |||
PointSale: | |||
code: Code | |||
@@ -336,6 +346,7 @@ field: | |||
availableQuantityDefault: Quantité par défaut | |||
quantity: Quantité | |||
behaviorCountStock: Gestion du stock | |||
behaviorDisplaySale: Affichage du total des ventes | |||
differentSupplierTaxRate: TVA différente pour le producteur | |||
supplierTaxRate: TVA du producteur | |||
propertyNoveltyExpirationDateActive: Marquer le produit comme "nouveauté" jusqu'au | |||
@@ -352,6 +363,7 @@ field: | |||
displayPriceUnitRef: Afficher le prix par unité de référence | |||
behaviorPrice: Travailler avec des tarifs | |||
productsQuantityAsTitle: Titre équivalent à quantité | |||
isDisabledOnPurchaseOrder: Automatiquement désactiver ce produit dans les bons de commandes | |||
behaviorPriceOptions: | |||
by-piece: À la pièce | |||
by-reference-unit: Par unité de référence | |||
@@ -360,6 +372,9 @@ field: | |||
by-product-family: Gérer le stock par produit (à l'unité) | |||
by-product: Gérer le stock par déclainaison | |||
unlimited: Illimité | |||
behaviorDisplaySaleOptions: | |||
by-measure: Par unité de référence | |||
by-quantity: Par quantité | |||
behaviorStockWeek: Renouvelement du stock chaque semaine | |||
behaviorStockWeekOptions: | |||
renewable: Renouvelable | |||
@@ -544,6 +559,7 @@ action: | |||
product: | |||
editStock: Gérer les stocks | |||
editProductFamily: Éditer le produit | |||
statSales: Afficher plus de semaines | |||
user: | |||
account: Mon compte | |||
logout: Me déconnecter |
@@ -0,0 +1,2 @@ | |||
{% import '@LcShop/backend/productfamily/macros.html.twig' as macros %} | |||
{{ macros.product_family_sales_statistic(orderUtils.getProductsSalesStatistic(item, 2), item) }} |
@@ -1,2 +0,0 @@ | |||
{% import '@LcShop/backend/productfamily/macros.html.twig' as macros %} | |||
{{ macros.total_order_product_family(orderUtils.getTotalProductOrderedLastWeeks(item, 2, true)['total'], item, true) }} |
@@ -1,316 +0,0 @@ | |||
{% extends '@LcShop/backend/default/list.html.twig' %} | |||
{% import '@LcShop/backend/default/block/macros.html.twig' as macros %} | |||
{% import '@LcShop/backend/productfamily/macros.html.twig' as product_family_macros %} | |||
{% block table %} | |||
<script> | |||
window.mixinUnitValues = { | |||
unitsList: {{ getUnitsList()|json_encode|raw }} | |||
}; | |||
window.mixinPriceValues = { | |||
taxRatesList: {{ getTaxRatesList()|json_encode|raw }} | |||
}; | |||
window.appProductFamilyValues = {}; | |||
window.productUnitPriceValues = {}; | |||
window.productForm ={}; | |||
window.formProductTemplate = {}; | |||
</script> | |||
<ul id="product-family-advanced-types-list"> | |||
{% for i,advancedType in advancedTypes %} | |||
{% set formValues = advancedType.vars.value %} | |||
{% set productFamily = paginator.currentPageResults[i] %} | |||
<script> | |||
window.appProductFamilyValues[{{ i }}] = { | |||
title: "{{ productFamily.title }}", | |||
behaviorCountStock: "{{ productFamily.behaviorCountStock }}", | |||
behaviorStockWeek: "{{ productFamily.behaviorStockWeek }}", | |||
availableQuantity: "{{ productFamily.availableQuantity }}", | |||
availableQuantityDefault: "{{ productFamily.availableQuantityDefault }}", | |||
{% if productFamily.propertyNoveltyExpirationDate %}propertyNoveltyExpirationDate: {{ productFamily.propertyNoveltyExpirationDate }},{% endif %} | |||
typeExpirationDate: "{{ productFamily.typeExpirationDate }}", | |||
behaviorExpirationDate: "{{ productFamily.behaviorExpirationDate }}", | |||
propertyExpirationDate: "{{ productFamily.propertyExpirationDate }}", | |||
{% if productFamily.activeProducts %}activeProducts: {{ productFamily.activeProducts }},{% endif %} | |||
}; | |||
window.productUnitPriceValues[{{ i }}] = { | |||
{% if productFamily.activeProducts %}activeProducts: {{ productFamily.activeProducts }},{% endif %} | |||
behaviorPrice: "{{ productFamily.behaviorPrice }}", | |||
unit: parseInt({{ productFamily.unit.id }}), | |||
{% if productFamily.taxRate %}taxRate: parseInt({{ productFamily.taxRate.id }}),{% endif %} | |||
{% if productFamily.supplierTaxRate %}supplierTaxRate: parseInt({{ productFamily.supplierTaxRate.id }}),{% endif %} | |||
quantity: "{{ productFamily.quantity }}", | |||
price: parseFloat({{ productFamily.price }}).toFixed(3), | |||
priceByRefUnit: parseFloat({{ productFamily.priceByRefUnit }}).toFixed(3), | |||
buyingPrice: parseFloat({{ productFamily.buyingPrice }}).toFixed(3), | |||
buyingPriceByRefUnit: parseFloat({{ productFamily.buyingPriceByRefUnit }}).toFixed(3), | |||
} | |||
</script> | |||
<li class="product-family-advanced-type-item"> | |||
<div id="product-family-advanced-type-{{ i }}" class="product-family-advanced-type"> | |||
{{ form_start(advancedType, {"attr": {"ref": 'advanced-type'}}) }} | |||
<product-unit-price ref="productUnitPrice" inline-template key-form="productfamily"> | |||
<div class="row"> | |||
{{ form_row(advancedType.unit, {"attr":{'v-model': 'unit', '@change': "unitUpdated"}}) }} | |||
<div class="input-group"> | |||
{{ form_widget(advancedType.quantity, {'attr' : {'v-model': 'quantity', '@change': "quantityUpdated"}}) }} | |||
<div class="input-group-append"> | |||
<span class="input-group-text">${ unitWording }</span> | |||
</div> | |||
</div> | |||
{{ macros.priceField(advancedType.buyingPrice, advancedType.buyingPriceWithTax, 'buyingPrice', 'by-piece') }} | |||
{{ macros.priceField(advancedType.buyingPriceByRefUnit, advancedType.buyingPriceByRefUnitWithTax, 'buyingPriceByRefUnit', 'by-reference-unit') }} | |||
<div class="input-group"> | |||
{{ form_widget(advancedType.multiplyingFactor, {'attr': {'v-model':'multiplyingFactor', '@change' : 'multiplyingFactorUpdated'}}) }} | |||
<div class="input-group-append"> | |||
<span class="input-group-text">X</span> | |||
</div> | |||
</div> | |||
{{ macros.priceField(advancedType.price, advancedType.priceWithTax, 'price', 'by-piece') }} | |||
{{ macros.priceField(advancedType.priceByRefUnit, advancedType.priceByRefUnitWithTax, 'priceByRefUnit', 'by-reference-unit') }} | |||
</div> | |||
</product-unit-price> | |||
<table class="table datagrid sortable lc-sortable-products products-collection-table" | |||
:data-index="formProducts.length" | |||
data-prototype="{{ product_family_macros.product_row(advancedType.products.vars.prototype)|e('html_attr') }}"> | |||
<thead> | |||
<tr> | |||
<th> | |||
</th> | |||
<th colspan="4" class="string"> | |||
Titre | |||
</th> | |||
<th colspan="2" class="string "> | |||
Quantité | |||
</th> | |||
<th colspan="2" class="quantity"> | |||
Unité | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class="buyingPriceByRefUnit"> | |||
PA HT / ${ getUnitReference() } | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class="priceByRefUnit"> | |||
PA TTC / ${ getUnitReference() } | |||
</th> | |||
<th colspan="3" class="price" | |||
v-show="getBehaviorPrice() =='{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'"> | |||
PA HT | |||
</th> | |||
<th colspan="3" class="price"> | |||
PA TTC | |||
</th> | |||
<th colspan="3" class=""> | |||
Coef | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class=""> | |||
PV HT / ${ getUnitReference() } | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class="price"> | |||
PV TTC / ${ getUnitReference() } | |||
</th> | |||
<th colspan="3" class="price" | |||
v-show="getBehaviorPrice() =='{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'"> | |||
PV HT | |||
</th> | |||
<th colspan="3" class="price"> | |||
PV TTC | |||
</th> | |||
<th colspan="2"> | |||
Marge HT | |||
</th> | |||
<th colspan="2" class="" | |||
v-show="behaviorExpirationDate== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_EXPIRATION_DATE_BY_PRODUCT') }}'"> | |||
<span style="text-transform: uppercase"> ${typeExpirationDate}</span> | |||
</th> | |||
<th colspan="2" | |||
v-show="behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT') }}'"> | |||
Stock | |||
</th> | |||
<th colspan="2" | |||
v-show="behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT') }}' && behaviorStockWeek!= '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_STOCK_WEEK_NON_RENEWABLE') }}'"> | |||
Stock par défaut | |||
</th> | |||
<th colspan="2" | |||
v-show="behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT') }}' || behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_MEASURE') }}'"> | |||
Semaine / Commandés | |||
</th> | |||
<th colspan="2" class=""> | |||
Action | |||
</th> | |||
</tr> | |||
</thead> | |||
<tbody class="products-collection"> | |||
<template> | |||
<product-form v-for="(formProduct, blop) in formProducts" v-bind:key="formProductKey[blop]" | |||
:key-form="formProductKey[blop]" ref="productForm" v-bind:product-family="productFamily" | |||
:template="formProduct"></product-form> | |||
</template> | |||
</tbody> | |||
<tfoot> | |||
<th> | |||
Rappel | |||
</th> | |||
<th colspan="4" class="string"> | |||
${title} | |||
</th> | |||
<th colspan="2" class="string "> | |||
${productFamily.quantity} | |||
</th> | |||
<th colspan="2" class="quantity"> | |||
${productFamily.unitWording} | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class="buyingPriceByRefUnit"> | |||
${productFamily.buyingPriceByRefUnit} | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class="priceByRefUnit"> | |||
${productFamily.buyingPriceByRefUnitWithTax} | |||
</th> | |||
<th colspan="3" class="price" | |||
v-show="getBehaviorPrice() =='{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'"> | |||
${productFamily.buyingPrice} | |||
</th> | |||
<th colspan="3" class="price"> | |||
${productFamily.buyingPriceWithTax} | |||
</th> | |||
<th colspan="3" class=""> | |||
${productFamily.multiplyingFactor} | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class=""> | |||
${productFamily.priceByRefUnit} | |||
</th> | |||
<th v-show="getBehaviorPrice() == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'" | |||
colspan="3" class="price"> | |||
${productFamily.priceByRefUnitWithTax} | |||
</th> | |||
<th colspan="3" class="price" | |||
v-show="getBehaviorPrice() =='{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'"> | |||
${productFamily.price} | |||
</th> | |||
<th colspan="3" class="price"> | |||
${productFamily.priceWithTax} | |||
</th> | |||
<th colspan="2" class="price"> | |||
${productFamily.marginProfit}€<br/> | |||
${productFamily.marginProfitPercent}% | |||
</td> | |||
<th colspan="2" class="" | |||
v-show="behaviorExpirationDate== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_EXPIRATION_DATE_BY_PRODUCT') }}'"> | |||
${propertyExpirationDate} | |||
</th> | |||
<th colspan="2" | |||
v-show="behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT') }}'"> | |||
</th> | |||
<th colspan="2" | |||
v-show="behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT') }}' && behaviorStockWeek!= '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_STOCK_WEEK_NON_RENEWABLE') }}'"> | |||
</th> | |||
<th colspan="2" | |||
v-show="behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT') }}' || behaviorCountStock== '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_MEASURE') }}'"> | |||
</th> | |||
<th colspan="2" class=""> | |||
</th> | |||
</tfoot> | |||
</table> | |||
<button type="button" class="add_tag_link btn-add-product btn btn-default" @click="addProductForm"><span | |||
class="fa fa-plus"></span> Ajouter une déclinaison | |||
</button> | |||
<p> | |||
<strong>Aide à l'utilisation - Raccourci clavier</strong> | |||
<ul> | |||
<li><strong>TAB</strong> : Champ suivant</li> | |||
<li><strong>SHIFT + TAB</strong> : Champ précédent</li> | |||
<li><strong>FLÈCHE BAS</strong> : Déclinaison suivante</li> | |||
<li><strong>FLÈCHE HAUT</strong> : Déclinaison précédente</li> | |||
<li><strong>SHIFT + [+]</strong> : Ajout d'une nouvelle déclinaison</li> | |||
</ul> | |||
</p> | |||
<div class="clearfix"></div> | |||
{% do advancedType.products.setRendered %} | |||
<script> | |||
window.productForm[{{ i }}] ={}; | |||
window.formProductTemplate[{{ i }}] = {}; | |||
{% for keyForm,y in sortableProductsField[i] %} | |||
{% set product = advancedType.products[y] %} | |||
window.productForm[{{ i }}][{{ keyForm }}] = { | |||
{% if product.vars.value.position %}position: "{{ product.vars.value.position }}",{% endif %} | |||
{% if product.vars.value.title %}title: "{{ product.vars.value.title }}",{% endif %} | |||
{% if product.vars.value.quantity %}quantity: "{{ product.vars.value.quantity }}",{% endif %} | |||
{% if product.vars.value.unit %}unit: {{ product.vars.value.unit.id }},{% endif %} | |||
{% if product.vars.value.buyingPrice %}buyingPrice: parseFloat({{ product.vars.value.buyingPrice }}).toFixed(3),{% endif %} | |||
{% if product.vars.value.buyingPriceByRefUnit %}buyingPriceByRefUnit: parseFloat({{ product.vars.value.buyingPriceByRefUnit }}).toFixed(3),{% endif %} | |||
{% if product.vars.value.price %}price: parseFloat({{ product.vars.value.price }}).toFixed(3),{% endif %} | |||
{% if product.vars.value.priceByRefUnit %}priceByRefUnit: parseFloat({{ product.vars.value.priceByRefUnit }}).toFixed(3),{% endif %} | |||
{% if product.vars.value.availableQuantity %}availableQuantity: parseInt({{ product.vars.value.availableQuantity }}),{% endif %} | |||
{% if product.vars.value.availableQuantityDefault %}availableQuantityDefault: parseInt({{ product.vars.value.availableQuantityDefault }}),{% endif %} | |||
{% if product.vars.value.propertyExpirationDate %}propertyExpirationDate: "{{ product.vars.value.propertyExpirationDate }}",{% endif %} | |||
{#{% if product.vars.value.expirationDate %}expirationDate: "{{ product.vars.value.expirationDate|date('d/m/Y') }}"{% endif %}#} | |||
}; | |||
window.formProductTemplate[{{ i }}][{{ keyForm }}] = '{{ product_family_macros.product_row(product, totalProductOrdered[i][product.vars.value.id])|replace({"\n":' ', "\r":' ', "'" : "\\'"})|raw }}'; | |||
{% endfor %} | |||
</script> | |||
{{ form_end(advancedType) }} | |||
</div> | |||
</li> | |||
{% endfor %} | |||
</ul> | |||
{% endblock table %} | |||
{% block head_stylesheets %} | |||
{{ parent() }} | |||
{% endblock %} | |||
{% block plugin_javascript %} | |||
{{ parent() }} | |||
{% endblock %} | |||
{% block script_javascript %} | |||
{{ parent() }} | |||
{% include '@LcShop/backend/default/block/script-vuejs.html.twig' %} | |||
<script src="{{ asset('assets/js/backend/script/productfamily/vuejs-advanced-edition-product-family.js')|lc_cache }}"></script> | |||
{% endblock %} |
@@ -17,6 +17,9 @@ | |||
<script src="{{ asset('bundles/lcshop/js/backend/plugin/daterange/moment.min.js')}}"></script> | |||
<script src="{{ asset('bundles/lcshop/js/backend/plugin/daterange/daterangepicker.js')}}"></script> | |||
<script src="{{ asset('bundles/lcshop/js/backend/plugin/chartjs/Chart.min.js') }}"></script> | |||
{% endblock %} | |||
{% block script_javascript %} |
@@ -1,51 +1,65 @@ | |||
{% trans_default_domain 'lcshop' %} | |||
{% macro total_order_product(totalProductOrdered, productFamily=false) %} | |||
{% for weekNumber, weekNumberQuantity in totalProductOrdered %} | |||
{% macro product_sales_statistic(productsSalesStatistic, productFamily=false) %} | |||
{% for weekNumber, weekNumberQuantity in productsSalesStatistic %} | |||
<span class="text-success"><i class="fa fa-calendar"></i> {{ weekNumber }}</span> | |||
<span class="text-info"><i class="fa fa-shopping-basket"></i> | |||
<strong> | |||
{{ weekNumberQuantity is null ? 0 : weekNumberQuantity}} | |||
{{ weekNumberQuantity is null ? 0 : weekNumberQuantity }} | |||
</strong> | |||
</span> | |||
<br /> | |||
<br/> | |||
{% endfor %} | |||
{% endmacro total_order_product %} | |||
{% macro total_order_product_family(totalProductOrdered, productFamily,forceByMeasure = false) %} | |||
{% for weekNumber, weekNumberQuantity in totalProductOrdered %} | |||
<span class="text-success"><i class="fa fa-calendar"></i> {{ weekNumber }}</span> | |||
<span class="text-info"><i class="fa fa-shopping-basket"></i> | |||
{% endmacro product_sales_statistic %} | |||
{% macro product_family_sales_statistic(productsSalesStatistic, productFamily) %} | |||
{% if productsSalesStatistic %} | |||
<button type="button" data-product-family="{{ productFamily.id }}" | |||
class="lc-show-products-sales-statistic btn btn-sm" | |||
data-toggle="tooltip" title="{{ 'action.product.statSales'|trans }}" | |||
data-url="{{ path('easyadmin', { action: 'showSalesStatistic', entity: 'ProductFamily', id: productFamily.id }) }}"> | |||
{% for weekNumber, weekNumberQuantity in productsSalesStatistic['data']['total_sales']['data']|reverse(true) %} | |||
<span class="text-success"><i class="fa fa-calendar"></i> {{ weekNumber }}</span> | |||
<span class="text-info"><i class="fa fa-shopping-basket"></i> | |||
<strong> | |||
{{ weekNumberQuantity is null ? 0 : weekNumberQuantity}} | |||
{% if productFamily and (productFamily.behaviorCountStock== constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_MEASURE') or forceByMeasure)%} | |||
{{ weekNumberQuantity is null ? 0 : weekNumberQuantity }} | |||
{% if productFamily and (productFamily.behaviorDisplaySale== constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_DISPLAY_SALE_BY_MEASURE')) %} | |||
{{ productFamily.unit.unitReference }} | |||
{% endif %} | |||
</strong> | |||
</span> | |||
<br /> | |||
{% endfor %} | |||
{% endmacro total_order_product_family %} | |||
<br/> | |||
{% endfor %} | |||
</button> | |||
{% endif %} | |||
{% endmacro product_family_sales_statistic %} | |||
{% macro product_field(colspan, field, field_name, field_display = false, display_suffix="",attr="") %} | |||
{% if field_display == false %}{% set field_display = field_name %}{% endif %} | |||
<td {{ attr|raw }} colspan="{{ colspan }}" class="{{ field_name }}"> | |||
<div class="value" v-show="{{ field_name }}Inherited == false" @click="setFocusOnField('{{ field_name }}Inherited', keyForm)"> | |||
<div class="value" v-show="{{ field_name }}Inherited == false" | |||
@click="setFocusOnField('{{ field_name }}Inherited', keyForm)"> | |||
<div v-if="{{ field_name }}"> | |||
{% verbatim %}{{ {% endverbatim %}{{ field_display }} {% verbatim %}}}{% endverbatim %}{{ display_suffix }} | |||
{% if field_name == 'priceWithTax' %} | |||
<br /> | |||
<span class="text-danger" v-show="productFamily.reductionActive">{% verbatim %}{{ finalPriceWithTaxAndReduction }}{% endverbatim %}€</span> | |||
<br/> | |||
<span class="text-danger" | |||
v-show="productFamily.reductionActive">{% verbatim %}{{ finalPriceWithTaxAndReduction }}{% endverbatim %}€</span> | |||
{% endif %} | |||
</div> | |||
<div v-else class="inherited"> | |||
{% verbatim %}{{ productFamily.{% endverbatim %}{{ field_display }} {% verbatim %}}}{% endverbatim %}{{ display_suffix }} | |||
{% if field_name == 'priceWithTax' %} | |||
<br /> | |||
<span class="text-danger" v-show="productFamily.reductionActive">{% verbatim %}{{ finalPriceWithTaxAndReduction }}{% endverbatim %}€</span> | |||
<br/> | |||
<span class="text-danger" | |||
v-show="productFamily.reductionActive">{% verbatim %}{{ finalPriceWithTaxAndReduction }}{% endverbatim %}€</span> | |||
{% endif %} | |||
</div> | |||
@@ -58,7 +72,7 @@ | |||
</td> | |||
{% endmacro %} | |||
{% macro product_row(product, totalProductOrdered) %} | |||
{% macro product_row(product, productsSalesStatistic) %} | |||
<tr class="lc-draggable" v-show="originProduct != true && status >= 0 "> | |||
<td> | |||
@@ -76,7 +90,8 @@ | |||
{{ _self.product_field(3, product.buyingPrice, 'buyingPrice',false, '€', 'v-show="productFamily.behaviorPrice == \'' ~ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') ~ '\'"') }} | |||
{{ _self.product_field(3, product.buyingPriceWithTax, 'buyingPriceWithTax',false, '€', 'v-show="productFamily.behaviorPrice == \'' ~ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') ~ '\'"') }} | |||
<td class="buyingPrice" colspan="3" v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'"> | |||
<td class="buyingPrice" colspan="3" | |||
v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'"> | |||
{% verbatim %}{{ finalBuyingPrice }}{% endverbatim %}€ | |||
</td> | |||
@@ -85,12 +100,15 @@ | |||
{{ _self.product_field(3, product.priceByRefUnitWithTax, 'priceByRefUnitWithTax',false, '€', 'v-show="productFamily.behaviorPrice == \'' ~ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') ~ '\'"') }} | |||
<td class="price" colspan="3" v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'"> | |||
<td class="price" colspan="3" | |||
v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'"> | |||
{% verbatim %}{{ finalPrice }}{% endverbatim %}€ | |||
</td> | |||
<td class="priceWithTax" colspan="3" v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'"> | |||
<td class="priceWithTax" colspan="3" | |||
v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_REFERENCE_UNIT') }}'"> | |||
{% verbatim %}{{ finalPriceWithTax }}{% endverbatim %}€ | |||
<span class="text-danger" v-show="productFamily.reductionActive">{% verbatim %}{{ finalPriceWithTaxAndReduction }}{% endverbatim %}€</span> | |||
<span class="text-danger" | |||
v-show="productFamily.reductionActive">{% verbatim %}{{ finalPriceWithTaxAndReduction }}{% endverbatim %}€</span> | |||
</td> | |||
{{ _self.product_field(3, product.price, 'price', false, '€', 'v-show="productFamily.behaviorPrice == \'' ~ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') ~ '\'"') }} | |||
@@ -98,11 +116,11 @@ | |||
<td colspan="2" v-show="productFamily.giftVoucherActive!= true"> | |||
<span v-if="productFamily.reductionActive" class="text-danger"> | |||
{% verbatim %}{{ marginProfitWithReduction }}{% endverbatim %}€<br /> | |||
{% verbatim %}{{ marginProfitWithReduction }}{% endverbatim %}€<br/> | |||
{% verbatim %}{{ marginProfitPercentWithReduction }}{% endverbatim %}% | |||
</span> | |||
<span v-else> | |||
{% verbatim %}{{ marginProfit }}{% endverbatim %}€<br /> | |||
{% verbatim %}{{ marginProfit }}{% endverbatim %}€<br/> | |||
{% verbatim %}{{ marginProfitPercent }}{% endverbatim %}% | |||
</span> | |||
</td> | |||
@@ -113,19 +131,19 @@ | |||
{{ _self.product_field(2, product.availableQuantityDefault, 'availableQuantityDefault',false, '', 'v-show="productFamily.behaviorCountStock== \'' ~ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT') ~ '\' && productFamily.behaviorStockWeek!= \'' ~ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_STOCK_WEEK_NON_RENEWABLE') ~ '\'"') }} | |||
<td colspan="2"> | |||
<td colspan="2"> | |||
{{ _self.total_order_product(totalProductOrdered) }} | |||
{{ _self.product_sales_statistic(productsSalesStatistic) }} | |||
</td> | |||
{# <td colspan="4" v-show="productFamily.giftVoucherActive== true"> | |||
{# <td colspan="4" v-show="productFamily.giftVoucherActive== true"> | |||
{{ form_row(product.giftVoucherReductionCart, {"label": false, "attr" : {'v-model' : 'giftVoucherReductionCart', ':required': 'status ==1 && productFamily.giftVoucherActive'}}) }} | |||
</td>#} | |||
<td colspan="3"> | |||
<button type="button" class="btn-sm btn-info" @click="modalProductForm()"> | |||
</td> #} | |||
<td colspan="3"> | |||
<button type="button" :class="hasExportInfo ? 'btn-sm btn-info' : 'btn-sm btn-secondary'" @click="modalProductForm()"> | |||
<i class="fa fa-edit"></i> | |||
</button> | |||
<button type="button" class="btn-sm btn-info" @click="changeStatus()"> | |||
@@ -149,10 +167,10 @@ | |||
</div> | |||
<div class="modal-body"> | |||
<div class="col"> | |||
{{ form_row(product.exportTitle) }} | |||
{{ form_row(product.exportTitle, {'attr' : {"v-model" : 'exportTitle'}}) }} | |||
</div> | |||
<div class="col"> | |||
{{ form_row(product.exportNote) }} | |||
{{ form_row(product.exportNote, {'attr' : {"v-model" : 'exportNote'}}) }} | |||
</div> | |||
@@ -8,8 +8,11 @@ | |||
{{ form_row(form.status) }} | |||
</div> | |||
<div class="col-6"> | |||
<label>Status de vente</label> | |||
<label>Statut de vente</label> | |||
{{ form_row(form.saleStatus) }} | |||
<label>Éligible ticket restaurant</label> | |||
{{ form_row(form.isEligibleTicketRestaurant) }} | |||
</div> | |||
<div class="col-12"> | |||
@@ -25,10 +28,10 @@ | |||
{% endfor %} | |||
{% do form.sections.setRendered %} | |||
</div> | |||
{{ macros.endCard() }} | |||
{{ macros.endCard(true) }} | |||
{{ macros.startCard(8, 'ProductFamily.main','light') }} | |||
{{ macros.startCard(0, 'ProductFamily.main','light') }} | |||
<div class="col-12"> | |||
{{ form_row(form.supplier) }} | |||
</div> |
@@ -6,14 +6,14 @@ | |||
<div class="row"> | |||
{{ macros.startCard(12, 'ProductFamily.products', 'light', true) }} | |||
<table class="table datagrid sortable lc-sortable-products products-collection-table" | |||
<table class="table datagrid sortable lc-sortable-products products-collection-table" style="margin-bottom: 20px;" | |||
:data-index="formProducts.length" | |||
data-prototype="{{ product_family_macros.product_row(form.products.vars.prototype)|e('html_attr') }}"> | |||
<thead> | |||
<tr> | |||
<th> | |||
</th> | |||
<th colspan="4" class="string" v-show="productsQuantityAsTitle == false"> | |||
<th colspan="4" class="string" v-show="productsQuantityAsTitle == false"> | |||
Titre | |||
<button v-on:click="emptyProductsField('title');" | |||
class="btn btn-empty-field" type="button"><i class="fa fa-undo"></i></button> | |||
@@ -43,7 +43,8 @@ | |||
<th colspan="3" class="price main-info"> | |||
PA HT | |||
<button v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'" v-on:click="emptyProductsField('buyingPrice');" | |||
<button v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'" | |||
v-on:click="emptyProductsField('buyingPrice');" | |||
class="btn btn-empty-field" type="button"><i class="fa fa-undo"></i></button> | |||
</th> | |||
<th colspan="3" class="price" | |||
@@ -75,15 +76,17 @@ | |||
<th colspan="3" class="price"> | |||
{# v-show="getBehaviorPrice() =='{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'"> #} | |||
PV HT | |||
<button v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'" v-on:click="emptyProductsField('price');" | |||
<button v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'" | |||
v-on:click="emptyProductsField('price');" | |||
class="btn btn-empty-field" type="button"><i class="fa fa-undo"></i></button> | |||
</th> | |||
<th colspan="3" class="price main-info"> | |||
PV TTC | |||
<button v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'" v-on:click="emptyProductsField('priceWithTax');" | |||
<button v-show="productFamily.behaviorPrice == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_PRICE_BY_PIECE') }}'" | |||
v-on:click="emptyProductsField('priceWithTax');" | |||
class="btn btn-empty-field" type="button"><i class="fa fa-undo"></i></button> | |||
</th> | |||
<th colspan="2" v-show="giftVoucherActive!= true"> | |||
<th colspan="2" v-show="giftVoucherActive!= true"> | |||
Marge HT | |||
</th> | |||
@@ -108,10 +111,10 @@ | |||
<th colspan="2"> | |||
Semaine / Commandés | |||
</th> | |||
{#<th colspan="4" | |||
{# <th colspan="4" | |||
v-show="giftVoucherActive== true"> | |||
Réduction "Bon cadeaux" | |||
</th>#} | |||
</th> #} | |||
<th colspan="3" class=""> | |||
Action | |||
</th> | |||
@@ -176,7 +179,7 @@ | |||
<th colspan="3" class="price main-info"> | |||
${productFamily.priceWithTax} | |||
</th> | |||
<th colspan="2" class="price" v-show="giftVoucherActive!= true"> | |||
<th colspan="2" class="price" v-show="giftVoucherActive!= true"> | |||
${productFamily.marginProfit}€<br/> | |||
${productFamily.marginProfitPercent}% | |||
</td> | |||
@@ -194,9 +197,9 @@ | |||
</th> | |||
<th colspan="2"> | |||
</th> | |||
{#<th colspan="4" | |||
{# <th colspan="4" | |||
v-show="giftVoucherActive== true"> | |||
</th>#} | |||
</th> #} | |||
<th colspan="3" class=""> | |||
</th> | |||
@@ -204,12 +207,20 @@ | |||
</table> | |||
<div class="col-12"> | |||
<div class="row" style="margin: 0 25px"> | |||
<div class="col-3 form-group"> | |||
{{ form_widget(form.productsQuantityAsTitle, {"attr":{'v-model' : 'productsQuantityAsTitle'}}) }} | |||
</div> | |||
<div class="col-3 form-group"></div> | |||
<div class="col-6 form-group float-right"> | |||
<button type="button" class="add_tag_link btn-add-product btn btn-default" @click="addProductForm"><span | |||
class="fa fa-plus"></span> Ajouter une déclinaison | |||
</button> | |||
</div> | |||
<div class="col-12"> | |||
{{ form_row(form.productsQuantityAsTitle, {"attr":{'v-model' : 'productsQuantityAsTitle'}}) }} | |||
<p> | |||
<strong>Aide à l'utilisation - Raccourci clavier</strong> | |||
<ul> | |||
@@ -221,8 +232,9 @@ | |||
</ul> | |||
</p> | |||
</div> | |||
</div> | |||
<div class="clearfix"></div> | |||
<div class="clearfix"></div> | |||
{{ macros.endCard() }} | |||
</div> | |||
@@ -249,10 +261,12 @@ | |||
{% if product.vars.value.availableQuantity %}availableQuantity: parseInt({{ product.vars.value.availableQuantity }}),{% endif %} | |||
{% if product.vars.value.availableQuantityDefault %}availableQuantityDefault: parseInt({{ product.vars.value.availableQuantityDefault }}),{% endif %} | |||
{% if product.vars.value.propertyExpirationDate %}propertyExpirationDate: "{{ product.vars.value.propertyExpirationDate }}",{% endif %} | |||
{#{% if product.vars.value.giftVoucherReductionCart %}giftVoucherReductionCart: {{ product.vars.value.giftVoucherReductionCart.id }},{% endif %}#} | |||
{% if product.vars.value.exportTitle %}exportTitle: "{{ product.vars.value.exportTitle }}",{% endif %} | |||
{% if product.vars.value.exportNote %}exportNote: "{{ product.vars.value.exportNote }}",{% endif %} | |||
{# {% if product.vars.value.giftVoucherReductionCart %}giftVoucherReductionCart: {{ product.vars.value.giftVoucherReductionCart.id }},{% endif %} #} | |||
{# {% if product.vars.value.expirationDate %}expirationDate: "{{ product.vars.value.expirationDate|date('d/m/Y') }}"{% endif %} #} | |||
}; | |||
window.formProductTemplate[{{ keyForm }}] = '{{ product_family_macros.product_row(product, totalProductOrdered[product.vars.value.id])|replace({"\n":' ', "\r":' ', "'" : "\\'"})|raw }}'; | |||
window.formProductTemplate[{{ keyForm }}] = '{{ product_family_macros.product_row(product, productsSalesStatistic['data'][product.vars.value.id]['data'])|replace({"\n":' ', "\r":' ', "'" : "\\'"})|raw }}'; | |||
{# {% endif %} #} | |||
{% endfor %} | |||
@@ -33,6 +33,8 @@ | |||
{% endif %} | |||
{% endfor %} | |||
{{ form_row(form.behaviorDisplaySale) }} | |||
</div> | |||
<div class="col"> | |||
<div v-show="behaviorCountStock == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT_FAMILY') }}' || behaviorCountStock == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_MEASURE') }}'" | |||
@@ -62,12 +64,22 @@ | |||
</div> | |||
</div> | |||
<p v-show="behaviorCountStock == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_PRODUCT_FAMILY') }}' || behaviorCountStock == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_BY_MEASURE') }}' || behaviorCountStock == '{{ constant('Lc\\ShopBundle\\Model\\ProductFamily::BEHAVIOR_COUNT_STOCK_UNLIMITED') }}' "> | |||
{{ product_family_macros.total_order_product_family(totalProductOrdered['total'], entity) }} | |||
{{ product_family_macros.product_family_sales_statistic(productsSalesStatistic, entity) }} | |||
</p> | |||
</div> | |||
{{ macros.endCard(true) }} | |||
{{ macros.startCard(0, 'ProductFamily.export', 'light') }} | |||
<div class="col"> | |||
{{ form_row(form.exportTitle) }} | |||
{{ form_row(form.exportNote) }} | |||
</div> | |||
{{ macros.endCard() }} | |||
{{ macros.startCard(4, 'ProductFamily.storage', 'light') }} | |||
<div class="col"> | |||
{{ form_row(form.depositoryZone) }} | |||
@@ -75,16 +87,17 @@ | |||
</div> | |||
{{ macros.endCard() }} | |||
{{ macros.endCard(true) }} | |||
{{ macros.startCard(8, 'ProductFamily.export', 'light') }} | |||
{{ macros.startCard(0, 'ProductFamily.purchaseOrder', 'light') }} | |||
<div class="col"> | |||
{{ form_row(form.exportTitle) }} | |||
{{ form_row(form.exportNote) }} | |||
{{ form_row(form.isDisabledOnPurchaseOrder) }} | |||
</div> | |||
{{ macros.endCard() }} | |||
</div> |
@@ -60,7 +60,13 @@ | |||
</div> | |||
{% endif %} | |||
<div class="direct-chat-text"> | |||
{{ message.message|nl2br }} | |||
<p>{{ message.message|nl2br }}</p> | |||
{% if message.imageFilename is not null %} | |||
<i>Photo jointe au message : </i> <br /> | |||
<a href="{{ lc_liip(message.imageFilename, 'big') }}" data-toggle="lightbox"> | |||
<img src="{{ lc_liip(message.imageFilename, 'thumb') }}" alt="Illustration ticket" /> | |||
</a> | |||
{% endif %} | |||
</div> | |||
</div> | |||
{% endfor %} | |||
@@ -85,6 +91,16 @@ | |||
{% endblock %} | |||
{% block head_stylesheets %} | |||
{{ parent() }} | |||
<link rel="stylesheet" | |||
href="{{ asset('bundles/lcshop/css/backend/ekko-lightbox/ekko-ligthbox.min.css') }}"> | |||
{% endblock head_stylesheets %} | |||
{% block plugin_javascript %} | |||
{{ parent() }} | |||
<script src="{{ asset('bundles/lcshop/js/backend/plugin/ekko-lightbox/ekko-lightbox.min.js')|lc_cache }}"></script> | |||
{% endblock plugin_javascript %} | |||
{% block script_javascript %} | |||
{{ parent() }} | |||
<script src="{{ asset('bundles/lcshop/js/backend/script/ticket/init-edit.js')|lc_cache }}"></script> |
@@ -93,6 +93,40 @@ | |||
{% endembed %} | |||
{% endmacro box_total_spent %} | |||
{% macro box_prepaid_account(user) %} | |||
{% embed '@LcShop/backend/default/block/embed_figure_box.twig' %} | |||
{% set userMerchant = creditUtils.getUserMerchant() %} | |||
{% trans_default_domain 'lcshop' %} | |||
{% block class %}bg-blue{% endblock %} | |||
{% block icon %}cash-register{% endblock %} | |||
{% block label %} | |||
<a class="text-white" href="{{ path('easyadmin', {"action" : 'show', 'entity': 'UserMerchant', 'id': userMerchant.id}) }}"> | |||
<i class="fa fa-pen"></i> Éditer | |||
</a> - Solde compte prépayé | |||
{% endblock %} | |||
{% block value %} | |||
{{ userMerchant.getCredit()|format_price(false) }} | |||
{% if userMerchant.getCreditActive() ==false %} | |||
<small>(inactif)</small> | |||
{% endif %} | |||
{% endblock %} | |||
{% endembed %} | |||
{% endmacro box_prepaid_account %} | |||
{% macro box_average_products(user) %} | |||
{% embed '@LcShop/backend/default/block/embed_figure_box.twig' %} | |||
{% trans_default_domain 'lcshop' %} | |||
{% block class %}bg-navy{% endblock %} | |||
{% block icon %}shopping-basket{% endblock %} | |||
{% block label %} Panier moyen{% endblock %} | |||
{% block value %} | |||
{{ orderUtils.getAverageProductsByUser(user)|format_price|raw }} | |||
{% endblock %} | |||
{% endembed %} | |||
{% endmacro box_average_products %} | |||
{% macro box_rank_total_order(user) %} | |||
{% embed '@LcShop/backend/default/block/embed_figure_box.twig' %} | |||
{% block class %}bg-info{% endblock %} |
@@ -6,7 +6,7 @@ | |||
{% block global_actions %} | |||
{% if entity is not null %} | |||
{% set action = {label : "action.user.switch", icon: 'user-secret', css_class: 'btn btn-sm btn-danger'} %} | |||
{% include '@LcShop/backend/user/block/user-switch.html.twig' with {item: entity, is_dropdown: false, action: action, translation_domain: 'lcshop', trans_parameters: {}, item_id: entity.id}%} | |||
{% include '@LcShop/backend/user/block/user-switch.html.twig' with {item: entity, is_dropdown: false, action: action, translation_domain: 'lcshop', trans_parameters: {}, item_id: entity.id} %} | |||
<button id="btn-ticket-write-to-user" | |||
data-url="{{ path('easyadmin', {'entity': 'Ticket', 'action': 'new'}) }}" | |||
@@ -24,17 +24,23 @@ | |||
<div class="col-3"> | |||
{{ user_macros.card_info(entity) }} | |||
</div> | |||
<div class="col-3"> | |||
{{ user_macros.box_rank_total_order(entity) }} | |||
{{ user_macros.box_total_order(entity) }} | |||
</div> | |||
<div class="col-3"> | |||
{{ user_macros.box_rank_sum_order(entity) }} | |||
{{ user_macros.box_total_spent(entity) }} | |||
</div> | |||
<div class="col-3"> | |||
{{ user_macros.box_register_since(entity) }} | |||
{{ user_macros.box_login_since(entity) }} | |||
<div class="col-9 row"> | |||
<div class="col-3"> | |||
{{ user_macros.box_rank_total_order(entity) }} | |||
{{ user_macros.box_total_order(entity) }} | |||
</div> | |||
<div class="col-3"> | |||
{{ user_macros.box_rank_sum_order(entity) }} | |||
{{ user_macros.box_total_spent(entity) }} | |||
</div> | |||
<div class="col-3"> | |||
{{ user_macros.box_prepaid_account(entity) }} | |||
{{ user_macros.box_average_products(entity) }} | |||
</div> | |||
<div class="col-3"> | |||
{{ user_macros.box_register_since(entity) }} | |||
{{ user_macros.box_login_since(entity) }} | |||
</div> | |||
</div> | |||
<div class="col-6"> | |||
{% set redeliveries = orderUtils.getRedeliveryByUser(entity) %} |
@@ -31,8 +31,12 @@ class MailUtils | |||
protected $parameterBag; | |||
protected $merchantUtils; | |||
public function __construct(MailjetTransport $mailjetTransport, Environment $templating, ParameterBagInterface $parameterBag, MerchantUtilsInterface $merchantUtils) | |||
{ | |||
public function __construct( | |||
MailjetTransport $mailjetTransport, | |||
Environment $templating, | |||
ParameterBagInterface $parameterBag, | |||
MerchantUtilsInterface $merchantUtils | |||
) { | |||
$this->transport = $mailjetTransport; | |||
$this->templating = $templating; | |||
$this->parameterBag = $parameterBag; | |||
@@ -105,6 +109,6 @@ class MailUtils | |||
$message->getHeaders()->addMailboxHeader('Disposition-Notification-To', $emailFromDispositionNotificationTo); | |||
}*/ | |||
$this->transport->send($message); | |||
return $this->transport->send($message); | |||
} | |||
} |
@@ -5,28 +5,55 @@ namespace Lc\ShopBundle\Services; | |||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | |||
use Symfony\Component\HttpFoundation\ParameterBag; | |||
use Symfony\Contracts\HttpClient\HttpClientInterface; | |||
use Twig\Environment; | |||
class MailjetSMS | |||
class MailjetSmsUtils | |||
{ | |||
const TO_USER = 'to-user' ; | |||
const CONTENT_MESSAGE = 'content-message' ; | |||
const CONTENT_TEMPLATE = 'content-template' ; | |||
const CONTENT_DATA = 'content-data' ; | |||
protected $client; | |||
protected $parameterBag; | |||
protected $mailUtils ; | |||
protected $utils ; | |||
protected $templating ; | |||
public function __construct(HttpClientInterface $client, ParameterBagInterface $parameterBag, UtilsManager $utilsManager) | |||
{ | |||
public function __construct( | |||
HttpClientInterface $client, | |||
ParameterBagInterface $parameterBag, | |||
MailUtils $mailUtils, | |||
Utils $utils, | |||
Environment $templating | |||
) { | |||
$this->client = $client; | |||
$this->parameterBag = $parameterBag; | |||
$this->mailUtils = $utilsManager->getMailUtils() ; | |||
$this->utils = $utilsManager->getUtils() ; | |||
$this->mailUtils = $mailUtils ; | |||
$this->utils = $utils ; | |||
$this->templating = $templating ; | |||
} | |||
public function send($user, $message) | |||
public function send($params = []) | |||
{ | |||
$user = isset($params[self::TO_USER]) ? $params[self::TO_USER] : null ; | |||
if($user) { | |||
$phone = $this->utils->formatPhoneNumber($user->getPhone()) ; | |||
$message = '' ; | |||
if(isset($params[self::CONTENT_MESSAGE])) { | |||
$message = $params[self::CONTENT_MESSAGE] ; | |||
} | |||
elseif(isset($params[self::CONTENT_TEMPLATE])) { | |||
$template = $params[self::CONTENT_TEMPLATE] ; | |||
$paramsTemplate = [] ; | |||
if(isset($params[self::CONTENT_DATA]) && is_array($params[self::CONTENT_DATA])) { | |||
$paramsTemplate = $params[self::CONTENT_DATA] ; | |||
} | |||
$message = $this->templating->render($template, $paramsTemplate) ; | |||
} | |||
if($this->parameterBag->get('mailjet.dev.redirect.active') == 1) { | |||
$this->mailUtils->send([ | |||
MailUtils::SUBJECT => 'Notification par SMS à '.$phone, | |||
@@ -59,15 +86,7 @@ class MailjetSMS | |||
] | |||
); | |||
$statusCode = $response->getStatusCode(); | |||
if ($statusCode == 200) { | |||
$content = $response->getContent(); | |||
$content = $response->toArray(); | |||
return $content; | |||
} else { | |||
// log | |||
} | |||
return $response ; | |||
} | |||
else { | |||
throw new \ErrorException('Le token SMS Mailjet n\'est pas défini.'); |
@@ -4,7 +4,7 @@ namespace Lc\ShopBundle\Services\Order; | |||
use App\Entity\OrderProductReductionCatalog; | |||
use App\Entity\OrderShop; | |||
use Doctrine\ORM\EntityManagerInterface; | |||
use App\Entity\OrderStatus; | |||
use Lc\ShopBundle\Context\DocumentInterface; | |||
use Lc\ShopBundle\Context\MerchantUtilsInterface; | |||
use Lc\ShopBundle\Context\OrderReductionCreditInterface; | |||
@@ -16,6 +16,7 @@ use Lc\ShopBundle\Context\ReductionCreditInterface; | |||
use Lc\ShopBundle\Context\SectionInterface; | |||
use Lc\ShopBundle\Context\SectionUtilsInterface; | |||
use Lc\ShopBundle\Context\UserUtilsInterface; | |||
use Lc\ShopBundle\Manager\EntityManager; | |||
use Lc\ShopBundle\Model\ProductFamily; | |||
use Lc\ShopBundle\Services\CreditUtils; | |||
use Lc\ShopBundle\Services\DocumentUtils; | |||
@@ -50,18 +51,28 @@ class OrderUtils | |||
protected $router; | |||
protected $sectionUtils ; | |||
public function __construct(EntityManagerInterface $em, Security $security, RouterInterface $router, UserUtilsInterface $userUtils, | |||
MerchantUtilsInterface $merchantUtils, PriceUtilsInterface $priceUtils, ProductFamilyUtilsInterface $productFamilyUtils, | |||
DocumentUtils $documentUtils, Utils $utils, CreditUtils $creditUtils, SectionUtilsInterface $sectionUtils) | |||
public function __construct( | |||
EntityManager $em, | |||
Security $security, | |||
RouterInterface $router, | |||
UserUtilsInterface $userUtils, | |||
MerchantUtilsInterface $merchantUtils, | |||
PriceUtilsInterface $priceUtils, | |||
ProductFamilyUtilsInterface $productFamilyUtils, | |||
DocumentUtils $documentUtils, | |||
Utils $utils, | |||
CreditUtils $creditUtils, | |||
SectionUtilsInterface $sectionUtils | |||
) | |||
{ | |||
$this->em = $em; | |||
$this->security = $security; | |||
$this->userUtils = $userUtils; | |||
$this->merchantUtils = $merchantUtils; | |||
$this->orderShopRepo = $this->em->getRepository($this->em->getClassMetadata(OrderShopInterface::class)->getName()); | |||
$this->reductionCreditRepo = $this->em->getRepository($this->em->getClassMetadata(ReductionCreditInterface::class)->getName()); | |||
$this->orderReductionCreditRepo = $this->em->getRepository($this->em->getClassMetadata(OrderReductionCreditInterface::class)->getName()); | |||
$this->documentRepo = $this->em->getRepository($this->em->getClassMetadata(DocumentInterface::class)->getName()); | |||
$this->orderShopRepo = $this->em->getRepository($this->em->getEntityName(OrderShopInterface::class)); | |||
$this->reductionCreditRepo = $this->em->getRepository($this->em->getEntityName(ReductionCreditInterface::class)); | |||
$this->orderReductionCreditRepo = $this->em->getRepository($this->em->getEntityName(OrderReductionCreditInterface::class)); | |||
$this->documentRepo = $this->em->getRepository($this->em->getEntityName(DocumentInterface::class)); | |||
$this->priceUtils = $priceUtils; | |||
$this->productFamilyUtils = $productFamilyUtils; | |||
$this->documentUtils = $documentUtils; | |||
@@ -257,6 +268,7 @@ class OrderUtils | |||
$data['count'] = $this->countQuantities($order); | |||
$data['total_with_tax'] = $this->priceUtils->getTotalWithTax($order); | |||
$data['order_products_by_category'] = $this->getOrderProductsByParentCategory($order); | |||
$data['total_remaining_to_be_paid'] = $this->getTotalRemainingToBePaid($order) ; | |||
} | |||
return $data; | |||
} | |||
@@ -296,8 +308,8 @@ class OrderUtils | |||
public function newOrderStatusHistory($order, $status, $origin = 'user') | |||
{ | |||
$orderStatusHistoryClass = $this->em->getClassMetadata(OrderStatusHistoryInterface::class); | |||
$orderStatusHistory = new $orderStatusHistoryClass->name; | |||
$orderStatusHistoryClass = $this->em->getEntityName(OrderStatusHistoryInterface::class); | |||
$orderStatusHistory = new $orderStatusHistoryClass; | |||
$orderStatusHistory->setOrderShop($order); | |||
$orderStatusHistory->setOrderStatus($status); | |||
$orderStatusHistory->setOrigin($origin); | |||
@@ -395,5 +407,30 @@ class OrderUtils | |||
} | |||
public function addPayment($orderShop, $meanPayment, $amount) | |||
{ | |||
$this->createOrderPayment([ | |||
'orderShop' => $orderShop, | |||
'meanPayment' => $meanPayment, | |||
'amount' => $amount | |||
]) ; | |||
$this->em->refresh($orderShop) ; | |||
if($this->isOrderPaid($orderShop)) { | |||
$nextStatus = OrderStatus::ALIAS_PAID ; | |||
} | |||
else { | |||
$nextStatus = OrderStatus::ALIAS_PARTIAL_PAYMENT ; | |||
} | |||
if($orderShop->getOrderStatus()->getAlias() != $nextStatus) { | |||
$orderShop = $this->changeOrderStatus( | |||
$nextStatus, | |||
$orderShop | |||
); | |||
} | |||
return $orderShop ; | |||
} | |||
} |
@@ -3,46 +3,44 @@ | |||
namespace Lc\ShopBundle\Services\Order; | |||
use App\Entity\CreditHistory; | |||
use App\Factory\Order\OrderPaymentFactory; | |||
use Lc\ShopBundle\Context\OrderPaymentInterface; | |||
use Lc\ShopBundle\Services\Utils; | |||
trait OrderUtilsPaymentTrait | |||
{ | |||
public function createOrderPayment($orderShop, $meanPayment, $amount, $reference = null, $comment = null, $paidAt = null) | |||
public function createOrderPayment($params = []) | |||
{ | |||
$classOrderPayment = $this->em->getClassMetadata(OrderPaymentInterface::class)->getName(); | |||
$orderPayment = new $classOrderPayment; | |||
$orderPayment->setOrderShop($orderShop); | |||
$orderPayment->setMeanPayment($meanPayment); | |||
$orderPayment->setAmount($amount); | |||
$orderPayment->setReference($reference); | |||
$orderPayment->setComment($comment); | |||
$orderPayment->setEditable(false); | |||
$orderPayment->setCreatedBy($orderShop->getUser()); | |||
$orderPayment->setUpdatedBy($orderShop->getUser()); | |||
if ($paidAt) { | |||
$orderPayment->setPaidAt($paidAt); | |||
} else { | |||
$orderPayment->setPaidAt(new \DateTime('now')); | |||
$orderPayment = $this->factoryManager->createEntity(OrderPaymentFactory::class, $params); | |||
if(isset($params['meanPayment']) && $params['meanPayment'] == Utils::MEAN_PAYMENT_CREDIT) { | |||
$this->creditUtils->createCreditHistory(CreditHistory::TYPE_DEBIT, $this->security->getUser(), [ | |||
'orderPayment' => $orderPayment | |||
]); | |||
} | |||
$this->em->persist($orderPayment); | |||
$this->em->flush(); | |||
$this->em->create($orderPayment) ; | |||
$this->em->flush() ; | |||
return $orderPayment; | |||
return $orderPayment ; | |||
} | |||
public function isOrderPaid($order, $mergeComplementaryOrderShop = false) | |||
{ | |||
$totalOrderPayments = $this->getTotalOrderPayments($order, $mergeComplementaryOrderShop) ; | |||
$totalOrder = $this->priceUtils->getTotalWithTax($order) ; | |||
if ( (abs($totalOrderPayments - $totalOrder) < 0.00001 | |||
|| $totalOrderPayments >= $totalOrder) | |||
&& $totalOrder > 0) { | |||
if ($this->getTotalOrderPayments($order, $mergeComplementaryOrderShop) >= $this->priceUtils->getTotalWithTax($order) && $this->priceUtils->getTotalWithTax($order) > 0) { | |||
return true; | |||
} else { | |||
} | |||
else { | |||
return false; | |||
} | |||
} | |||
public function getTotalOrderPayments($order, $mergeComplementaryOrderShop = false): float | |||
@@ -61,4 +59,18 @@ trait OrderUtilsPaymentTrait | |||
return $totalAmount; | |||
} | |||
public function getTotalRemainingToBePaid($order) | |||
{ | |||
return $this->priceUtils->getTotalWithTax($order) - $this->getTotalOrderPayments($order) ; | |||
} | |||
public function isOrderShopPositiveAmountRemainingToBePaid($orderShop) | |||
{ | |||
return $this->getTotalRemainingToBePaid($orderShop) > 0 ; | |||
} | |||
public function addPayment() | |||
{ | |||
} | |||
} |
@@ -73,13 +73,18 @@ trait OrderUtilsReductionTrait | |||
$orderShop->addOrderReductionCart($orderReductionCart) ; | |||
if($this->isOrderShopPositiveAmount($orderShop)) { | |||
if($this->isOrderShopPositiveAmount($orderShop) | |||
&& $this->isOrderShopPositiveAmountRemainingToBePaid($orderShop)) { | |||
$this->em->persist($orderReductionCart); | |||
$this->em->flush(); | |||
return $orderReductionCart ; | |||
} | |||
return false; | |||
else { | |||
$orderShop->removeOrderReductionCart($orderReductionCart) ; | |||
return false; | |||
} | |||
} | |||
public function isReductionCreditAllowAddToOrder($orderShop, $reductionCredit) | |||
@@ -148,13 +153,19 @@ trait OrderUtilsReductionTrait | |||
$orderShop->addOrderReductionCredit($orderReductionCredit) ; | |||
if($this->isOrderShopPositiveAmount($orderShop)) { | |||
if($this->isOrderShopPositiveAmount($orderShop) | |||
&& $this->isOrderShopPositiveAmountRemainingToBePaid($orderShop)) { | |||
$this->em->persist($orderReductionCredit); | |||
$this->em->flush(); | |||
return $orderReductionCredit; | |||
} | |||
else { | |||
$orderShop->removeOrderReductionCredit($orderReductionCredit) ; | |||
return false ; | |||
return false; | |||
} | |||
} | |||
@@ -67,12 +67,14 @@ class ProductPriceUtils | |||
public function getPriceByRefUnitWithTaxAndReduction(ProductPropertyInterface $product) | |||
{ | |||
return $this->applyReductionCatalog( | |||
return ($this->getPriceByRefUnitWithTax($product) * $this->getPriceWithTaxAndReduction($product)) | |||
/ $this->getPriceWithTax($product) ; | |||
/*return $this->applyReductionCatalog( | |||
$product, | |||
$this->getPriceByRefUnit($product), | |||
$this->getPriceByRefUnitWithTax($product) | |||
); | |||
);*/ | |||
} | |||
@@ -1,6 +1,6 @@ | |||
<?php | |||
namespace Lc\ShopBundle\Services ; | |||
namespace Lc\ShopBundle\Services; | |||
use App\Entity\TicketMessage; | |||
use Doctrine\ORM\EntityManagerInterface; | |||
@@ -10,159 +10,210 @@ use Lc\ShopBundle\Context\TicketInterface; | |||
use Lc\ShopBundle\Context\TicketMessageInterface; | |||
use Lc\ShopBundle\Context\UserInterface; | |||
use Lc\ShopBundle\Model\Ticket; | |||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; | |||
use Symfony\Component\HttpFoundation\File\Exception\FileException; | |||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; | |||
class TicketUtils | |||
{ | |||
protected $em ; | |||
protected $merchantUtils ; | |||
protected $mailUtils ; | |||
protected $authorizationChecker ; | |||
public function __construct(EntityManagerInterface $em, MerchantUtilsInterface $merchantUtils, MailUtils $mailUtils, AuthorizationCheckerInterface $authorizationChecker) | |||
{ | |||
$this->em = $em ; | |||
$this->merchantUtils = $merchantUtils ; | |||
$this->mailUtils = $mailUtils ; | |||
$this->authorizationChecker = $authorizationChecker ; | |||
protected $em; | |||
protected $merchantUtils; | |||
protected $mailUtils; | |||
protected $authorizationChecker; | |||
protected $parameterBag; | |||
public function __construct( | |||
EntityManagerInterface $em, | |||
MerchantUtilsInterface $merchantUtils, | |||
MailUtils $mailUtils, | |||
AuthorizationCheckerInterface $authorizationChecker, | |||
ParameterBagInterface $parameterBag | |||
) { | |||
$this->em = $em; | |||
$this->merchantUtils = $merchantUtils; | |||
$this->mailUtils = $mailUtils; | |||
$this->authorizationChecker = $authorizationChecker; | |||
$this->parameterBag = $parameterBag; | |||
} | |||
public function getTicketsByUser($user) | |||
{ | |||
$ticketRepo = $this->em->getRepository(TicketInterface::class); | |||
return $ticketRepo->findBy(array('user' => $user)); | |||
} | |||
public function uploadImageTicketMessage($formTicket) | |||
{ | |||
$image = $formTicket->get('image')->getData(); | |||
if ($image) { | |||
$originalFilename = pathinfo($image->getClientOriginalName(), PATHINFO_FILENAME); | |||
$newFilename = uniqid().'.'.$image->guessExtension(); | |||
try { | |||
$image->move( | |||
$this->parameterBag->get('app.ticket_images_directory'), | |||
$newFilename | |||
); | |||
} catch (FileException $e) { | |||
throw new \ErrorException("Une erreur est survenue lors de l'upload du fichier."); | |||
} | |||
return $this->parameterBag->get('app.ticket_images_subdirectory').$newFilename; | |||
} | |||
public function getTicketsByUser($user){ | |||
$ticketRepo = $this->em->getRepository(TicketInterface::class); | |||
return $ticketRepo->findBy(array('user'=>$user)); | |||
return false; | |||
} | |||
public function createTicket($params): TicketInterface | |||
{ | |||
$classTicket = $this->em->getClassMetadata(TicketInterface::class)->getName(); | |||
$ticket = new $classTicket; | |||
$ticket->setMerchant($this->merchantUtils->getMerchantCurrent()); | |||
$ticket->setStatus(1); | |||
if (isset($params['user'])) { | |||
$ticket->setUser($params['user']); | |||
$email = $params['user']->getEmail(); | |||
$firstname = $params['user']->getFirstname(); | |||
} else { | |||
$ticket->setVisitorFirstname($params['visitorFirstname']); | |||
$ticket->setVisitorLastname($params['visitorLastname']); | |||
$ticket->setVisitorEmail($params['visitorEmail']); | |||
$ticket->setVisitorToken(uniqid()); | |||
$email = $params['visitorEmail']; | |||
$firstname = $params['visitorFirstname']; | |||
} | |||
public function createTicket($params): TicketInterface | |||
{ | |||
$classTicket = $this->em->getClassMetadata(TicketInterface::class)->getName() ; | |||
$ticket = new $classTicket ; | |||
$ticket->setMerchant($this->merchantUtils->getMerchantCurrent()) ; | |||
$ticket->setStatus(1) ; | |||
if(isset($params['user'])) { | |||
$ticket->setUser($params['user']) ; | |||
$email = $params['user']->getEmail() ; | |||
$firstname = $params['user']->getFirstname() ; | |||
} | |||
else { | |||
$ticket->setVisitorFirstname($params['visitorFirstname']) ; | |||
$ticket->setVisitorLastname($params['visitorLastname']) ; | |||
$ticket->setVisitorEmail($params['visitorEmail']) ; | |||
$ticket->setVisitorToken(uniqid()) ; | |||
$email = $params['visitorEmail'] ; | |||
$firstname = $params['visitorFirstname'] ; | |||
} | |||
$ticket->setStatus(Ticket::TICKET_STATUS_OPEN) ; | |||
$ticket->setType($params['type']) ; | |||
if(isset($params['orderShop']) && $params['orderShop'] && $params['orderShop'] instanceof OrderShopInterface) { | |||
$ticket->setOrderShop($params['orderShop']) ; | |||
} | |||
$ticket->setSubject($params['subject']) ; | |||
$this->em->persist($ticket); | |||
$classTicketMessage = $this->em->getClassMetadata(TicketMessageInterface::class)->getName() ; | |||
$ticketMessage = new $classTicketMessage ; | |||
$ticketMessage->setStatus(1) ; | |||
$ticketMessage->setTicket($ticket) ; | |||
$ticketMessage->setMessage($params['message']) ; | |||
if(isset($params['createByAdmin']) && $params['createByAdmin'])$ticketMessage->setAnswerByAdmin(true); | |||
$this->em->persist($ticketMessage); | |||
$this->em->flush() ; | |||
if(isset($params['createByAdmin']) && $params['createByAdmin']) { | |||
// envoi email au client | |||
$this->mailUtils->send([ | |||
MailUtils::SUBJECT => 'Vous avez reçu un nouveau message', | |||
MailUtils::TO_EMAIL => $email, | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-new-by-admin', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $firstname, | |||
'ticket' => $ticket | |||
], | |||
]); | |||
}else{ | |||
$this->mailUtils->send([ | |||
MailUtils::SUBJECT => 'Nouvelle demande', | |||
MailUtils::TO_EMAIL => $email, | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-new', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $firstname, | |||
'ticket' => $ticket | |||
], | |||
]); | |||
} | |||
$ticket->setStatus(Ticket::TICKET_STATUS_OPEN); | |||
$ticket->setType($params['type']); | |||
if (isset($params['orderShop']) && $params['orderShop'] && $params['orderShop'] instanceof OrderShopInterface) { | |||
$ticket->setOrderShop($params['orderShop']); | |||
} | |||
$ticket->setSubject($params['subject']); | |||
$this->em->persist($ticket); | |||
$this->notifyAdmin($ticket, $ticketMessage); | |||
$classTicketMessage = $this->em->getClassMetadata(TicketMessageInterface::class)->getName(); | |||
$ticketMessage = new $classTicketMessage; | |||
$ticketMessage->setStatus(1); | |||
$ticketMessage->setTicket($ticket); | |||
$ticketMessage->setMessage($params['message']); | |||
return $ticket ; | |||
if (isset($params['imageFilename']) && $params['imageFilename']) { | |||
$ticketMessage->setImageFilename($params['imageFilename']); | |||
} | |||
public function createTicketMessage($params) | |||
{ | |||
$classTicketMessage = $this->em->getClassMetadata(TicketMessageInterface::class)->getName() ; | |||
$ticketMessage = new $classTicketMessage ; | |||
$ticket = $params['ticket'] ; | |||
$ticketMessage->setStatus(1) ; | |||
$ticketMessage->setTicket($ticket) ; | |||
$ticketMessage->setMessage($params['message']) ; | |||
if(isset($params['answerByAdmin']) && $params['answerByAdmin']) { | |||
$ticketMessage->setAnswerByAdmin($params['answerByAdmin']) ; | |||
// envoi email au client | |||
$this->mailUtils->send([ | |||
MailUtils::SUBJECT => 'Réponse à votre demande', | |||
MailUtils::TO_EMAIL => $ticket->getUser() ? $ticket->getUser()->getEmail() : $ticket->getVisitorEmail(), | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-response', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $ticket->getUser() ? $ticket->getUser()->getFirstname() : $ticket->getVisitorFirstname(), | |||
'ticket' => $ticket | |||
], | |||
]) ; | |||
} | |||
$this->em->persist($ticketMessage); | |||
if(isset($params['closeTicket']) && $params['closeTicket']) { | |||
$ticket->setStatus(Ticket::TICKET_STATUS_CLOSED) ; | |||
} | |||
if (isset($params['createByAdmin']) && $params['createByAdmin']) { | |||
$ticketMessage->setAnswerByAdmin(true); | |||
} | |||
$this->em->persist($ticketMessage); | |||
$this->em->flush(); | |||
if (isset($params['createByAdmin']) && $params['createByAdmin']) { | |||
// envoi email au client | |||
$this->mailUtils->send( | |||
[ | |||
MailUtils::SUBJECT => 'Vous avez reçu un nouveau message', | |||
MailUtils::TO_EMAIL => $email, | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-new-by-admin', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $firstname, | |||
'ticket' => $ticket, | |||
], | |||
] | |||
); | |||
} else { | |||
$this->mailUtils->send( | |||
[ | |||
MailUtils::SUBJECT => 'Nouvelle demande', | |||
MailUtils::TO_EMAIL => $email, | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-new', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $firstname, | |||
'ticket' => $ticket, | |||
], | |||
] | |||
); | |||
} | |||
$ticket->setUpdatedAt(new \DateTime()) ; | |||
$this->em->persist($ticket); | |||
$this->em->flush() ; | |||
$this->notifyAdmin($ticket, $ticketMessage); | |||
return $ticket; | |||
} | |||
public function createTicketMessage($params) | |||
{ | |||
$classTicketMessage = $this->em->getClassMetadata(TicketMessageInterface::class)->getName(); | |||
$ticketMessage = new $classTicketMessage; | |||
$ticket = $params['ticket']; | |||
$ticketMessage->setStatus(1); | |||
$ticketMessage->setTicket($ticket); | |||
$ticketMessage->setMessage($params['message']); | |||
if (isset($params['answerByAdmin']) && $params['answerByAdmin']) { | |||
$ticketMessage->setAnswerByAdmin($params['answerByAdmin']); | |||
// envoi email au client | |||
$this->mailUtils->send( | |||
[ | |||
MailUtils::SUBJECT => 'Réponse à votre demande', | |||
MailUtils::TO_EMAIL => $ticket->getUser() ? $ticket->getUser()->getEmail( | |||
) : $ticket->getVisitorEmail(), | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-response', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $ticket->getUser() ? $ticket->getUser()->getFirstname( | |||
) : $ticket->getVisitorFirstname(), | |||
'ticket' => $ticket, | |||
], | |||
] | |||
); | |||
} | |||
$this->em->persist($ticketMessage); | |||
return $ticketMessage ; | |||
if (isset($params['imageFilename']) && $params['imageFilename']) { | |||
$ticketMessage->setImageFilename($params['imageFilename']); | |||
} | |||
if (isset($params['closeTicket']) && $params['closeTicket']) { | |||
$ticket->setStatus(Ticket::TICKET_STATUS_CLOSED); | |||
} | |||
public function notifyAdmin($ticket, $ticketMessage){ | |||
$userRepo = $this->em->getRepository(UserInterface::class); | |||
$usersToNotify = $userRepo->findByTicketTypesNotification($ticket->getType()); | |||
$ticket->setUpdatedAt(new \DateTime()); | |||
$this->em->persist($ticket); | |||
$this->em->flush(); | |||
foreach ($usersToNotify as $userToNotify){ | |||
return $ticketMessage; | |||
} | |||
if($this->authorizationChecker->isGranted('ROLE_ADMIN', $userToNotify)){ | |||
public function notifyAdmin($ticket, $ticketMessage) | |||
{ | |||
$userRepo = $this->em->getRepository(UserInterface::class); | |||
$usersToNotify = $userRepo->findByTicketTypesNotification($ticket->getType()); | |||
// envoi email au client | |||
$this->mailUtils->send([ | |||
MailUtils::SUBJECT => 'Nouveau ticket sur placedulocal', | |||
MailUtils::TO_EMAIL => $userToNotify->getEmail(), | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-notification', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $userToNotify->getFirstname(), | |||
'ticket' => $ticket, | |||
'ticketMessage' => $ticketMessage | |||
], | |||
]) ; | |||
} | |||
} | |||
foreach ($usersToNotify as $userToNotify) { | |||
if ($this->authorizationChecker->isGranted('ROLE_ADMIN', $userToNotify)) { | |||
// envoi email au client | |||
$this->mailUtils->send( | |||
[ | |||
MailUtils::SUBJECT => 'Nouveau ticket sur placedulocal', | |||
MailUtils::TO_EMAIL => $userToNotify->getEmail(), | |||
MailUtils::CONTENT_TEMPLATE => 'mail/ticket-notification', | |||
MailUtils::CONTENT_DATA => [ | |||
'firstname' => $userToNotify->getFirstname(), | |||
'ticket' => $ticket, | |||
'ticketMessage' => $ticketMessage, | |||
], | |||
] | |||
); | |||
} | |||
} | |||
} | |||
} | |||
@@ -377,7 +377,7 @@ class Utils | |||
return $this->getNextDay($this->getDayByNumber($number, 'en')); | |||
} | |||
public function getDayByNumber($number, $lang = 'fr') | |||
public static function getDayByNumber($number, $lang = 'fr') | |||
{ | |||
if ($lang == 'fr') { | |||
$daysArray = [ | |||
@@ -615,5 +615,74 @@ class Utils | |||
return $phone ; | |||
} | |||
public function getMetaTitle($entity) | |||
{ | |||
if($entity) { | |||
if(method_exists($entity, 'getMetaTitle')) { | |||
return $entity->getMetaTitle() ; | |||
} | |||
elseif(method_exists($entity, 'getTitle')) { | |||
return $entity->getTitle() ; | |||
} | |||
} | |||
return '' ; | |||
} | |||
public function getMetaDescription($entity) | |||
{ | |||
if($entity) { | |||
if(method_exists($entity, 'getMetaDescription')) { | |||
return $entity->getMetaDescription() ; | |||
} | |||
elseif(method_exists($entity, 'getDescription')) { | |||
return $entity->getDescription() ; | |||
} | |||
} | |||
return '' ; | |||
} | |||
public function getOpenGraphTitle($entity) | |||
{ | |||
if($entity) { | |||
if(method_exists($entity, 'getOpenGraphTitle')) { | |||
return $entity->getOpenGraphTitle() ; | |||
} | |||
elseif(method_exists($entity, 'getTitle')) { | |||
return $entity->getTitle() ; | |||
} | |||
} | |||
return '' ; | |||
} | |||
public function getOpenGraphDescription($entity) | |||
{ | |||
if($entity) { | |||
if(method_exists($entity, 'getOpenGraphDescription')) { | |||
return $entity->getOpenGraphDescription() ; | |||
} | |||
elseif(method_exists($entity, 'getDescription')) { | |||
return $entity->getDescription() ; | |||
} | |||
} | |||
return '' ; | |||
} | |||
public function getOpenGraphImage($entity) | |||
{ | |||
if($entity) { | |||
if(method_exists($entity, 'getOpenGraphImage')) { | |||
return $entity->getOpenGraphImage() ; | |||
} | |||
elseif(method_exists($entity, 'getImage')) { | |||
return $entity->getImage() ; | |||
} | |||
} | |||
return '' ; | |||
} | |||
} |
@@ -29,6 +29,7 @@ class UtilsManager | |||
protected $statisticsUtils; | |||
protected $pointLocationUtils ; | |||
protected $sectionUtils ; | |||
protected $mailjetSmsUtils ; | |||
public function __construct( | |||
Utils $utils, | |||
@@ -44,7 +45,8 @@ class UtilsManager | |||
TicketUtils $ticketUtils, | |||
PointLocationUtils $pointLocationUtils, | |||
UtilsProcess $utilsProcess, | |||
SectionUtilsInterface $sectionUtils | |||
SectionUtilsInterface $sectionUtils, | |||
MailjetSmsUtils $mailjetSmsUtils | |||
) | |||
{ | |||
$this->utils = $utils ; | |||
@@ -61,6 +63,7 @@ class UtilsManager | |||
$this->pointLocationUtils = $pointLocationUtils ; | |||
$this->utilsProcess = $utilsProcess ; | |||
$this->sectionUtils = $sectionUtils ; | |||
$this->mailjetSmsUtils = $mailjetSmsUtils ; | |||
} | |||
public function getUtils(): Utils | |||
@@ -133,4 +136,9 @@ class UtilsManager | |||
return $this->sectionUtils ; | |||
} | |||
public function getMailjetSmsUtils(): MailjetSmsUtils | |||
{ | |||
return $this->mailjetSmsUtils ; | |||
} | |||
} |
@@ -93,13 +93,15 @@ class UtilsProcess | |||
if ($entity->getImage() && file_exists($basePath . $entity->getImage())) { | |||
$extension = (pathinfo($basePath . $entity->getImage(), PATHINFO_EXTENSION)); | |||
$extension = strtolower(pathinfo($basePath . $entity->getImage(), PATHINFO_EXTENSION)); | |||
if ($extension == "jpg" || $extension == "png" || $extension == "gif") { | |||
$newImage = md5(uniqid()) . '.' . $extension; | |||
if ($folder) $newImage = $folder . '/' . $newImage; | |||
copy($basePath . $entity->getImage(), $basePath . $newImage); | |||
$entity->setImage($newImage); | |||
}else{ | |||
$entity->setImage(null); | |||
} | |||
} else { | |||
$entity->setImage(null); |
@@ -0,0 +1,172 @@ | |||
<?php | |||
namespace Lc\ShopBundle\Statistic; | |||
use Lc\ShopBundle\Manager\EntityManager; | |||
use Symfony\Component\OptionsResolver\OptionsResolver; | |||
class Statistic | |||
{ | |||
protected $properties = array(); | |||
protected $averageProperties = array(); | |||
protected $labels; | |||
protected $dateStart; | |||
protected $dateEnd; | |||
protected $interval; | |||
protected $em; | |||
protected $resultsSort; | |||
public function __construct(EntityManager $entityManager, ?\DateTime $dateStart = null, ?\DateTime $dateEnd = null, ?string $interval = null) | |||
{ | |||
$this->em = $entityManager; | |||
$this->dateStart = $dateStart; | |||
$this->dateEnd = $dateEnd; | |||
$this->interval = $interval; | |||
} | |||
public function addProperty(string $propertyName, array $options) | |||
{ | |||
$resolver = new OptionsResolver(); | |||
$this->configurePropertyOptions($resolver); | |||
$this->properties[$propertyName] = $resolver->resolve($options); | |||
$this->resultsSort[$propertyName] = $propertyName; | |||
} | |||
public function addAverageProperty(string $propertyName, array $options) | |||
{ | |||
$resolver = new OptionsResolver(); | |||
$this->configurePropertyOptions($resolver); | |||
$this->averageProperties[$propertyName] = $resolver->resolve($options); | |||
$this->resultsSort[$propertyName] = $propertyName; | |||
} | |||
public function setAverageData(string $propertyName, string $key, $value) | |||
{ | |||
if (isset($this->averageProperties[$propertyName])) { | |||
$this->averageProperties[$propertyName]['data'][$key] += number_format($value, 2); | |||
} else { | |||
throw new \Exception('La proprieté "' . $propertyName . '" n\'existe pas '); | |||
} | |||
} | |||
public function setData(string $propertyName, string $key, $value) | |||
{ | |||
if (isset($this->properties[$propertyName])) { | |||
$this->properties[$propertyName]['data'][$key] += number_format($value, 2); | |||
$this->properties[$propertyName]['total_period'] += number_format($value, 2); | |||
} else { | |||
throw new \Exception('La proprieté "' . $propertyName . '" n\'existe pas '); | |||
} | |||
} | |||
public function setAveragePropertiesData() | |||
{ | |||
foreach ($this->getLabels() as $key => $label) { | |||
foreach ($this->getAverageProperties() as $averagePropertyName => $averageProperty) { | |||
if ($this->getData($averageProperty['divider'], $key)) { | |||
$this->setAverageData($averagePropertyName, $key, $this->getData($averageProperty['dividend'], $key) / $this->getData($averageProperty['divider'], $key)); | |||
} | |||
if ($this->getTotalPeriod($averageProperty['divider'])) { | |||
$this->averageProperties[$averagePropertyName]['total_period'] = $this->getTotalPeriod($averageProperty['dividend']) / $this->getTotalPeriod($averageProperty['divider']); | |||
} | |||
} | |||
} | |||
foreach ($this->getProperties() as $propertyName => $property) { | |||
$this->properties[$propertyName]['average_period'] = number_format($this->properties[$propertyName]['total_period'] / count($this->getLabels()),2); | |||
} | |||
foreach ($this->getAverageProperties() as $averagePropertyName => $averageProperty) { | |||
$this->averageProperties[$averagePropertyName]['average_period'] = number_format($this->averageProperties[$averagePropertyName]['total_period'] / count($this->getLabels()), 2); | |||
} | |||
} | |||
public function getLabels() | |||
{ | |||
return $this->labels; | |||
} | |||
public function getProperties() | |||
{ | |||
return $this->properties; | |||
} | |||
public function getData($propertyName, $key) | |||
{ | |||
if (!isset($this->properties[$propertyName])) { | |||
throw new \Exception('La proprieté "' . $propertyName . '" n\'existe pas '); | |||
} | |||
return $this->properties[$propertyName]['data'][$key]; | |||
} | |||
public function getTotalPeriod($propertyName) | |||
{ | |||
if (!isset($this->properties[$propertyName])) { | |||
throw new \Exception('La proprieté "' . $propertyName . '" n\'existe pas '); | |||
} | |||
return $this->properties[$propertyName]['total_period']; | |||
} | |||
public function getAverageProperties() | |||
{ | |||
return $this->averageProperties; | |||
} | |||
public function getResults() | |||
{ | |||
$results = array_replace($this->resultsSort, $this->properties, $this->averageProperties); | |||
return $results; | |||
} | |||
public function getAsArray() | |||
{ | |||
return array( | |||
'label' => $this->getLabels(), | |||
'data' => $this->getResults(), | |||
); | |||
} | |||
public function getDateRange() | |||
{ | |||
return new \DatePeriod($this->getDateStart(), new \DateInterval('P1' . $this->getInterval()), $this->getDateEnd()); | |||
} | |||
public function getDateStart() | |||
{ | |||
return $this->dateStart; | |||
} | |||
public function getDateEnd() | |||
{ | |||
return $this->dateEnd; | |||
} | |||
public function getInterval() | |||
{ | |||
return $this->interval; | |||
} | |||
public function configurePropertyOptions(OptionsResolver $resolver) | |||
{ | |||
$resolver->setDefaults([ | |||
'unit' => '', | |||
'label' => 'Chiffre affaire produit', | |||
'label_short' => 'CA produit', | |||
'data' => array(), | |||
'total_period' => 0, | |||
'average_period' => 0, | |||
'dividend' => null, | |||
'divider' => null, | |||
]); | |||
} | |||
} |