Combination creation management
This commit is contained in:
@@ -63,7 +63,7 @@ class Attribute extends BaseAction implements EventSubscriberInterface
|
|||||||
|
|
||||||
// Add atribute to all product templates if required
|
// Add atribute to all product templates if required
|
||||||
if ($event->getAddToAllTemplates() != 0) {
|
if ($event->getAddToAllTemplates() != 0) {
|
||||||
// TODO: add to all product template
|
$this->doAddToAllTemplates($attribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class Feature extends BaseAction implements EventSubscriberInterface
|
|||||||
|
|
||||||
// Add atribute to all product templates if required
|
// Add atribute to all product templates if required
|
||||||
if ($event->getAddToAllTemplates() != 0) {
|
if ($event->getAddToAllTemplates() != 0) {
|
||||||
// TODO: add to all product template
|
$this->doAddToAllTemplates($feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ use Thelia\Model\AttributeAvQuery;
|
|||||||
use Thelia\Model\Currency;
|
use Thelia\Model\Currency;
|
||||||
use Thelia\Model\Map\AttributeCombinationTableMap;
|
use Thelia\Model\Map\AttributeCombinationTableMap;
|
||||||
use Propel\Runtime\ActiveQuery\Criteria;
|
use Propel\Runtime\ActiveQuery\Criteria;
|
||||||
|
use Thelia\Core\Event\Product\ProductCombinationGenerationEvent;
|
||||||
|
use Propel\Runtime\Connection\ConnectionInterface;
|
||||||
|
|
||||||
class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
||||||
{
|
{
|
||||||
@@ -65,7 +67,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
|||||||
|
|
||||||
if ($salesElement == null) {
|
if ($salesElement == null) {
|
||||||
// Create a new default product sale element
|
// Create a new default product sale element
|
||||||
$salesElement = $event->getProduct()->createDefaultProductSaleElement($con, 0, 0, $event->getCurrencyId(), true);
|
$salesElement = $event->getProduct()->createDefaultProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true);
|
||||||
} else {
|
} else {
|
||||||
// This (new) one is the default
|
// This (new) one is the default
|
||||||
$salesElement->setIsDefault(true)->save($con);
|
$salesElement->setIsDefault(true)->save($con);
|
||||||
@@ -87,7 +89,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
|||||||
->setAttributeAvId($attributeAvId)
|
->setAttributeAvId($attributeAvId)
|
||||||
->setAttribute($attributeAv->getAttribute())
|
->setAttribute($attributeAv->getAttribute())
|
||||||
->setProductSaleElements($salesElement)
|
->setProductSaleElements($salesElement)
|
||||||
->save();
|
->save($con);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,8 +208,9 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
|||||||
|
|
||||||
if ($product->countSaleElements() <= 0) {
|
if ($product->countSaleElements() <= 0) {
|
||||||
// If we just deleted the last PSE, create a default one
|
// If we just deleted the last PSE, create a default one
|
||||||
$product->createDefaultProductSaleElement($con, 0, 0, $event->getCurrencyId(), true);
|
$product->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true);
|
||||||
} elseif ($pse->getIsDefault()) {
|
}
|
||||||
|
elseif ($pse->getIsDefault()) {
|
||||||
|
|
||||||
// If we deleted the default PSE, make the last created one the default
|
// If we deleted the default PSE, make the last created one the default
|
||||||
$pse = ProductSaleElementsQuery::create()
|
$pse = ProductSaleElementsQuery::create()
|
||||||
@@ -230,6 +233,83 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate combinations. All existing combinations for the product are deleted.
|
||||||
|
*
|
||||||
|
* @param ProductCombinationGenerationEvent $event
|
||||||
|
*/
|
||||||
|
public function generateCombinations(ProductCombinationGenerationEvent $event) {
|
||||||
|
|
||||||
|
$con = Propel::getWriteConnection(ProductSaleElementsTableMap::DATABASE_NAME);
|
||||||
|
|
||||||
|
$con->beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Delete all product's productSaleElement
|
||||||
|
ProductSaleElementsQuery::create()->filterByProductId($event->product->getId())->delete();
|
||||||
|
|
||||||
|
$isDefault = true;
|
||||||
|
|
||||||
|
// Create all combinations
|
||||||
|
foreach($event->getCombinations() as $combinationAttributesAvIds) {
|
||||||
|
|
||||||
|
// Create the PSE
|
||||||
|
$saleElement = $event->getProduct()->createProductSaleElement(
|
||||||
|
$con,
|
||||||
|
$event->getWeight(),
|
||||||
|
$event->getPrice(),
|
||||||
|
$event->getSalePrice(),
|
||||||
|
$event->getCurrencyId(),
|
||||||
|
$isDefault,
|
||||||
|
$event->getOnsale(),
|
||||||
|
$event->getIsnew(),
|
||||||
|
$event->getQuantity(),
|
||||||
|
$event->getEanCode(),
|
||||||
|
$event->getReference()
|
||||||
|
);
|
||||||
|
|
||||||
|
$isDefault = false;
|
||||||
|
|
||||||
|
$this->createCombination($con, $saleElement, $combinationAttributesAvIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store all the stuff !
|
||||||
|
$con->commit();
|
||||||
|
}
|
||||||
|
catch (\Exception $ex) {
|
||||||
|
|
||||||
|
$con->rollback();
|
||||||
|
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a combination for a given product sale element
|
||||||
|
*
|
||||||
|
* @param ConnectionInterface $con the Propel connection
|
||||||
|
* @param ProductSaleElement $salesElement the product sale element
|
||||||
|
* @param unknown $combinationAttributes an array oif attributes av IDs
|
||||||
|
*/
|
||||||
|
protected function createCombination(ConnectionInterface $con, ProductSaleElements $salesElement, $combinationAttributes)
|
||||||
|
{
|
||||||
|
foreach ($combinationAttributes as $attributeAvId) {
|
||||||
|
|
||||||
|
$attributeAv = AttributeAvQuery::create()->findPk($attributeAvId);
|
||||||
|
|
||||||
|
if ($attributeAv !== null) {
|
||||||
|
$attributeCombination = new AttributeCombination();
|
||||||
|
|
||||||
|
$attributeCombination
|
||||||
|
->setAttributeAvId($attributeAvId)
|
||||||
|
->setAttribute($attributeAv->getAttribute())
|
||||||
|
->setProductSaleElements($salesElement)
|
||||||
|
->save($con);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@@ -239,6 +319,8 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface
|
|||||||
TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128),
|
TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128),
|
||||||
TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128),
|
TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128),
|
||||||
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128),
|
TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128),
|
||||||
|
TheliaEvents::PRODUCT_COMBINATION_GENERATION => array("generateCombinations", 128),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,8 @@
|
|||||||
|
|
||||||
<form name="thelia.admin.product_sale_element.update" class="Thelia\Form\ProductSaleElementUpdateForm"/>
|
<form name="thelia.admin.product_sale_element.update" class="Thelia\Form\ProductSaleElementUpdateForm"/>
|
||||||
<form name="thelia.admin.product_default_sale_element.update" class="Thelia\Form\ProductDefaultSaleElementUpdateForm"/>
|
<form name="thelia.admin.product_default_sale_element.update" class="Thelia\Form\ProductDefaultSaleElementUpdateForm"/>
|
||||||
|
<form name="thelia.admin.product_combination.build" class="Thelia\Form\ProductCombinationGenerationForm"/>
|
||||||
|
|
||||||
|
|
||||||
<form name="thelia.admin.product.deletion" class="Thelia\Form\ProductModificationForm"/>
|
<form name="thelia.admin.product.deletion" class="Thelia\Form\ProductModificationForm"/>
|
||||||
|
|
||||||
|
|||||||
@@ -372,6 +372,10 @@
|
|||||||
<default key="_controller">Thelia\Controller\Admin\ProductController::updateProductSaleElementsAction</default>
|
<default key="_controller">Thelia\Controller\Admin\ProductController::updateProductSaleElementsAction</default>
|
||||||
</route>
|
</route>
|
||||||
|
|
||||||
|
<route id="admin.product.combination.build" path="/admin/product/combination/build">
|
||||||
|
<default key="_controller">Thelia\Controller\Admin\ProductController::buildCombinationsAction</default>
|
||||||
|
</route>
|
||||||
|
|
||||||
<route id="admin.product.combination.defaut-price.update" path="/admin/product/default-price/update">
|
<route id="admin.product.combination.defaut-price.update" path="/admin/product/default-price/update">
|
||||||
<default key="_controller">Thelia\Controller\Admin\ProductController::updateProductDefaultSaleElementAction</default>
|
<default key="_controller">Thelia\Controller\Admin\ProductController::updateProductDefaultSaleElementAction</default>
|
||||||
</route>
|
</route>
|
||||||
|
|||||||
@@ -157,23 +157,6 @@ class FeatureController extends AbstractCrudController
|
|||||||
'postscriptum' => $object->getPostscriptum()
|
'postscriptum' => $object->getPostscriptum()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Setup features values
|
|
||||||
/*
|
|
||||||
* FIXME : doesn't work. "We get a This form should not contain extra fields." error
|
|
||||||
$attr_av_list = FeatureAvQuery::create()
|
|
||||||
->joinWithI18n($this->getCurrentEditionLocale())
|
|
||||||
->filterByFeatureId($object->getId())
|
|
||||||
->find();
|
|
||||||
|
|
||||||
$attr_array = array();
|
|
||||||
|
|
||||||
foreach ($attr_av_list as $attr_av) {
|
|
||||||
$attr_array[$attr_av->getId()] = $attr_av->getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
$data['feature_values'] = $attr_array;
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Setup the object form
|
// Setup the object form
|
||||||
return new FeatureModificationForm($this->getRequest(), "form", $data);
|
return new FeatureModificationForm($this->getRequest(), "form", $data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ use Thelia\Model\Country;
|
|||||||
use Thelia\Tools\NumberFormat;
|
use Thelia\Tools\NumberFormat;
|
||||||
use Thelia\Model\Product;
|
use Thelia\Model\Product;
|
||||||
use Thelia\Model\CurrencyQuery;
|
use Thelia\Model\CurrencyQuery;
|
||||||
|
use Thelia\Form\ProductCombinationGenerationForm;
|
||||||
|
use Thelia\Core\Event\Product\ProductCombinationGenerationEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages products
|
* Manages products
|
||||||
@@ -1025,6 +1027,108 @@ class ProductController extends AbstractCrudController
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create combinations
|
||||||
|
protected function combine($input, &$output, &$tmp) {
|
||||||
|
$current = array_shift($input);
|
||||||
|
|
||||||
|
if (count($input) > 0) {
|
||||||
|
foreach($current as $element) {
|
||||||
|
$tmp[] = $element;
|
||||||
|
$this->combine($input, $output, $tmp);
|
||||||
|
array_pop($tmp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
foreach($current as $element) {
|
||||||
|
$tmp[] = $element;
|
||||||
|
$output[] = $tmp;
|
||||||
|
array_pop($tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build combinations from the combination output builder
|
||||||
|
*/
|
||||||
|
public function buildCombinationsAction() {
|
||||||
|
|
||||||
|
// Check current user authorization
|
||||||
|
if (null !== $response = $this->checkAuth($this->resourceCode, AccessManager::UPDATE)) return $response;
|
||||||
|
|
||||||
|
$error_msg = false;
|
||||||
|
|
||||||
|
$changeForm = new ProductCombinationGenerationForm($this->getRequest());
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Check the form against constraints violations
|
||||||
|
$form = $this->validateForm($changeForm, "POST");
|
||||||
|
|
||||||
|
// Get the form field values
|
||||||
|
$data = $form->getData();
|
||||||
|
|
||||||
|
// Rework attributes_av array, to build an array which contains all combinations,
|
||||||
|
// in the form combination[] = array of combination attributes av IDs
|
||||||
|
//
|
||||||
|
// First, create an array of attributes_av ID in the form $attributes_av_list[$attribute_id] = array of attributes_av ID
|
||||||
|
// from the list of attribute_id:attributes_av ID from the form.
|
||||||
|
$combinations = $attributes_av_list = array();
|
||||||
|
|
||||||
|
foreach($data['attribute_av'] as $item) {
|
||||||
|
list($attribute_id, $attribute_av_id) = explode(':', $item);
|
||||||
|
|
||||||
|
if (! isset($attributes_av_list[$attribute_id]))
|
||||||
|
$attributes_av_list[$attribute_id] = array();
|
||||||
|
|
||||||
|
|
||||||
|
$attributes_av_list[$attribute_id][] = $attribute_av_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, recursively combine array
|
||||||
|
$combinations = $tmp = array();
|
||||||
|
|
||||||
|
$this->combine($attributes_av_list, $combinations, $tmp);
|
||||||
|
|
||||||
|
// Create event
|
||||||
|
$event = new ProductCombinationGenerationEvent(
|
||||||
|
$this->getExistingObject(),
|
||||||
|
$data['currency'],
|
||||||
|
$combinations
|
||||||
|
);
|
||||||
|
|
||||||
|
$event
|
||||||
|
->setReference($data['reference'] == null ? '' : $data['reference'])
|
||||||
|
->setPrice($data['price'] == null ? 0 : $data['price'])
|
||||||
|
->setWeight($data['weight'] == null ? 0 : $data['weight'])
|
||||||
|
->setQuantity($data['quantity'] == null ? 0 : $data['quantity'])
|
||||||
|
->setSalePrice($data['sale_price'] == null ? 0 : $data['sale_price'])
|
||||||
|
->setOnsale($data['onsale'] == null ? false : $data['onsale'])
|
||||||
|
->setIsnew($data['isnew'] == null ? false : $data['isnew'])
|
||||||
|
->setEanCode($data['ean_code'] == null ? '' : $data['ean_code'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->dispatch(TheliaEvents::PRODUCT_COMBINATION_GENERATION, $event);
|
||||||
|
|
||||||
|
// Log object modification
|
||||||
|
$this->adminLogAppend(sprintf("Combination generation for product reference %s", $event->getProduct()->getRef()));
|
||||||
|
|
||||||
|
// Redirect to the success URL
|
||||||
|
$this->redirect($changeForm->getSuccessUrl());
|
||||||
|
|
||||||
|
} catch (FormValidationException $ex) {
|
||||||
|
// Form cannot be validated
|
||||||
|
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
|
||||||
|
} catch (\Exception $ex) {
|
||||||
|
// Any other error
|
||||||
|
$error_msg = $ex->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setupFormErrorContext(
|
||||||
|
$this->getTranslator()->trans("Combination builder"), $error_msg, $changeForm, $ex);
|
||||||
|
|
||||||
|
// At this point, the form has errors, and should be redisplayed.
|
||||||
|
return $this->renderEditionTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked through Ajax; this method calculates the taxed price from the unaxed price, and
|
* Invoked through Ajax; this method calculates the taxed price from the unaxed price, and
|
||||||
* vice versa.
|
* vice versa.
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
/*************************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Thelia */
|
||||||
|
/* */
|
||||||
|
/* Copyright (c) OpenStudio */
|
||||||
|
/* email : info@thelia.net */
|
||||||
|
/* web : http://www.thelia.net */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or modify */
|
||||||
|
/* it under the terms of the GNU General Public License as published by */
|
||||||
|
/* the Free Software Foundation; either version 3 of the License */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU General Public License for more details. */
|
||||||
|
/* */
|
||||||
|
/* You should have received a copy of the GNU General Public License */
|
||||||
|
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
/* */
|
||||||
|
/*************************************************************************************/
|
||||||
|
|
||||||
|
namespace Thelia\Core\Event\Product;
|
||||||
|
use Thelia\Model\Product;
|
||||||
|
|
||||||
|
class ProductCombinationGenerationEvent extends ProductEvent
|
||||||
|
{
|
||||||
|
protected $reference;
|
||||||
|
protected $price;
|
||||||
|
protected $currency_id;
|
||||||
|
protected $weight;
|
||||||
|
protected $quantity;
|
||||||
|
protected $sale_price;
|
||||||
|
protected $onsale;
|
||||||
|
protected $isnew;
|
||||||
|
protected $ean_code;
|
||||||
|
protected $combinations;
|
||||||
|
|
||||||
|
public function __construct(Product $product, $currency_id, $combinations)
|
||||||
|
{
|
||||||
|
parent::__construct($product);
|
||||||
|
|
||||||
|
$this->setCombinations($combinations);
|
||||||
|
$this->setCurrencyId($currency_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrencyId()
|
||||||
|
{
|
||||||
|
return $this->currency_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrencyId($currency_id)
|
||||||
|
{
|
||||||
|
$this->currency_id = $currency_id;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReference()
|
||||||
|
{
|
||||||
|
return $this->reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setReference($reference)
|
||||||
|
{
|
||||||
|
$this->reference = $reference;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrice()
|
||||||
|
{
|
||||||
|
return $this->price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPrice($price)
|
||||||
|
{
|
||||||
|
$this->price = $price;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWeight()
|
||||||
|
{
|
||||||
|
return $this->weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setWeight($weight)
|
||||||
|
{
|
||||||
|
$this->weight = $weight;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuantity()
|
||||||
|
{
|
||||||
|
return $this->quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuantity($quantity)
|
||||||
|
{
|
||||||
|
$this->quantity = $quantity;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSalePrice()
|
||||||
|
{
|
||||||
|
return $this->sale_price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSalePrice($sale_price)
|
||||||
|
{
|
||||||
|
$this->sale_price = $sale_price;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOnsale()
|
||||||
|
{
|
||||||
|
return $this->onsale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOnsale($onsale)
|
||||||
|
{
|
||||||
|
$this->onsale = $onsale;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsnew()
|
||||||
|
{
|
||||||
|
return $this->isnew;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setIsnew($isnew)
|
||||||
|
{
|
||||||
|
$this->isnew = $isnew;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEanCode()
|
||||||
|
{
|
||||||
|
return $this->ean_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setEanCode($ean_code)
|
||||||
|
{
|
||||||
|
$this->ean_code = $ean_code;
|
||||||
|
return $this;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCombinations()
|
||||||
|
{
|
||||||
|
return $this->combinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCombinations($combinations)
|
||||||
|
{
|
||||||
|
$this->combinations = $combinations;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -285,6 +285,8 @@ final class TheliaEvents
|
|||||||
const PRODUCT_DELETE_PRODUCT_SALE_ELEMENT = "action.deleteProductSaleElement";
|
const PRODUCT_DELETE_PRODUCT_SALE_ELEMENT = "action.deleteProductSaleElement";
|
||||||
const PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT = "action.updateProductSaleElement";
|
const PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT = "action.updateProductSaleElement";
|
||||||
|
|
||||||
|
const PRODUCT_COMBINATION_GENERATION = "action.productCombineationGeneration";
|
||||||
|
|
||||||
const PRODUCT_SET_TEMPLATE = "action.productSetTemplate";
|
const PRODUCT_SET_TEMPLATE = "action.productSetTemplate";
|
||||||
|
|
||||||
const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory";
|
const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory";
|
||||||
|
|||||||
@@ -128,14 +128,17 @@ class AttributeAvailability extends BaseI18nLoop
|
|||||||
|
|
||||||
foreach ($attributesAv as $attributeAv) {
|
foreach ($attributesAv as $attributeAv) {
|
||||||
$loopResultRow = new LoopResultRow($loopResult, $attributeAv, $this->versionable, $this->timestampable, $this->countable);
|
$loopResultRow = new LoopResultRow($loopResult, $attributeAv, $this->versionable, $this->timestampable, $this->countable);
|
||||||
$loopResultRow->set("ID", $attributeAv->getId())
|
$loopResultRow
|
||||||
->set("IS_TRANSLATED",$attributeAv->getVirtualColumn('IS_TRANSLATED'))
|
->set("ID" , $attributeAv->getId())
|
||||||
->set("LOCALE",$locale)
|
->set("ATTRIBUTE_ID" , $attributeAv->getAttributeId())
|
||||||
->set("TITLE",$attributeAv->getVirtualColumn('i18n_TITLE'))
|
->set("IS_TRANSLATED", $attributeAv->getVirtualColumn('IS_TRANSLATED'))
|
||||||
->set("CHAPO", $attributeAv->getVirtualColumn('i18n_CHAPO'))
|
->set("LOCALE" , $locale)
|
||||||
->set("DESCRIPTION", $attributeAv->getVirtualColumn('i18n_DESCRIPTION'))
|
->set("TITLE" , $attributeAv->getVirtualColumn('i18n_TITLE'))
|
||||||
->set("POSTSCRIPTUM", $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM'))
|
->set("CHAPO" , $attributeAv->getVirtualColumn('i18n_CHAPO'))
|
||||||
->set("POSITION", $attributeAv->getPosition());
|
->set("DESCRIPTION" , $attributeAv->getVirtualColumn('i18n_DESCRIPTION'))
|
||||||
|
->set("POSTSCRIPTUM" , $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM'))
|
||||||
|
->set("POSITION" , $attributeAv->getPosition())
|
||||||
|
;
|
||||||
|
|
||||||
$loopResult->addRow($loopResultRow);
|
$loopResult->addRow($loopResultRow);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,30 +199,29 @@ class Form extends AbstractSmartyPlugin
|
|||||||
{
|
{
|
||||||
if ($repeat) {
|
if ($repeat) {
|
||||||
|
|
||||||
$formFieldView = $this->getFormFieldView($params);
|
$formFieldView = $this->getFormFieldView($params);
|
||||||
$formFieldConfig = $this->getFormFieldConfig($params);
|
$formFieldConfig = $this->getFormFieldConfig($params);
|
||||||
|
|
||||||
$this->assignFormTypeValues($template, $formFieldConfig, $formFieldView);
|
$this->assignFormTypeValues($template, $formFieldConfig, $formFieldView);
|
||||||
|
|
||||||
$value = $formFieldView->vars["value"];
|
$value = $formFieldView->vars["value"];
|
||||||
|
|
||||||
// We have a collection
|
$key = $this->getParam($params, 'value_key', null);
|
||||||
if (0 < $value_count = count($formFieldView->children)) {
|
|
||||||
|
|
||||||
$key = $this->getParam($params, 'value_key', null);
|
// We (may) have a collection
|
||||||
|
if ($key !== null) {
|
||||||
|
|
||||||
if ($key !== null) {
|
// Force array
|
||||||
// If the field is not found, use an empty value
|
if (! is_array($value)) $value = array();
|
||||||
$val = array_key_exists($key, $value) ? $value[$key] : '';
|
|
||||||
|
|
||||||
$name = sprintf("%s[%s]", $formFieldView->vars["full_name"], $key);
|
// If the field is not found, use an empty value
|
||||||
|
$val = array_key_exists($key, $value) ? $value[$key] : '';
|
||||||
|
|
||||||
$val = $value[$key];
|
$name = sprintf("%s[%s]", $formFieldView->vars["full_name"], $key);
|
||||||
|
|
||||||
$this->assignFieldValues($template, $name, $val, $formFieldView->vars, $value_count);
|
$val = $value[$key];
|
||||||
} else {
|
|
||||||
throw new \InvalidArgumentException(sprintf("Missing or empty parameter 'value_key' for field '%s'", $formFieldView->vars["name"]));
|
$this->assignFieldValues($template, $name, $val, $formFieldView->vars, count($formFieldView->children));
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$this->assignFieldValues($template, $formFieldView->vars["full_name"], $formFieldView->vars["value"], $formFieldView->vars);
|
$this->assignFieldValues($template, $formFieldView->vars["full_name"], $formFieldView->vars["value"], $formFieldView->vars);
|
||||||
}
|
}
|
||||||
|
|||||||
91
core/lib/Thelia/Form/ProductCombinationGenerationForm.php
Normal file
91
core/lib/Thelia/Form/ProductCombinationGenerationForm.php
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
/*************************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Thelia */
|
||||||
|
/* */
|
||||||
|
/* Copyright (c) OpenStudio */
|
||||||
|
/* email : info@thelia.net */
|
||||||
|
/* web : http://www.thelia.net */
|
||||||
|
/* */
|
||||||
|
/* This program is free software; you can redistribute it and/or modify */
|
||||||
|
/* it under the terms of the GNU General Public License as published by */
|
||||||
|
/* the Free Software Foundation; either version 3 of the License */
|
||||||
|
/* */
|
||||||
|
/* This program is distributed in the hope that it will be useful, */
|
||||||
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||||
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||||
|
/* GNU General Public License for more details. */
|
||||||
|
/* */
|
||||||
|
/* You should have received a copy of the GNU General Public License */
|
||||||
|
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
/* */
|
||||||
|
/*************************************************************************************/
|
||||||
|
namespace Thelia\Form;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraints\GreaterThan;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Thelia\Model\Currency;
|
||||||
|
use Thelia\Core\Translation\Translator;
|
||||||
|
|
||||||
|
class ProductCombinationGenerationForm extends BaseForm
|
||||||
|
{
|
||||||
|
protected function buildForm()
|
||||||
|
{
|
||||||
|
$this->formBuilder
|
||||||
|
->add('product_id', 'integer', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Product ID'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_id_field'),
|
||||||
|
'constraints' => array(new GreaterThan(array('value' => 0)))
|
||||||
|
))
|
||||||
|
->add('currency', 'integer', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Price currency *'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_currency_field'),
|
||||||
|
'constraints' => array(new GreaterThan(array('value' => 0)))
|
||||||
|
))
|
||||||
|
->add('reference', 'text', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Reference'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_reference_field')
|
||||||
|
))
|
||||||
|
->add('price', 'number', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Product price excluding taxes'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_price_field')
|
||||||
|
))
|
||||||
|
->add('weight', 'number', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Weight'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_weight_field')
|
||||||
|
))
|
||||||
|
->add('quantity', 'number', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Available quantity'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_quantity_field')
|
||||||
|
))
|
||||||
|
->add('sale_price', 'number', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Sale price excluding taxes'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_price_with_tax_field')
|
||||||
|
))
|
||||||
|
->add('onsale', 'integer', array(
|
||||||
|
'label' => Translator::getInstance()->trans('This product is on sale'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_onsale_field')
|
||||||
|
))
|
||||||
|
->add('isnew', 'integer', array(
|
||||||
|
'label' => Translator::getInstance()->trans('Advertise this product as new'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_isnew_field')
|
||||||
|
))
|
||||||
|
->add('ean_code', 'text', array(
|
||||||
|
'label' => Translator::getInstance()->trans('EAN Code'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_ean_code_field')
|
||||||
|
))
|
||||||
|
->add('attribute_av', 'collection', array(
|
||||||
|
'type' => 'text',
|
||||||
|
'label' => Translator::getInstance()->trans('Attribute ID:Attribute AV ID'),
|
||||||
|
'label_attr' => array('for' => 'combination_builder_attribute_av_id'),
|
||||||
|
'allow_add' => true,
|
||||||
|
'allow_delete' => true,
|
||||||
|
))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'thelia_product_combination_generation_form';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator;
|
|||||||
|
|
||||||
class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm
|
class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm
|
||||||
{
|
{
|
||||||
use StandardDescriptionFieldsTrait;
|
|
||||||
|
|
||||||
protected function buildForm()
|
protected function buildForm()
|
||||||
{
|
{
|
||||||
$this->formBuilder
|
$this->formBuilder
|
||||||
@@ -77,7 +75,7 @@ class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm
|
|||||||
"label_attr" => array("for" => "quantity_field")
|
"label_attr" => array("for" => "quantity_field")
|
||||||
))
|
))
|
||||||
->add("sale_price", "number", array(
|
->add("sale_price", "number", array(
|
||||||
"label" => Translator::getInstance()->trans("Sale price without taxes"),
|
"label" => Translator::getInstance()->trans("Sale price excluding taxes"),
|
||||||
"label_attr" => array("for" => "price_with_tax_field")
|
"label_attr" => array("for" => "price_with_tax_field")
|
||||||
))
|
))
|
||||||
->add("sale_price_with_tax", "number", array(
|
->add("sale_price_with_tax", "number", array(
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator;
|
|||||||
|
|
||||||
class ProductSaleElementUpdateForm extends BaseForm
|
class ProductSaleElementUpdateForm extends BaseForm
|
||||||
{
|
{
|
||||||
use StandardDescriptionFieldsTrait;
|
|
||||||
|
|
||||||
protected function buildForm()
|
protected function buildForm()
|
||||||
{
|
{
|
||||||
$this->formBuilder
|
$this->formBuilder
|
||||||
@@ -112,7 +110,7 @@ class ProductSaleElementUpdateForm extends BaseForm
|
|||||||
)
|
)
|
||||||
))
|
))
|
||||||
->add('sale_price', 'collection', array(
|
->add('sale_price', 'collection', array(
|
||||||
'label' => Translator::getInstance()->trans('Sale price without taxes'),
|
'label' => Translator::getInstance()->trans('Sale price excluding taxes'),
|
||||||
'label_attr' => array('for' => 'price_with_tax_field'),
|
'label_attr' => array('for' => 'price_with_tax_field'),
|
||||||
'allow_add' => true,
|
'allow_add' => true,
|
||||||
'allow_delete' => true,
|
'allow_delete' => true,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Attribute extends BaseAttribute {
|
|||||||
$this->dispatchEvent(TheliaEvents::BEFORE_CREATEATTRIBUTE, new AttributeEvent($this));
|
$this->dispatchEvent(TheliaEvents::BEFORE_CREATEATTRIBUTE, new AttributeEvent($this));
|
||||||
|
|
||||||
// Set the current position for the new object
|
// Set the current position for the new object
|
||||||
//$this->setPosition($this->getNextPosition());
|
$this->setPosition($this->getNextPosition());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Feature extends BaseFeature {
|
|||||||
$this->dispatchEvent(TheliaEvents::BEFORE_CREATEFEATURE, new FeatureEvent($this));
|
$this->dispatchEvent(TheliaEvents::BEFORE_CREATEFEATURE, new FeatureEvent($this));
|
||||||
|
|
||||||
// Set the current position for the new object
|
// Set the current position for the new object
|
||||||
//$this->setPosition($this->getNextPosition());
|
$this->setPosition($this->getNextPosition());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class Product extends BaseProduct
|
|||||||
$this->setTaxRuleId($taxRuleId);
|
$this->setTaxRuleId($taxRuleId);
|
||||||
|
|
||||||
// Create the default product sale element of this product
|
// Create the default product sale element of this product
|
||||||
$sale_elements = $this->createDefaultProductSaleElement($con, $baseWeight, $basePrice, $priceCurrencyId, true);
|
$sale_elements = $this->createProductSaleElement($con, $baseWeight, $basePrice, $basePrice, $priceCurrencyId, true);
|
||||||
|
|
||||||
// Store all the stuff !
|
// Store all the stuff !
|
||||||
$con->commit();
|
$con->commit();
|
||||||
@@ -185,19 +185,20 @@ class Product extends BaseProduct
|
|||||||
/**
|
/**
|
||||||
* Create a basic product sale element attached to this product.
|
* Create a basic product sale element attached to this product.
|
||||||
*/
|
*/
|
||||||
public function createDefaultProductSaleElement(ConnectionInterface $con, $weight, $basePrice, $currencyId, $isDefault) {
|
public function createProductSaleElement(ConnectionInterface $con, $weight, $basePrice, $salePrice, $currencyId, $isDefault, $isPromo = false, $isNew = false, $quantity = 0, $eanCode = '', $ref = false) {
|
||||||
|
|
||||||
// Create an empty product sale element
|
// Create an empty product sale element
|
||||||
$sale_elements = new ProductSaleElements();
|
$sale_elements = new ProductSaleElements();
|
||||||
|
|
||||||
$sale_elements
|
$sale_elements
|
||||||
->setProduct($this)
|
->setProduct($this)
|
||||||
->setRef($this->getRef())
|
->setRef($ref == false ? $this->getRef() : $ref)
|
||||||
->setPromo(0)
|
->setPromo($isPromo)
|
||||||
->setNewness(0)
|
->setNewness($isNew)
|
||||||
->setWeight($weight)
|
->setWeight($weight)
|
||||||
->setIsDefault($isDefault)
|
->setIsDefault($isDefault)
|
||||||
->setEanCode('')
|
->setEanCode($eanCode)
|
||||||
|
->setQuantity($quantity)
|
||||||
->save($con)
|
->save($con)
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ class Product extends BaseProduct
|
|||||||
|
|
||||||
$product_price
|
$product_price
|
||||||
->setProductSaleElements($sale_elements)
|
->setProductSaleElements($sale_elements)
|
||||||
->setPromoPrice($basePrice)
|
->setPromoPrice($salePrice)
|
||||||
->setPrice($basePrice)
|
->setPrice($basePrice)
|
||||||
->setCurrencyId($currencyId)
|
->setCurrencyId($currencyId)
|
||||||
->save($con)
|
->save($con)
|
||||||
|
|||||||
@@ -9,7 +9,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Body (where all modal content resides)
|
// Body (where all modal content resides)
|
||||||
.modal-body {
|
.modal-body {
|
||||||
max-height: none;
|
max-height: none;
|
||||||
|
.scrollable {
|
||||||
|
border: 1px solid @input-border;
|
||||||
|
border-radius: @input-border-radius;
|
||||||
|
height: 458px;
|
||||||
|
overflow: auto;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,13 @@ $('#{$dialog_id}').on('hidden.bs.modal', function() {
|
|||||||
// Clear error status
|
// Clear error status
|
||||||
$("#{$dialog_id} .error").removeClass('error');
|
$("#{$dialog_id} .error").removeClass('error');
|
||||||
|
|
||||||
|
$('#{$dialog_id} .form-group').removeClass('has-error')
|
||||||
|
|
||||||
// Empty field values
|
// Empty field values
|
||||||
$("#{$dialog_id} input[type=text], #{$dialog_id} select").val('');
|
$("#{$dialog_id} input[type=text], #{$dialog_id} select").val('');
|
||||||
|
|
||||||
|
// Uncheck boxes
|
||||||
|
$("#{$dialog_id} input[type=checkbox]").removeAttr('checked');
|
||||||
|
|
||||||
|
{$additionnal_js_code|default:''}
|
||||||
});
|
});
|
||||||
@@ -26,9 +26,8 @@
|
|||||||
close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}"
|
close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}"
|
||||||
}
|
}
|
||||||
|
|
||||||
{* Be sure to get the product ID, even if the form could not be validated *}
|
{* Be sure to get the product ID and current tab, even if the form could not be validated *}
|
||||||
<input type="hidden" name="product_id" value="{$product_id}" />
|
<input type="hidden" name="product_id" value="{$product_id}" />
|
||||||
|
|
||||||
<input type="hidden" name="current_tab" value="details" />
|
<input type="hidden" name="current_tab" value="details" />
|
||||||
|
|
||||||
{form_hidden_fields form=$form}
|
{form_hidden_fields form=$form}
|
||||||
@@ -177,8 +176,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{/form_field}
|
{/form_field}
|
||||||
|
|
||||||
{module_include location='product_details_shipping_form'}
|
|
||||||
|
|
||||||
{form_field form=$form field='quantity'}
|
{form_field form=$form field='quantity'}
|
||||||
<div class="form-group {if $error}has-error{/if}">
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
|
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
|
||||||
@@ -189,7 +186,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/form_field}
|
{/form_field}
|
||||||
|
|
||||||
{module_include location='product_details_quantity_form'}
|
{module_include location='product_details_details_form'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -268,9 +265,8 @@
|
|||||||
close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}"
|
close_url = "{url path='/admin/categories' category_id=$DEFAULT_CATEGORY}"
|
||||||
}
|
}
|
||||||
|
|
||||||
{* Be sure to get the product ID, even if the form could not be validated *}
|
{* Be sure to get the product ID and current tab, even if the form could not be validated *}
|
||||||
<input type="hidden" name="product_id" value="{$product_id}" />
|
<input type="hidden" name="product_id" value="{$product_id}" />
|
||||||
|
|
||||||
<input type="hidden" name="current_tab" value="details" />
|
<input type="hidden" name="current_tab" value="details" />
|
||||||
|
|
||||||
{form_hidden_fields form=$form}
|
{form_hidden_fields form=$form}
|
||||||
@@ -340,7 +336,7 @@
|
|||||||
{module_include location='product_combinations_list_caption'}
|
{module_include location='product_combinations_list_caption'}
|
||||||
|
|
||||||
{loop type="auth" name="can_create" role="ADMIN" resource="admin.product" access="UPDATE"}
|
{loop type="auth" name="can_create" role="ADMIN" resource="admin.product" access="UPDATE"}
|
||||||
<a class="btn btn-default btn-primary action-btn" title="{intl l='Quickly create combinations using the combination builder'}" href="#combination_generator_dialog" data-toggle="modal">
|
<a class="btn btn-default btn-primary action-btn" id="open_combination_builder" title="{intl l='Quickly create combinations using the combination builder'}" href="#combination_builder_dialog" data-toggle="modal">
|
||||||
{intl l='Combination builder'}
|
{intl l='Combination builder'}
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new combination'}" href="#combination_creation_dialog" data-toggle="modal">
|
<a class="btn btn-default btn-primary action-btn" title="{intl l='Add a new combination'}" href="#combination_creation_dialog" data-toggle="modal">
|
||||||
@@ -358,8 +354,8 @@
|
|||||||
<th class="text-center">{intl l='Price<br />w/ taxes (%currency)' currency=$currency_symbol}</th>
|
<th class="text-center">{intl l='Price<br />w/ taxes (%currency)' currency=$currency_symbol}</th>
|
||||||
<th class="text-center">{intl l='Weight<br />(Kg)'}</th>
|
<th class="text-center">{intl l='Weight<br />(Kg)'}</th>
|
||||||
<th class="text-center">{intl l='Default'}</th>
|
<th class="text-center">{intl l='Default'}</th>
|
||||||
|
<th class="text-center">{intl l='Sale'}</th>
|
||||||
<th class="text-center">{intl l='New'}</th>
|
<th class="text-center">{intl l='New'}</th>
|
||||||
<th class="text-center">{intl l='Sale'}</th>
|
|
||||||
<th class="text-center">{intl l='Sale price<br />w/o taxes (%currency)' currency=$currency_symbol}</th>
|
<th class="text-center">{intl l='Sale price<br />w/o taxes (%currency)' currency=$currency_symbol}</th>
|
||||||
<th class="text-center">{intl l='Sale price<br />w/ taxes (%currency)' currency=$currency_symbol}</th>
|
<th class="text-center">{intl l='Sale price<br />w/ taxes (%currency)' currency=$currency_symbol}</th>
|
||||||
<th class="actions"> </th>
|
<th class="actions"> </th>
|
||||||
@@ -375,18 +371,20 @@
|
|||||||
|
|
||||||
{for $idx = 0 to $total_value_count-1}
|
{for $idx = 0 to $total_value_count-1}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="12">
|
<td colspan="10">
|
||||||
|
|
||||||
{form_field form=$form field='product_sale_element_id' value_key=$idx}
|
{form_field form=$form field='product_sale_element_id' value_key=$idx}
|
||||||
<input type="hidden" name="{$name}" value="{$value}" />
|
<input type="hidden" name="{$name}" value="{$value}" />
|
||||||
|
|
||||||
{$current_pse_id = $value}
|
{$current_pse_id = $value}
|
||||||
|
|
||||||
{$current_pse_id}: {loop name="product.sales.elements.combinations" type="attribute_combination" product_sale_elements=$current_pse_id backend_context="1"}
|
{loop name="product.sales.elements.combinations" type="attribute_combination" product_sale_elements=$current_pse_id backend_context="1"}
|
||||||
{if $LOOP_COUNT > 1} - {/if}{$ATTRIBUTE_TITLE}
|
{if $LOOP_COUNT > 1} - {/if}{$ATTRIBUTE_TITLE}
|
||||||
{/loop}
|
{/loop}
|
||||||
{/form_field}
|
{/form_field}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<td colspan"2" class="text-right">ID: {$current_pse_id}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
@@ -469,10 +467,16 @@
|
|||||||
<p class="title title-without-tabs">{intl l='Attribute Combinations'}</p>
|
<p class="title title-without-tabs">{intl l='Attribute Combinations'}</p>
|
||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
{intl
|
<p>{intl
|
||||||
l='This product has no combination. The default price is used. <a data-toggle="modal" href="%url">Click here to create a new combination</a>'
|
l='This product has no combination. The default price is used. <a data-toggle="modal" href="%url">Click here to create a new combination</a>.'
|
||||||
url='#combination_creation_dialog'
|
url='#combination_creation_dialog'
|
||||||
}
|
}</p>
|
||||||
|
<p>
|
||||||
|
{intl
|
||||||
|
l='You may also quickly create combinations from products attributes using the <a href="%url" data-toggle="modal">Combination Builder</a>.'
|
||||||
|
url='#combination_builder_dialog'
|
||||||
|
}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -494,7 +498,7 @@
|
|||||||
<label class="control-label">{intl l="Attribute"} : </label>
|
<label class="control-label">{intl l="Attribute"} : </label>
|
||||||
<select name="attribute_id" id="attribute_id" class="form-control">
|
<select name="attribute_id" id="attribute_id" class="form-control">
|
||||||
<option value="">{intl l='Select an attribute...'}</option>
|
<option value="">{intl l='Select an attribute...'}</option>
|
||||||
{loop name="product-attributes" type="attribute" product=$product_id backend_context="1" lang=$edit_language_id}
|
{loop name="product-attributes" type="attribute" order="manual" product=$product_id backend_context="1" lang=$edit_language_id}
|
||||||
<option value="{$ID}">{$TITLE}</option>
|
<option value="{$ID}">{$TITLE}</option>
|
||||||
{/loop}
|
{/loop}
|
||||||
</select>
|
</select>
|
||||||
@@ -583,3 +587,184 @@
|
|||||||
form_action = {url path='/admin/product/combination/delete'}
|
form_action = {url path='/admin/product/combination/delete'}
|
||||||
form_content = {$smarty.capture.combination_delete_dialog nofilter}
|
form_content = {$smarty.capture.combination_delete_dialog nofilter}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{* -- Combination builder dialog -------------------------------------------- *}
|
||||||
|
|
||||||
|
{* Capture the dialog body, to pass it to the generic dialog *}
|
||||||
|
|
||||||
|
{form name="thelia.admin.product_combination.build"}
|
||||||
|
|
||||||
|
{capture "combination_builder_dialog"}
|
||||||
|
|
||||||
|
{* Be sure to get the product ID and current tab, even if the form could not be validated *}
|
||||||
|
<input type="hidden" name="product_id" value="{$product_id}" />
|
||||||
|
<input type="hidden" name="current_tab" value="details" />
|
||||||
|
|
||||||
|
{form_hidden_fields form=$form}
|
||||||
|
|
||||||
|
{form_field form=$form field='product_id'}
|
||||||
|
<input type="hidden" name="{$name}" value="{$product_id}" />
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
{if $form_error}<div class="alert alert-danger" id="combination_builder_dialog_error">{$form_error_message}</div>{/if}
|
||||||
|
|
||||||
|
{loop type="currency" name="get-currency-symbol" id=$edit_currency_id backend_context="1"}
|
||||||
|
{$currency_symbol = $SYMBOL}
|
||||||
|
|
||||||
|
{form_field form=$form field='currency'}
|
||||||
|
<input type="hidden" name="{$name}" value="{$ID}" />
|
||||||
|
{/form_field}
|
||||||
|
{/loop}
|
||||||
|
|
||||||
|
{form_field form=$form field='success_url'}
|
||||||
|
<input type="hidden" name="{$name}" value="{url path='/admin/products/update' product_id=$product_id current_tab='details'}" />
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
<div class="alert alert-info">
|
||||||
|
{intl l='Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="scrollable">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{$index = 0}
|
||||||
|
{loop name="product-attributes" type="attribute" order="manual" product=$product_id backend_context="1" lang=$edit_language_id}
|
||||||
|
{ifloop rel="product-attributes-av"}
|
||||||
|
<li>
|
||||||
|
<strong>{$TITLE}</strong>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{loop name="product-attributes-av" type="attribute_availability" attribute="{$ID}" order="manual" backend_context="1" lang=$edit_language_id}
|
||||||
|
<li>
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
{form_field form=$form field='attribute_av' value_key=$index}
|
||||||
|
<input data-attribute-id="{$ATTRIBUTE_ID}" class="attribute_av_value" type="checkbox" name="{$name}" value="{$ATTRIBUTE_ID}:{$ID}" {if $value == "$ATTRIBUTE_ID:$ID"}checked="checked"{/if}>{$TITLE}
|
||||||
|
{/form_field}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{$index = $index + 1}
|
||||||
|
{/loop}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{/ifloop}
|
||||||
|
{/loop}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
{form_field form=$form field='price'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<label for="price_without_tax" class="control-label">{$label} : </label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="price_without_tax" name="{$name}" class="price_field automatic_price_field form-control" value="{$value}" title="{$label}" placeholder="{intl l='Price excl. taxes'}">
|
||||||
|
<span class="input-group-addon">{$currency_symbol}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
{form_field form=$form field='reference'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Combination reference'}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
{form_field form=$form field='ean_code'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Combination EAN Code'}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
{form_field form=$form field='weight'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<label for="{$label_attr.for}" class="control-label">{$label} : </label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Product weight'}">
|
||||||
|
<span class="input-group-addon">{intl l="Kg"}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
{form_field form=$form field='quantity'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<label for="{$label_attr.for}" class="control-label">{intl l='Quantity'} : </label>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" id="{$label_attr.for}" name="{$name}" class="form-control" value="{$value}" title="{$label}" placeholder="{intl l='Current quantity'}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{form_field form=$form field='sale_price'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<label for="sale_price_without_tax" class="control-label">{$label} : </label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="sale_price_without_tax" name="{$name}" class="price_field automatic_price_field form-control" value="{$value}" title="{$label}" placeholder="{intl l='Product price'}">
|
||||||
|
<span class="input-group-addon">{$currency_symbol}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
{form_field form=$form field='onsale'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="{$label_attr.for}" name="{$name}" value="1">
|
||||||
|
{$label}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
{form_field form=$form field='isnew'}
|
||||||
|
<div class="form-group {if $error}has-error{/if}">
|
||||||
|
<div class="checkbox">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="{$label_attr.for}" name="{$name}" value="1">
|
||||||
|
{$label}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/form_field}
|
||||||
|
|
||||||
|
<div class="well well-sm" style="margin-bottom: 0"><b>{intl l='<span id="number_of_generated_combinations">0</span> combinations'}</b></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/capture}
|
||||||
|
|
||||||
|
{include
|
||||||
|
file = "includes/generic-create-dialog.html"
|
||||||
|
|
||||||
|
dialog_id = "combination_builder_dialog"
|
||||||
|
dialog_title = {intl l="Create combinations"}
|
||||||
|
dialog_body = {$smarty.capture.combination_builder_dialog nofilter}
|
||||||
|
|
||||||
|
dialog_ok_label = {intl l="Create combinations"}
|
||||||
|
|
||||||
|
form_action = {url path='/admin/product/combination/build'}
|
||||||
|
form_enctype = {form_enctype form=$form}
|
||||||
|
form_error_message = ''
|
||||||
|
|
||||||
|
ok_button_id = "combination_builder_dialog_ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
{/form}
|
||||||
|
|||||||
@@ -344,6 +344,21 @@ $(function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- Combination builder stuff --------------------------------------------
|
||||||
|
|
||||||
|
$('#open_combination_builder').click(function(ev) {
|
||||||
|
if (! confirm("{intl l='Existing combiations will be deleted. Do you want to continue ?'}'")) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
{include
|
||||||
|
file = "includes/generic-js-dialog.html"
|
||||||
|
dialog_id = "combination_builder_dialog"
|
||||||
|
form_name = "thelia.admin.product_combination.build"
|
||||||
|
}
|
||||||
|
|
||||||
// Automatic update of price fields: any change in the taxed (resp. untaxed) price
|
// Automatic update of price fields: any change in the taxed (resp. untaxed) price
|
||||||
// will update the untaxed (resp. taxed) one
|
// will update the untaxed (resp. taxed) one
|
||||||
$('.automatic_price_field').typeWatch({
|
$('.automatic_price_field').typeWatch({
|
||||||
@@ -353,6 +368,47 @@ $(function() {
|
|||||||
update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price'));
|
update_price($(this).val(), $(this).data('price-type'), $(this).data('rel-price'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Count generated combinations in real time
|
||||||
|
function countGeneratedCombinations() {
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
|
||||||
|
var counter = {};
|
||||||
|
|
||||||
|
var list = $('.attribute_av_value:checked');
|
||||||
|
|
||||||
|
if (list.length > 0) {
|
||||||
|
console.log("ok !");
|
||||||
|
|
||||||
|
list.each(function() {
|
||||||
|
var attr_id = $(this).data('attribute-id');
|
||||||
|
|
||||||
|
console.log("att="+attr_id);
|
||||||
|
|
||||||
|
if (undefined != counter[attr_id])
|
||||||
|
counter[attr_id]++;
|
||||||
|
else
|
||||||
|
counter[attr_id] = 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(counter);
|
||||||
|
|
||||||
|
total = 1;
|
||||||
|
|
||||||
|
for(var count in counter) {
|
||||||
|
total *= counter[count];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.attribute_av_value').change(function(ev) {
|
||||||
|
var total = countGeneratedCombinations();
|
||||||
|
|
||||||
|
$('#number_of_generated_combinations').text(total);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user