diff --git a/core/lib/Thelia/Action/Attribute.php b/core/lib/Thelia/Action/Attribute.php index 7db04518a..30ceea0e8 100644 --- a/core/lib/Thelia/Action/Attribute.php +++ b/core/lib/Thelia/Action/Attribute.php @@ -63,7 +63,7 @@ class Attribute extends BaseAction implements EventSubscriberInterface // Add atribute to all product templates if required if ($event->getAddToAllTemplates() != 0) { - // TODO: add to all product template + $this->doAddToAllTemplates($attribute); } } diff --git a/core/lib/Thelia/Action/Feature.php b/core/lib/Thelia/Action/Feature.php index 6ae7645e3..36853f444 100644 --- a/core/lib/Thelia/Action/Feature.php +++ b/core/lib/Thelia/Action/Feature.php @@ -63,7 +63,7 @@ class Feature extends BaseAction implements EventSubscriberInterface // Add atribute to all product templates if required if ($event->getAddToAllTemplates() != 0) { - // TODO: add to all product template + $this->doAddToAllTemplates($feature); } } diff --git a/core/lib/Thelia/Action/ProductSaleElement.php b/core/lib/Thelia/Action/ProductSaleElement.php index 8349eb1bf..9c40025d8 100644 --- a/core/lib/Thelia/Action/ProductSaleElement.php +++ b/core/lib/Thelia/Action/ProductSaleElement.php @@ -40,6 +40,8 @@ use Thelia\Model\AttributeAvQuery; use Thelia\Model\Currency; use Thelia\Model\Map\AttributeCombinationTableMap; use Propel\Runtime\ActiveQuery\Criteria; +use Thelia\Core\Event\Product\ProductCombinationGenerationEvent; +use Propel\Runtime\Connection\ConnectionInterface; class ProductSaleElement extends BaseAction implements EventSubscriberInterface { @@ -65,7 +67,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface if ($salesElement == null) { // 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 { // This (new) one is the default $salesElement->setIsDefault(true)->save($con); @@ -87,7 +89,7 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface ->setAttributeAvId($attributeAvId) ->setAttribute($attributeAv->getAttribute()) ->setProductSaleElements($salesElement) - ->save(); + ->save($con); } } } @@ -206,8 +208,9 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface if ($product->countSaleElements() <= 0) { // If we just deleted the last PSE, create a default one - $product->createDefaultProductSaleElement($con, 0, 0, $event->getCurrencyId(), true); - } elseif ($pse->getIsDefault()) { + $product->createProductSaleElement($con, 0, 0, 0, $event->getCurrencyId(), true); + } + elseif ($pse->getIsDefault()) { // If we deleted the default PSE, make the last created one the default $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} */ @@ -239,6 +319,8 @@ class ProductSaleElement extends BaseAction implements EventSubscriberInterface TheliaEvents::PRODUCT_ADD_PRODUCT_SALE_ELEMENT => array("create", 128), TheliaEvents::PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT => array("update", 128), TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT => array("delete", 128), + TheliaEvents::PRODUCT_COMBINATION_GENERATION => array("generateCombinations", 128), + ); } } diff --git a/core/lib/Thelia/Config/Resources/config.xml b/core/lib/Thelia/Config/Resources/config.xml index c0af99896..84e9ef92e 100755 --- a/core/lib/Thelia/Config/Resources/config.xml +++ b/core/lib/Thelia/Config/Resources/config.xml @@ -90,6 +90,8 @@
+ + diff --git a/core/lib/Thelia/Config/Resources/routing/admin.xml b/core/lib/Thelia/Config/Resources/routing/admin.xml index dc5a2c522..64bf37541 100755 --- a/core/lib/Thelia/Config/Resources/routing/admin.xml +++ b/core/lib/Thelia/Config/Resources/routing/admin.xml @@ -372,6 +372,10 @@ Thelia\Controller\Admin\ProductController::updateProductSaleElementsAction + + Thelia\Controller\Admin\ProductController::buildCombinationsAction + + Thelia\Controller\Admin\ProductController::updateProductDefaultSaleElementAction diff --git a/core/lib/Thelia/Controller/Admin/FeatureController.php b/core/lib/Thelia/Controller/Admin/FeatureController.php index 7696b9c6e..1be5dfeec 100644 --- a/core/lib/Thelia/Controller/Admin/FeatureController.php +++ b/core/lib/Thelia/Controller/Admin/FeatureController.php @@ -157,23 +157,6 @@ class FeatureController extends AbstractCrudController '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 return new FeatureModificationForm($this->getRequest(), "form", $data); } diff --git a/core/lib/Thelia/Controller/Admin/ProductController.php b/core/lib/Thelia/Controller/Admin/ProductController.php index 84e16815d..5540c529b 100644 --- a/core/lib/Thelia/Controller/Admin/ProductController.php +++ b/core/lib/Thelia/Controller/Admin/ProductController.php @@ -64,6 +64,8 @@ use Thelia\Model\Country; use Thelia\Tools\NumberFormat; use Thelia\Model\Product; use Thelia\Model\CurrencyQuery; +use Thelia\Form\ProductCombinationGenerationForm; +use Thelia\Core\Event\Product\ProductCombinationGenerationEvent; /** * 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 * vice versa. diff --git a/core/lib/Thelia/Core/Event/Product/ProductCombinationGenerationEvent.php b/core/lib/Thelia/Core/Event/Product/ProductCombinationGenerationEvent.php new file mode 100644 index 000000000..05b55d733 --- /dev/null +++ b/core/lib/Thelia/Core/Event/Product/ProductCombinationGenerationEvent.php @@ -0,0 +1,159 @@ +. */ +/* */ +/*************************************************************************************/ + +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; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Core/Event/TheliaEvents.php b/core/lib/Thelia/Core/Event/TheliaEvents.php index 42b20d8cd..dfd2d27aa 100755 --- a/core/lib/Thelia/Core/Event/TheliaEvents.php +++ b/core/lib/Thelia/Core/Event/TheliaEvents.php @@ -285,6 +285,8 @@ final class TheliaEvents const PRODUCT_DELETE_PRODUCT_SALE_ELEMENT = "action.deleteProductSaleElement"; const PRODUCT_UPDATE_PRODUCT_SALE_ELEMENT = "action.updateProductSaleElement"; + const PRODUCT_COMBINATION_GENERATION = "action.productCombineationGeneration"; + const PRODUCT_SET_TEMPLATE = "action.productSetTemplate"; const PRODUCT_ADD_ACCESSORY = "action.productAddProductAccessory"; diff --git a/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php b/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php index e9a7d9eb8..10c1dda00 100755 --- a/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php +++ b/core/lib/Thelia/Core/Template/Loop/AttributeAvailability.php @@ -128,14 +128,17 @@ class AttributeAvailability extends BaseI18nLoop foreach ($attributesAv as $attributeAv) { $loopResultRow = new LoopResultRow($loopResult, $attributeAv, $this->versionable, $this->timestampable, $this->countable); - $loopResultRow->set("ID", $attributeAv->getId()) - ->set("IS_TRANSLATED",$attributeAv->getVirtualColumn('IS_TRANSLATED')) - ->set("LOCALE",$locale) - ->set("TITLE",$attributeAv->getVirtualColumn('i18n_TITLE')) - ->set("CHAPO", $attributeAv->getVirtualColumn('i18n_CHAPO')) - ->set("DESCRIPTION", $attributeAv->getVirtualColumn('i18n_DESCRIPTION')) - ->set("POSTSCRIPTUM", $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM')) - ->set("POSITION", $attributeAv->getPosition()); + $loopResultRow + ->set("ID" , $attributeAv->getId()) + ->set("ATTRIBUTE_ID" , $attributeAv->getAttributeId()) + ->set("IS_TRANSLATED", $attributeAv->getVirtualColumn('IS_TRANSLATED')) + ->set("LOCALE" , $locale) + ->set("TITLE" , $attributeAv->getVirtualColumn('i18n_TITLE')) + ->set("CHAPO" , $attributeAv->getVirtualColumn('i18n_CHAPO')) + ->set("DESCRIPTION" , $attributeAv->getVirtualColumn('i18n_DESCRIPTION')) + ->set("POSTSCRIPTUM" , $attributeAv->getVirtualColumn('i18n_POSTSCRIPTUM')) + ->set("POSITION" , $attributeAv->getPosition()) + ; $loopResult->addRow($loopResultRow); } diff --git a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php index 494e219eb..ee49fe1ec 100755 --- a/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php +++ b/core/lib/Thelia/Core/Template/Smarty/Plugins/Form.php @@ -199,30 +199,29 @@ class Form extends AbstractSmartyPlugin { if ($repeat) { - $formFieldView = $this->getFormFieldView($params); - $formFieldConfig = $this->getFormFieldConfig($params); + $formFieldView = $this->getFormFieldView($params); + $formFieldConfig = $this->getFormFieldConfig($params); - $this->assignFormTypeValues($template, $formFieldConfig, $formFieldView); + $this->assignFormTypeValues($template, $formFieldConfig, $formFieldView); $value = $formFieldView->vars["value"]; - // We have a collection - if (0 < $value_count = count($formFieldView->children)) { + $key = $this->getParam($params, 'value_key', null); - $key = $this->getParam($params, 'value_key', null); + // We (may) have a collection + if ($key !== null) { - if ($key !== null) { - // If the field is not found, use an empty value - $val = array_key_exists($key, $value) ? $value[$key] : ''; + // Force array + if (! is_array($value)) $value = array(); - $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); - } else { - throw new \InvalidArgumentException(sprintf("Missing or empty parameter 'value_key' for field '%s'", $formFieldView->vars["name"])); - } + $val = $value[$key]; + + $this->assignFieldValues($template, $name, $val, $formFieldView->vars, count($formFieldView->children)); } else { $this->assignFieldValues($template, $formFieldView->vars["full_name"], $formFieldView->vars["value"], $formFieldView->vars); } diff --git a/core/lib/Thelia/Form/ProductCombinationGenerationForm.php b/core/lib/Thelia/Form/ProductCombinationGenerationForm.php new file mode 100644 index 000000000..8e4b155b9 --- /dev/null +++ b/core/lib/Thelia/Form/ProductCombinationGenerationForm.php @@ -0,0 +1,91 @@ +. */ +/* */ +/*************************************************************************************/ +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'; + } +} \ No newline at end of file diff --git a/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php b/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php index 2643692a2..54fb2fdc1 100644 --- a/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php +++ b/core/lib/Thelia/Form/ProductDefaultSaleElementUpdateForm.php @@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator; class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm { - use StandardDescriptionFieldsTrait; - protected function buildForm() { $this->formBuilder @@ -77,7 +75,7 @@ class ProductDefaultSaleElementUpdateForm extends ProductSaleElementUpdateForm "label_attr" => array("for" => "quantity_field") )) ->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") )) ->add("sale_price_with_tax", "number", array( diff --git a/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php b/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php index da831545a..cda72cc72 100644 --- a/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php +++ b/core/lib/Thelia/Form/ProductSaleElementUpdateForm.php @@ -29,8 +29,6 @@ use Thelia\Core\Translation\Translator; class ProductSaleElementUpdateForm extends BaseForm { - use StandardDescriptionFieldsTrait; - protected function buildForm() { $this->formBuilder @@ -112,7 +110,7 @@ class ProductSaleElementUpdateForm extends BaseForm ) )) ->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'), 'allow_add' => true, 'allow_delete' => true, diff --git a/core/lib/Thelia/Model/Attribute.php b/core/lib/Thelia/Model/Attribute.php index 615fa643e..3439cac06 100755 --- a/core/lib/Thelia/Model/Attribute.php +++ b/core/lib/Thelia/Model/Attribute.php @@ -20,7 +20,7 @@ class Attribute extends BaseAttribute { $this->dispatchEvent(TheliaEvents::BEFORE_CREATEATTRIBUTE, new AttributeEvent($this)); // Set the current position for the new object - //$this->setPosition($this->getNextPosition()); + $this->setPosition($this->getNextPosition()); return true; } diff --git a/core/lib/Thelia/Model/Feature.php b/core/lib/Thelia/Model/Feature.php index 800ff832a..2627d5190 100755 --- a/core/lib/Thelia/Model/Feature.php +++ b/core/lib/Thelia/Model/Feature.php @@ -20,7 +20,7 @@ class Feature extends BaseFeature { $this->dispatchEvent(TheliaEvents::BEFORE_CREATEFEATURE, new FeatureEvent($this)); // Set the current position for the new object - //$this->setPosition($this->getNextPosition()); + $this->setPosition($this->getNextPosition()); return true; } diff --git a/core/lib/Thelia/Model/Product.php b/core/lib/Thelia/Model/Product.php index f16a8a776..1b3f1eefa 100755 --- a/core/lib/Thelia/Model/Product.php +++ b/core/lib/Thelia/Model/Product.php @@ -167,7 +167,7 @@ class Product extends BaseProduct $this->setTaxRuleId($taxRuleId); // 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 ! $con->commit(); @@ -185,19 +185,20 @@ class Product extends BaseProduct /** * 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 $sale_elements = new ProductSaleElements(); $sale_elements ->setProduct($this) - ->setRef($this->getRef()) - ->setPromo(0) - ->setNewness(0) + ->setRef($ref == false ? $this->getRef() : $ref) + ->setPromo($isPromo) + ->setNewness($isNew) ->setWeight($weight) ->setIsDefault($isDefault) - ->setEanCode('') + ->setEanCode($eanCode) + ->setQuantity($quantity) ->save($con) ; @@ -206,7 +207,7 @@ class Product extends BaseProduct $product_price ->setProductSaleElements($sale_elements) - ->setPromoPrice($basePrice) + ->setPromoPrice($salePrice) ->setPrice($basePrice) ->setCurrencyId($currencyId) ->save($con) diff --git a/templates/admin/default/assets/less/thelia/modals.less b/templates/admin/default/assets/less/thelia/modals.less index 2d371e6c0..ee6b3c662 100755 --- a/templates/admin/default/assets/less/thelia/modals.less +++ b/templates/admin/default/assets/less/thelia/modals.less @@ -9,7 +9,22 @@ } } +.modal-header { + h3 { + margin-bottom: 0; + } +} + // Body (where all modal content resides) .modal-body { 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; + } } \ No newline at end of file diff --git a/templates/admin/default/includes/generic-js-dialog.html b/templates/admin/default/includes/generic-js-dialog.html index 891bb0957..f12a64664 100644 --- a/templates/admin/default/includes/generic-js-dialog.html +++ b/templates/admin/default/includes/generic-js-dialog.html @@ -30,6 +30,13 @@ $('#{$dialog_id}').on('hidden.bs.modal', function() { // Clear error status $("#{$dialog_id} .error").removeClass('error'); + $('#{$dialog_id} .form-group').removeClass('has-error') + // Empty field values $("#{$dialog_id} input[type=text], #{$dialog_id} select").val(''); + + // Uncheck boxes + $("#{$dialog_id} input[type=checkbox]").removeAttr('checked'); + + {$additionnal_js_code|default:''} }); \ No newline at end of file diff --git a/templates/admin/default/includes/product-details-tab.html b/templates/admin/default/includes/product-details-tab.html index 67b20ab77..096b0a7e7 100644 --- a/templates/admin/default/includes/product-details-tab.html +++ b/templates/admin/default/includes/product-details-tab.html @@ -26,9 +26,8 @@ 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 *} - {form_hidden_fields form=$form} @@ -177,8 +176,6 @@ {/form_field} - {module_include location='product_details_shipping_form'} - {form_field form=$form field='quantity'}
@@ -189,7 +186,7 @@
{/form_field} - {module_include location='product_details_quantity_form'} + {module_include location='product_details_details_form'} @@ -268,9 +265,8 @@ 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 *} - {form_hidden_fields form=$form} @@ -340,7 +336,7 @@ {module_include location='product_combinations_list_caption'} {loop type="auth" name="can_create" role="ADMIN" resource="admin.product" access="UPDATE"} - + {intl l='Combination builder'} @@ -358,8 +354,8 @@ {intl l='Price
w/ taxes (%currency)' currency=$currency_symbol} {intl l='Weight
(Kg)'} {intl l='Default'} + {intl l='Sale'} {intl l='New'} - {intl l='Sale'} {intl l='Sale price
w/o taxes (%currency)' currency=$currency_symbol} {intl l='Sale price
w/ taxes (%currency)' currency=$currency_symbol}   @@ -375,18 +371,20 @@ {for $idx = 0 to $total_value_count-1} - + {form_field form=$form field='product_sale_element_id' value_key=$idx} {$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} {/loop} {/form_field} + + ID: {$current_pse_id} @@ -469,10 +467,16 @@

{intl l='Attribute Combinations'}

- {intl - l='This product has no combination. The default price is used. Click here to create a new combination' - url='#combination_creation_dialog' - } +

{intl + l='This product has no combination. The default price is used. Click here to create a new combination.' + url='#combination_creation_dialog' + }

+

+ {intl + l='You may also quickly create combinations from products attributes using the Combination Builder.' + url='#combination_builder_dialog' + } +

@@ -494,7 +498,7 @@ @@ -583,3 +587,184 @@ form_action = {url path='/admin/product/combination/delete'} 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 *} + + + + {form_hidden_fields form=$form} + + {form_field form=$form field='product_id'} + + {/form_field} + + {if $form_error}
{$form_error_message}
{/if} + + {loop type="currency" name="get-currency-symbol" id=$edit_currency_id backend_context="1"} + {$currency_symbol = $SYMBOL} + + {form_field form=$form field='currency'} + + {/form_field} + {/loop} + + {form_field form=$form field='success_url'} + + {/form_field} + +
+ {intl l='Select attribute values to combine. You may enter a default value for some of the fields of the generated combinations.'} +
+ +
+
+
+
    + {$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"} +
  • + {$TITLE} +
      + {loop name="product-attributes-av" type="attribute_availability" attribute="{$ID}" order="manual" backend_context="1" lang=$edit_language_id} +
    • +
      + +
      +
    • + {$index = $index + 1} + {/loop} +
    +
  • + {/ifloop} + {/loop} +
+
+
+ +
+ {form_field form=$form field='price'} +
+ + +
+ + {$currency_symbol} +
+
+ {/form_field} + + {form_field form=$form field='reference'} +
+ + +
+ +
+
+ {/form_field} + + {form_field form=$form field='ean_code'} +
+ + +
+ +
+
+ {/form_field} + +
+
+ {form_field form=$form field='weight'} +
+ + +
+ + {intl l="Kg"} +
+
+ {/form_field} +
+ +
+ {form_field form=$form field='quantity'} +
+ + +
+ +
+
+ {/form_field} +
+
+ + {form_field form=$form field='sale_price'} +
+ +
+ + {$currency_symbol} +
+
+ {/form_field} + + {form_field form=$form field='onsale'} +
+
+ +
+
+ {/form_field} + + {form_field form=$form field='isnew'} +
+
+ +
+
+ {/form_field} + +
{intl l='0 combinations'}
+
+
+ +{/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} diff --git a/templates/admin/default/product-edit.html b/templates/admin/default/product-edit.html index 446e1fd7f..184fb916b 100644 --- a/templates/admin/default/product-edit.html +++ b/templates/admin/default/product-edit.html @@ -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 // will update the untaxed (resp. taxed) one $('.automatic_price_field').typeWatch({ @@ -353,6 +368,47 @@ $(function() { 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); + }); });