Browse Source

[Administration] Gestion des accessoires

feature/rotating_product
Guillaume Bourgeois 5 months ago
parent
commit
1ecdb52521
25 changed files with 493 additions and 17 deletions
  1. +31
    -11
      backend/controllers/AccessoryController.php
  2. +15
    -0
      backend/views/accessory/_form.php
  3. +1
    -0
      backend/views/accessory/create.php
  4. +13
    -1
      backend/views/accessory/index.php
  5. +1
    -0
      backend/views/accessory/update.php
  6. +0
    -2
      backend/views/product/_form.php
  7. +6
    -0
      backend/web/css/screen.css
  8. +11
    -0
      backend/web/sass/accessory/_form.scss
  9. +1
    -0
      backend/web/sass/screen.scss
  10. +1
    -0
      common/components/BusinessLogic.php
  11. +6
    -0
      common/components/BusinessLogicTrait.php
  12. +31
    -0
      console/migrations/m240620_074924_create_table_product_accessory.php
  13. +26
    -0
      domain/Product/Accessory/Accessory.php
  14. +18
    -0
      domain/Product/Accessory/AccessoryBuilder.php
  15. +20
    -0
      domain/Product/Accessory/AccessoryManager.php
  16. +7
    -0
      domain/Product/Accessory/AccessoryRepository.php
  17. +22
    -0
      domain/Product/Product/ProductManager.php
  18. +9
    -3
      domain/Product/Product/ProductModule.php
  19. +86
    -0
      domain/Product/ProductAccessory/ProductAccessory.php
  20. +15
    -0
      domain/Product/ProductAccessory/ProductAccessoryBuilder.php
  21. +13
    -0
      domain/Product/ProductAccessory/ProductAccessoryDefinition.php
  22. +51
    -0
      domain/Product/ProductAccessory/ProductAccessoryManager.php
  23. +38
    -0
      domain/Product/ProductAccessory/ProductAccessoryModule.php
  24. +42
    -0
      domain/Product/ProductAccessory/ProductAccessoryRepository.php
  25. +29
    -0
      domain/Product/ProductAccessory/ProductAccessoryRepositoryQuery.php

+ 31
- 11
backend/controllers/AccessoryController.php View File

@@ -68,31 +68,31 @@ class AccessoryController extends BackendController

public function actionIndex()
{
$accessoryModule = $this->getAccessoryModule();

return $this->render('index', [
'searchModel' => $accessoryModule->getBuilder()->instanciateAccessory($this->getProducerCurrent()),
'dataProvider' => $accessoryModule->getRepository()->queryAll()->getDataProvider(20),
'dataProvider' => $this->getAccessoryModule()->getRepository()
->queryAccessories()->getDataProvider(20),
]);
}

public function actionCreate()
{
$accessoryModule = $this->getAccessoryModule();
$accessory = $accessoryModule->getBuilder()->instanciateAccessory($this->getProducerCurrent());
$accessoryModel = $accessoryModule->getBuilder()->instanciateAccessory($this->getProducerCurrent());

if ($accessory->load(\Yii::$app->request->post()) && $accessory->validate()) {
if ($accessoryModel->load(\Yii::$app->request->post()) && $accessoryModel->validate()) {
$accessory = $accessoryModule->getManager()->createAccessory(
$this->getProducerCurrent(),
$accessory->getName(),
$accessory->getQuantity()
$accessoryModel->getName(),
$accessoryModel->getQuantity()
);
$this->afterSave($accessory, $accessoryModel->getSelectedProductsIds());
$this->setFlash('success', "Accessoire ajouté");
return $this->redirectAfterSave('accessory', $accessory->getId());
}

return $this->render('create', [
'accessory' => $accessory,
'accessory' => $this->initFormModel($accessoryModel),
'productsArray' => $this->findProducts()
]);
}

@@ -104,12 +104,13 @@ class AccessoryController extends BackendController
$accessory = $this->findAccessory($id);

if ($accessory->load(\Yii::$app->request->post()) && $accessory->validate() && $accessory->save()) {
$this->setFlash('success', "Accessoire modifié");
$this->afterSave($accessory, $accessory->getSelectedProductsIds());
return $this->redirectAfterSave('accessory', $accessory->getId());
}

return $this->render('update', [
'accessory' => $accessory,
'accessory' => $this->initFormModel($accessory),
'productsArray' => $this->findProducts()
]);
}

@@ -128,6 +129,19 @@ class AccessoryController extends BackendController
return $this->redirect('index');
}

public function afterSave(Accessory $accessory, $selectedProductsIdsArray): void
{
if(!is_array($selectedProductsIdsArray)) {
$selectedProductsIdsArray = [];
}
$this->getAccessoryModule()->getManager()->manageProducts($accessory, $selectedProductsIdsArray);
}

public function findProducts(): array
{
return $this->getProductModule()->getRepository()->findProducts(true);
}

/**
* @throws NotFoundHttpException
*/
@@ -139,4 +153,10 @@ class AccessoryController extends BackendController
throw new NotFoundHttpException("L'accessoire n'a pas été trouvé.");
}
}

public function initFormModel(Accessory $accessoryModel)
{
$this->getAccessoryModule()->getBuilder()->initSelectedProductsIds($accessoryModel);
return $accessoryModel;
}
}

+ 15
- 0
backend/views/accessory/_form.php View File

@@ -36,6 +36,8 @@
* termes.
*/

use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\widgets\ActiveForm;

?>
@@ -55,6 +57,19 @@ use yii\widgets\ActiveForm;
<?= $form->field($accessory, 'quantity')->textInput() ?>
</div>
</div>
<div class="panel panel-default" id="panel-products">
<div class="panel-heading">
<h3 class="panel-title">
<i class="fa fa-clone"></i>
Produits
</h3>
</div>
<div class="panel-body">
<?= Html::activeCheckboxList($accessory, 'selected_products_ids', ArrayHelper::map($productsArray, 'id', function ($product, $defaultValue) {
return Html::encode($product->name);
}), ['encode' => false, 'class' => '']) ?>
</div>
</div>
</div>
<?= $this->render('@backend/views/_include/form_actions.php',[
'model' => $accessory,

+ 1
- 0
backend/views/accessory/create.php View File

@@ -45,5 +45,6 @@ $this->addBreadcrumb('Ajouter') ;
<div class="accessory-create">
<?= $this->render('_form', [
'accessory' => $accessory,
'productsArray' => $productsArray
]) ?>
</div>

+ 13
- 1
backend/views/accessory/index.php View File

@@ -52,10 +52,22 @@ $this->addButton(['label' => 'Nouvel accessoire <span class="glyphicon glyphicon

<div class="accessory-index">
<?= GridView::widget([
'filterModel' => $searchModel,
'dataProvider' => $dataProvider,
'columns' => [
'name',
'quantity',
[
'label' => 'Produits',
'format' => 'raw',
'value' => function($accessory) {
$html = '';
foreach($accessory->getProductAccessories() as $productAccessory) {
$html .= '<span class="label label-default">'.Html::encode($productAccessory->getProduct()->name).'</span>&nbsp;';
}

return $html;
}
],
[
'class' => 'yii\grid\ActionColumn',
'template' => '{update} {delete}',

+ 1
- 0
backend/views/accessory/update.php View File

@@ -48,5 +48,6 @@ $this->addBreadcrumb('Modifier') ;
<div class="accessory-update">
<?= $this->render('_form', [
'accessory' => $accessory,
'productsArray' => $productsArray
]) ?>
</div>

+ 0
- 2
backend/views/product/_form.php View File

@@ -236,7 +236,6 @@ $taxRateModule = $this->getTaxRateModule();
</div>
</div>
</div>

</div>
<div class="clr"></div>
</div>
@@ -248,5 +247,4 @@ $taxRateModule = $this->getTaxRateModule();
]); ?>

<?php ActiveForm::end(); ?>

</div>

+ 6
- 0
backend/web/css/screen.css View File

@@ -2308,6 +2308,12 @@ body.login-page .login-box .login-box-body .checkbox label input {
font-weight: normal;
}

/* line 5, ../sass/accessory/_form.scss */
.accessory-form #panel-products .panel-body label {
display: block;
font-weight: normal;
}

/**
Copyright Souke (2018)


+ 11
- 0
backend/web/sass/accessory/_form.scss View File

@@ -0,0 +1,11 @@

.accessory-form {
#panel-products {
.panel-body {
label {
display: block;
font-weight: normal;
}
}
}
}

+ 1
- 0
backend/web/sass/screen.scss View File

@@ -1533,6 +1533,7 @@ a.btn, button.btn {
@import "subscription/_form.scss" ;
@import "product/_index.scss" ;
@import "product/_form.scss" ;
@import "accessory/_form.scss" ;
@import "stats/_products.scss" ;
@import "distribution/_index.scss" ;
@import "user/_emails.scss" ;

+ 1
- 0
common/components/BusinessLogic.php View File

@@ -28,6 +28,7 @@ class BusinessLogic
$this->getUserModule(),
$this->getUserMessageModule(),
$this->getPointSaleDistributionModule(),
$this->getProductAccessoryModule(),
$this->getAccessoryModule(),
$this->getProductDistributionModule(),
$this->getProductCategoryModule(),

+ 6
- 0
common/components/BusinessLogicTrait.php View File

@@ -26,6 +26,7 @@ use domain\Producer\Producer\ProducerModule;
use domain\Producer\ProducerPriceRange\ProducerPriceRangeModule;
use domain\Product\Accessory\AccessoryModule;
use domain\Product\Product\ProductModule;
use domain\Product\ProductAccessory\ProductAccessoryModule;
use domain\Product\ProductCategory\ProductCategoryModule;
use domain\Product\ProductPointSale\ProductPointSaleModule;
use domain\Product\ProductPrice\Module\ProductPriceModule;
@@ -43,6 +44,11 @@ use domain\User\UserUserGroup\UserUserGroupModule;

trait BusinessLogicTrait
{
public function getProductAccessoryModule(): ProductAccessoryModule
{
return ProductAccessoryModule::getInstance();
}

public function getAccessoryModule(): AccessoryModule
{
return AccessoryModule::getInstance();

+ 31
- 0
console/migrations/m240620_074924_create_table_product_accessory.php View File

@@ -0,0 +1,31 @@
<?php

use yii\db\Migration;
use yii\db\Schema;

/**
* Class m240620_074924_create_table_product_accessory
*/
class m240620_074924_create_table_product_accessory extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable('product_accessory', [
'id' => 'pk',
'id_product' => Schema::TYPE_INTEGER.' NOT NULl',
'id_accessory' => Schema::TYPE_INTEGER.' NOT NULl',
'quantity' => Schema::TYPE_INTEGER.' NOT NULL'
]);
}

/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable('product_accessory');
}
}

+ 26
- 0
domain/Product/Accessory/Accessory.php View File

@@ -4,10 +4,14 @@ namespace domain\Product\Accessory;

use common\components\ActiveRecordCommon;
use domain\Producer\Producer\Producer;
use domain\Product\ProductAccessory\ProductAccessory;
use yii\db\ActiveQuery;

class Accessory extends ActiveRecordCommon
{
// Produits sélectionnés dans le formulaire d'édition
public $selected_products_ids;

public static function tableName()
{
return 'accessory';
@@ -19,6 +23,7 @@ class Accessory extends ActiveRecordCommon
[['name', 'id_producer'], 'required'],
[['name'], 'string', 'max' => 255],
[['quantity', 'id_producer'], 'integer'],
[['selected_products_ids'], 'safe'],
];
}

@@ -71,10 +76,31 @@ class Accessory extends ActiveRecordCommon
return $this;
}

public function getProductAccessories(): array
{
return $this->productAccessoriesRelation;
}

public function getSelectedProductsIds()
{
return $this->selected_products_ids;
}

public function setSelectedProductsIds(array $selectedProductsIdsArray): self
{
$this->selected_products_ids = $selectedProductsIdsArray;
return $this;
}

/* Relations */

public function getProducerRelation(): ActiveQuery
{
return $this->hasOne(Producer::class, ['id' => 'id_producer']);
}

public function getProductAccessoriesRelation(): ActiveQuery
{
return $this->hasMany(ProductAccessory::class, ['id_accessory' => 'id']);
}
}

+ 18
- 0
domain/Product/Accessory/AccessoryBuilder.php View File

@@ -4,9 +4,17 @@ namespace domain\Product\Accessory;

use domain\_\AbstractBuilder;
use domain\Producer\Producer\Producer;
use domain\Product\ProductAccessory\ProductAccessoryRepository;

class AccessoryBuilder extends AbstractBuilder
{
protected ProductAccessoryRepository $productAccessoryRepository;

public function loadDependencies(): void
{
$this->productAccessoryRepository = $this->loadService(ProductAccessoryRepository::class);
}

public function instanciateAccessory(Producer $producer): Accessory
{
$accessory = new Accessory();
@@ -14,4 +22,14 @@ class AccessoryBuilder extends AbstractBuilder

return $accessory;
}

public function initSelectedProductsIds(Accessory $accessory): void
{
$selectedProductsIdsArray = [];
$productAccessoriesArray = $this->productAccessoryRepository->findProductAccessoriesByAccessory($accessory);
foreach($productAccessoriesArray as $productAccessory) {
$selectedProductsIdsArray[] = $productAccessory->getProduct()->id;
}
$accessory->setSelectedProductsIds($selectedProductsIdsArray);
}
}

+ 20
- 0
domain/Product/Accessory/AccessoryManager.php View File

@@ -4,14 +4,22 @@ namespace domain\Product\Accessory;

use domain\_\AbstractManager;
use domain\Producer\Producer\Producer;
use domain\Product\Product\ProductRepository;
use domain\Product\ProductAccessory\ProductAccessoryManager;
use domain\Product\ProductAccessory\ProductAccessoryRepository;
use yii\base\ErrorException;

class AccessoryManager extends AbstractManager
{
protected AccessoryBuilder $accessoryBuilder;
protected ProductAccessoryManager $productAccessoryManager;
protected ProductRepository $productRepository;

public function loadDependencies(): void
{
$this->accessoryBuilder = $this->loadService(AccessoryBuilder::class);
$this->productAccessoryManager = $this->loadService(ProductAccessoryManager::class);
$this->productRepository = $this->loadService(ProductRepository::class);
}

public function createAccessory(Producer $producer, string $name, ?int $quantity): Accessory
@@ -22,4 +30,16 @@ class AccessoryManager extends AbstractManager
$accessory->save();
return $accessory;
}

public function manageProducts(Accessory $accessory, array $selectedProductsIds = []): void
{
$this->productAccessoryManager->deleteAllProductAccessoriesByAccessory($accessory);

foreach($selectedProductsIds as $selectedProductId) {
$product = $this->productRepository->findOneProductById($selectedProductId);
if($product) {
$this->productAccessoryManager->createProductAccessory($product, $accessory, 1);
}
}
}
}

+ 7
- 0
domain/Product/Accessory/AccessoryRepository.php View File

@@ -3,6 +3,8 @@
namespace domain\Product\Accessory;

use domain\_\AbstractRepository;
use domain\_\RepositoryQueryInterface;
use yii\db\ActiveQuery;

class AccessoryRepository extends AbstractRepository
{
@@ -23,6 +25,11 @@ class AccessoryRepository extends AbstractRepository
];
}

public function queryAccessories(): RepositoryQueryInterface
{
return $this->createDefaultQuery();
}

public function findOneAccessoryById(int $id): ?Accessory
{
return $this->createDefaultQuery()

+ 22
- 0
domain/Product/Product/ProductManager.php View File

@@ -0,0 +1,22 @@
<?php

namespace domain\Product\Product;

use domain\_\AbstractManager;
use domain\Product\Accessory\Accessory;
use domain\Product\ProductAccessory\ProductAccessoryManager;

class ProductManager extends AbstractManager
{
protected ProductAccessoryManager $productAccessoryManager;

public function loadDependencies(): void
{
$this->productAccessoryManager = $this->loadService(ProductAccessoryManager::class);
}

public function needAccessory(Product $product, Accessory $accessory, int $quantity): void
{
$this->productAccessoryManager->createProductAccessoryIfNotExist($product, $accessory, $quantity);
}
}

+ 9
- 3
domain/Product/Product/ProductModule.php View File

@@ -12,8 +12,9 @@ class ProductModule extends AbstractModule
return [
ProductDefinition::class,
ProductSolver::class,
ProductRepository::class,
ProductBuilder::class,
ProductRepository::class,
ProductManager::class
];
}

@@ -27,13 +28,18 @@ class ProductModule extends AbstractModule
return ProductSolver::getInstance();
}

public function getBuilder(): ProductBuilder
{
return ProductBuilder::getInstance();
}

public function getRepository(): ProductRepository
{
return ProductRepository::getInstance();
}

public function getBuilder(): ProductBuilder
public function getManager(): ProductManager
{
return ProductBuilder::getInstance();
return ProductManager::getInstance();
}
}

+ 86
- 0
domain/Product/ProductAccessory/ProductAccessory.php View File

@@ -0,0 +1,86 @@
<?php

namespace domain\Product\ProductAccessory;

use common\components\ActiveRecordCommon;
use domain\Product\Accessory\Accessory;
use domain\Product\Product\Product;
use yii\db\ActiveQuery;

class ProductAccessory extends ActiveRecordCommon
{
public static function tableName()
{
return 'product_accessory';
}

public function rules()
{
return [
[['id_product', 'id_accessory', 'quantity'], 'required'],
[['id_product', 'id_accessory', 'quantity'], 'integer'],
];
}

public function attributeLabels()
{
return [
'id' => 'ID',
'id_product' => 'Produit',
'id_accessory' => 'Accessoire',
'quantity' => 'Quantité',
];
}

/* Getters / Setters */

public function getId(): ?int
{
return $this->id;
}

public function getProduct(): Product
{
return $this->productRelation;
}

public function setProduct(Product $product): self
{
$this->populateFieldObject('id_product', 'productRelation', $product);
return $this;
}

public function getAccessory(): Accessory
{
return $this->accessoryRelation;
}

public function setAccessory(Accessory $accessory): self
{
$this->populateFieldObject('id_accessory', 'accessoryRelation', $accessory);
return $this;
}

public function getQuantity(): ?int
{
return $this->quantity;
}

public function setQuantity(?int $quantity): self
{
$this->quantity = $quantity;
return $this;
}

/* Relations */

public function getProductRelation(): ActiveQuery
{
return $this->hasOne(Product::class, ['id' => 'id_product']);
}

public function getAccessoryRelation(): ActiveQuery
{
return $this->hasOne(Accessory::class, ['id' => 'id_accessory']);
}
}

+ 15
- 0
domain/Product/ProductAccessory/ProductAccessoryBuilder.php View File

@@ -0,0 +1,15 @@
<?php

namespace domain\Product\ProductAccessory;

use domain\_\AbstractBuilder;
use domain\Product\Product\Product;

class ProductAccessoryBuilder extends AbstractBuilder
{
public function instanciateProductAccessory(): ProductAccessory
{
$productAccessory = new ProductAccessory();
return $productAccessory;
}
}

+ 13
- 0
domain/Product/ProductAccessory/ProductAccessoryDefinition.php View File

@@ -0,0 +1,13 @@
<?php

namespace domain\Product\ProductAccessory;

use domain\_\AbstractDefinition;

class ProductAccessoryDefinition extends AbstractDefinition
{
public function getEntityFqcn(): string
{
return ProductAccessory::class;
}
}

+ 51
- 0
domain/Product/ProductAccessory/ProductAccessoryManager.php View File

@@ -0,0 +1,51 @@
<?php

namespace domain\Product\ProductAccessory;

use domain\_\AbstractManager;
use domain\Product\Accessory\Accessory;
use domain\Product\Product\Product;

class ProductAccessoryManager extends AbstractManager
{
protected ProductAccessoryBuilder $productAccessoryBuilder;
protected ProductAccessoryRepository $productAccessoryRepository;

public function loadDependencies(): void
{
$this->productAccessoryBuilder = $this->loadService(ProductAccessoryBuilder::class);
$this->productAccessoryRepository = $this->loadService(ProductAccessoryRepository::class);
}

public function createProductAccessory(Product $product, Accessory $accessory, int $quantity): ProductAccessory
{
$productAccessory = $this->productAccessoryBuilder->instanciateProductAccessory();
$productAccessory->setProduct($product);
$productAccessory->setAccessory($accessory);
$productAccessory->setQuantity($quantity);
$productAccessory->save();
return $productAccessory;
}

public function createProductAccessoryIfNotExist(Product $product, Accessory $accessory, int $quantity): ProductAccessory
{
$productAccessory = $this->productAccessoryRepository->findOneProductAccessory($product, $accessory);
if($productAccessory) {
$productAccessory->setQuantity($quantity);
$productAccessory->save();
}
else {
$productAccessory = $this->createProductAccessory($product, $accessory, $quantity);
}

return $productAccessory;
}

public function deleteAllProductAccessoriesByAccessory(Accessory $accessory): void
{
$productAccessoriesArray = $this->productAccessoryRepository->findProductAccessoriesByAccessory($accessory);
foreach($productAccessoriesArray as $productAccessory) {
$productAccessory->delete();
}
}
}

+ 38
- 0
domain/Product/ProductAccessory/ProductAccessoryModule.php View File

@@ -0,0 +1,38 @@
<?php

namespace domain\Product\ProductAccessory;

use domain\_\AbstractModule;

class ProductAccessoryModule extends AbstractModule
{
public function getServices(): array
{
return [
ProductAccessoryDefinition::class,
ProductAccessoryBuilder::class,
ProductAccessoryRepository::class,
ProductAccessoryManager::class,
];
}

public function getDefinition(): ProductAccessoryDefinition
{
return ProductAccessoryDefinition::getInstance();
}

public function getBuilder(): ProductAccessoryBuilder
{
return ProductAccessoryBuilder::getInstance();
}

public function getRepository(): ProductAccessoryRepository
{
return ProductAccessoryRepository::getInstance();
}

public function getManager(): ProductAccessoryManager
{
return ProductAccessoryManager::getInstance();
}
}

+ 42
- 0
domain/Product/ProductAccessory/ProductAccessoryRepository.php View File

@@ -0,0 +1,42 @@
<?php

namespace domain\Product\ProductAccessory;

use domain\_\AbstractRepository;
use domain\Product\Accessory\Accessory;
use domain\Product\Product\Product;

class ProductAccessoryRepository extends AbstractRepository
{
protected ProductAccessoryRepositoryQuery $query;

public function loadDependencies(): void
{
$this->loadQuery(ProductAccessoryRepositoryQuery::class);
}

public function getDefaultOptionsSearch(): array
{
return [
self::WITH => [],
self::JOIN_WITH => ['accessoryRelation'],
self::ORDER_BY => 'accessory.name ASC',
self::ATTRIBUTE_ID_PRODUCER => ''
];
}

public function findOneProductAccessory(Product $product, Accessory $accessory): ?ProductAccessory
{
return $this->createDefaultQuery()
->filterByProduct($product)
->filterByAccessory($accessory)
->findOne();
}

public function findProductAccessoriesByAccessory(Accessory $accessory): array
{
return $this->createDefaultQuery()
->filterByAccessory($accessory)
->find();
}
}

+ 29
- 0
domain/Product/ProductAccessory/ProductAccessoryRepositoryQuery.php View File

@@ -0,0 +1,29 @@
<?php

namespace domain\Product\ProductAccessory;

use domain\_\AbstractRepositoryQuery;
use domain\Product\Accessory\Accessory;
use domain\Product\Product\Product;

class ProductAccessoryRepositoryQuery extends AbstractRepositoryQuery
{
protected ProductAccessoryDefinition $definition;

public function loadDependencies(): void
{
$this->loadDefinition(ProductAccessoryDefinition::class);
}

public function filterByProduct(Product $product): self
{
$this->andWhere(['product_accessory.id_product' => $product->id]);
return $this;
}

public function filterByAccessory(Accessory $accessory): self
{
$this->andWhere(['product_accessory.id_accessory' => $accessory->id]);
return $this;
}
}

Loading…
Cancel
Save