You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 satır
19KB

  1. <?php
  2. /**
  3. * Copyright distrib (2018)
  4. *
  5. * contact@opendistrib.net
  6. *
  7. * Ce logiciel est un programme informatique servant à aider les producteurs
  8. * à distribuer leur production en circuits courts.
  9. *
  10. * Ce logiciel est régi par la licence CeCILL soumise au droit français et
  11. * respectant les principes de diffusion des logiciels libres. Vous pouvez
  12. * utiliser, modifier et/ou redistribuer ce programme sous les conditions
  13. * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
  14. * sur le site "http://www.cecill.info".
  15. *
  16. * En contrepartie de l'accessibilité au code source et des droits de copie,
  17. * de modification et de redistribution accordés par cette licence, il n'est
  18. * offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
  19. * seule une responsabilité restreinte pèse sur l'auteur du programme, le
  20. * titulaire des droits patrimoniaux et les concédants successifs.
  21. *
  22. * A cet égard l'attention de l'utilisateur est attirée sur les risques
  23. * associés au chargement, à l'utilisation, à la modification et/ou au
  24. * développement et à la reproduction du logiciel par l'utilisateur étant
  25. * donné sa spécificité de logiciel libre, qui peut le rendre complexe à
  26. * manipuler et qui le réserve donc à des développeurs et des professionnels
  27. * avertis possédant des connaissances informatiques approfondies. Les
  28. * utilisateurs sont donc invités à charger et tester l'adéquation du
  29. * logiciel à leurs besoins dans des conditions permettant d'assurer la
  30. * sécurité de leurs systèmes et ou de leurs données et, plus généralement,
  31. * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
  32. *
  33. * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
  34. * pris connaissance de la licence CeCILL, et que vous en avez accepté les
  35. * termes.
  36. */
  37. namespace backend\controllers;
  38. use backend\forms\ProductPriceUploadForm;
  39. use common\helpers\CSV;
  40. use common\helpers\GlobalParam;
  41. use common\logic\Distribution\ProductDistribution\Model\ProductDistribution;
  42. use common\logic\PointSale\PointSale\Model\PointSale;
  43. use common\logic\Product\Product\Model\Product;
  44. use common\logic\Product\Product\Model\ProductSearch;
  45. use common\logic\Product\ProductPointSale\Model\ProductPointSale;
  46. use common\logic\Product\ProductPrice\Model\ProductPrice;
  47. use common\logic\Product\ProductPrice\Model\ProductPriceSearch;
  48. use common\logic\User\UserProducer\Model\UserProducer;
  49. use Yii;
  50. use yii\filters\AccessControl;
  51. use yii\helpers\Html;
  52. use yii\web\NotFoundHttpException;
  53. use yii\filters\VerbFilter;
  54. use common\helpers\Upload;
  55. use yii\web\UploadedFile;
  56. /**
  57. * ProduitController implements the CRUD actions for Produit model.
  58. */
  59. class ProductController extends BackendController
  60. {
  61. var $enableCsrfValidation = false;
  62. public function behaviors()
  63. {
  64. return [
  65. 'verbs' => [
  66. 'class' => VerbFilter::class,
  67. 'actions' => [
  68. ],
  69. ],
  70. 'access' => [
  71. 'class' => AccessControl::class,
  72. 'rules' => [
  73. [
  74. 'allow' => true,
  75. 'roles' => ['@'],
  76. 'matchCallback' => function ($rule, $action) {
  77. return $this->getUserManager()->hasAccessBackend();
  78. }
  79. ]
  80. ],
  81. ],
  82. ];
  83. }
  84. /**
  85. * Liste les modèles Produit.
  86. *
  87. * @return mixed
  88. */
  89. public function actionIndex()
  90. {
  91. $searchModel = new ProductSearch();
  92. $dataProvider = $searchModel->search(\Yii::$app->request->queryParams);
  93. return $this->render('index', [
  94. 'searchModel' => $searchModel,
  95. 'dataProvider' => $dataProvider,
  96. ]);
  97. }
  98. /**
  99. * Crée un Product.
  100. */
  101. public function actionCreate()
  102. {
  103. $productManager = $this->getProductManager();
  104. $distributionManager = $this->getDistributionManager();
  105. $model = $productManager->instanciateProduct();
  106. $model->active = 1;
  107. $model->id_producer = GlobalParam::getCurrentProducerId();
  108. $model->monday = 1;
  109. $model->tuesday = 1;
  110. $model->wednesday = 1;
  111. $model->thursday = 1;
  112. $model->friday = 1;
  113. $model->saturday = 1;
  114. $model->sunday = 1;
  115. $model->available_on_points_sale = 1;
  116. if ($model->load(\Yii::$app->request->post()) && $productManager->saveCreate($model)) {
  117. $lastProductOrder = Product::find()->where('id_producer = :id_producer')->params([':id_producer' => GlobalParam::getCurrentProducerId()])->orderBy('order DESC')->one();
  118. if ($lastProductOrder) {
  119. $model->order = ++$lastProductOrder->order;
  120. }
  121. Upload::uploadFile($model, 'photo');
  122. $productManager->saveUpdate($model);
  123. $this->processAvailabilityPointsSale($model);
  124. $distributionManager->addProductIncomingDistributions($model);
  125. $this->setFlash('success', 'Produit <strong>' . Html::encode($model->name) . '</strong> ajouté');
  126. return $this->redirect(['index']);
  127. } else {
  128. return $this->render('create', [
  129. 'model' => $model,
  130. ]);
  131. }
  132. }
  133. /**
  134. * Modifie un Product.
  135. */
  136. public function actionUpdate($id)
  137. {
  138. $productManager = $this->getProductManager();
  139. $distributionManager = $this->getDistributionManager();
  140. $request = Yii::$app->request;
  141. $model = $this->findModel($id);
  142. foreach ($model->productPointSale as $productPointSale) {
  143. $model->pointsSale[] = $productPointSale->id_point_sale;
  144. }
  145. $photoFilenameOld = $model->photo;
  146. if ($model->load(\Yii::$app->request->post()) && $productManager->saveUpdate($model)) {
  147. Upload::uploadFile($model, 'photo', $photoFilenameOld);
  148. $deletePhoto = $request->post('delete_photo', 0);
  149. if ($deletePhoto) {
  150. $model->photo = '';
  151. $model->save();
  152. }
  153. $this->processAvailabilityPointsSale($model);
  154. if ($model->apply_distributions) {
  155. $distributionManager->addProductIncomingDistributions($model);
  156. }
  157. $this->setFlash('success', 'Produit <strong>' . Html::encode($model->name) . '</strong> modifié');
  158. return $this->redirect(['index']);
  159. }
  160. return $this->render('update/update', [
  161. 'model' => $model,
  162. 'action' => 'update',
  163. ]);
  164. }
  165. /**
  166. * Traite les accès restreints d'un point de vente.
  167. */
  168. public function processAvailabilityPointsSale($product)
  169. {
  170. $pointSaleManager = $this->getPointSaleManager();
  171. $productPointSaleManager = $this->getProductPointSaleManager();
  172. ProductPointSale::deleteAll(['id_product' => $product->id]);
  173. if (is_array($product->pointsSale) && count($product->pointsSale)) {
  174. foreach ($product->pointsSale as $key => $idPointSale) {
  175. $pointSale = $pointSaleManager->findOnePointSaleById($idPointSale);
  176. if ($pointSale) {
  177. $productPointSaleManager->createProductPointSale(
  178. $product,
  179. $pointSale,
  180. ($product->available_on_points_sale) ? false : true
  181. );
  182. }
  183. }
  184. }
  185. }
  186. public function actionPricesList(int $id)
  187. {
  188. $model = $this->findModel($id);
  189. $searchModel = new ProductPriceSearch();
  190. $searchModel->id_product = $id;
  191. $dataProvider = $searchModel->search(array_merge(\Yii::$app->request->queryParams, [
  192. 'id_product' => $id
  193. ]));
  194. $userProducerWithProductPercent = UserProducer::searchAll([], [
  195. 'join_with' => ['user'],
  196. 'conditions' => 'user_producer.product_price_percent != 0',
  197. ]);
  198. $pointSaleWithProductPercent = PointSale::searchAll([], [
  199. 'conditions' => 'point_sale.product_price_percent != 0'
  200. ]);
  201. return $this->render('update/prices/list', [
  202. 'model' => $model,
  203. 'action' => 'prices-list',
  204. 'searchModel' => $searchModel,
  205. 'dataProvider' => $dataProvider,
  206. 'userProducerWithProductPercent' => $userProducerWithProductPercent,
  207. 'pointSaleWithProductPercent' => $pointSaleWithProductPercent,
  208. ]);
  209. }
  210. public function actionPricesCreate($idProduct)
  211. {
  212. $model = new ProductPrice();
  213. $model->id_product = $idProduct;
  214. $modelProduct = $this->findModel($idProduct);
  215. if ($model->load(\Yii::$app->request->post())) {
  216. $conditionsProductPriceExist = [
  217. 'id_product' => $idProduct,
  218. 'id_user' => $model->id_user ?? null,
  219. 'id_user_group' => $model->id_user_group ?? null,
  220. 'id_point_sale' => $model->id_point_sale ?? null,
  221. 'from_quantity' => $model->from_quantity ?? null,
  222. ];
  223. $productPriceExist = ProductPrice::findOne($conditionsProductPriceExist);
  224. if ($productPriceExist) {
  225. $productPriceExist->delete();
  226. $this->setFlash('warning', 'Un prix existait déjà pour cet utilisateur / point de vente, il a été supprimé.');
  227. }
  228. if ($model->save()) {
  229. $this->setFlash('success', 'Le prix a bien été ajouté.');
  230. return $this->redirect(['product/prices-list', 'id' => $idProduct]);
  231. }
  232. }
  233. return $this->render('update/prices/create', [
  234. 'model' => $model,
  235. 'modelProduct' => $modelProduct,
  236. ]);
  237. }
  238. public function actionPricesUpdate($id)
  239. {
  240. $model = $this->findModelProductPrice($id);
  241. $modelProduct = $this->findModel($model->id_product);
  242. if ($model->load(\Yii::$app->request->post()) && $model->save()) {
  243. $this->setFlash('success', 'Prix modifié');
  244. return $this->redirect(['product/prices-list', 'id' => $model->id_product]);
  245. }
  246. return $this->render('update/prices/update', [
  247. 'model' => $model,
  248. 'modelProduct' => $modelProduct,
  249. 'action' => 'prices-update',
  250. ]);
  251. }
  252. public function actionPricesDelete($id)
  253. {
  254. $productPrice = $this->findModelProductPrice($id);
  255. $productPrice->delete();
  256. $this->setFlash('success', 'Prix supprimé');
  257. return $this->redirect(['product/prices-list', 'id' => $productPrice->id_product]);
  258. }
  259. /**
  260. * Supprime un Product.
  261. */
  262. public function actionDelete(int $id, bool $confirm = false)
  263. {
  264. $product = $this->findModel($id);
  265. if ($confirm) {
  266. $product->delete();
  267. ProductDistribution::deleteAll(['id_product' => $id]);
  268. $this->setFlash('success', 'Produit <strong>' . Html::encode($product->name) . '</strong> supprimé');
  269. } else {
  270. $this->setFlash('info', 'Souhaitez-vous vraiment supprimer le produit <strong>' . Html::encode($product->name) . '</strong> ? '
  271. . Html::a('Oui', ['product/delete', 'id' => $id, 'confirm' => 1], ['class' => 'btn btn-default']) . ' ' . Html::a('Non', ['product/index'], ['class' => 'btn btn-default']));
  272. }
  273. return $this->redirect(['index']);
  274. }
  275. /**
  276. * Modifie l'ordre des produits.
  277. */
  278. public function actionOrder()
  279. {
  280. $array = Yii::$app->request->post('array');
  281. $orderArray = json_decode(stripslashes($array));
  282. foreach ($orderArray as $id => $order) {
  283. $product = $this->findModel($id);
  284. $product->order = $order;
  285. $product->save();
  286. }
  287. }
  288. public function actionAjaxToggleActive($id, $active)
  289. {
  290. \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
  291. $distributionManager = $this->getDistributionManager();
  292. $product = $this->findModel($id);
  293. $product->active = (int) $active;
  294. $product->save();
  295. $distributionManager->addProductIncomingDistributions($product);
  296. return ['success', 'id' => $id, 'active' => $active];
  297. }
  298. /**
  299. * Import des prix produits via un fichier CSV.
  300. *
  301. * @return mixed
  302. */
  303. public function actionPriceImport()
  304. {
  305. $productManager = $this->getProductManager();
  306. $productPriceManager = $this->getProductPriceManager();
  307. $userGroupManager = $this->getUserGroupManager();
  308. $pointSaleManager = $this->getPointSaleManager();
  309. $userManager = $this->getUserManager();
  310. $model = new ProductPriceUploadForm();
  311. if (Yii::$app->request->isPost) {
  312. $model->file = UploadedFile::getInstance($model, 'file');
  313. if ($model->file && $model->validate()) {
  314. $productPriceCsvArray = array_map(function($data) { return str_getcsv($data,";");}, file($model->file->tempName));
  315. if(!$productPriceCsvArray || count($productPriceCsvArray[0]) != 6) {
  316. $this->setFlash('error', "Format de fichier invalide. Veuillez vérifier que le séparateur de champs de votre fichier est bien \";\".");
  317. }
  318. else {
  319. unset($productPriceCsvArray[0]);
  320. $countUpdate = 0;
  321. $dataNotFound = false;
  322. foreach ($productPriceCsvArray as $productPriceCsv) {
  323. if (count($productPriceCsv) != 6) {
  324. $dataNotFound = true;
  325. continue;
  326. }
  327. $productName = $productPriceCsv[0];
  328. $userArray = explode('#', $productPriceCsv[1]);
  329. $userGroupName = $productPriceCsv[2];
  330. $pointSaleName = $productPriceCsv[3];
  331. $quantityFrom = (float)$productPriceCsv[4];
  332. $price = (float)$productPriceCsv[5];
  333. $product = $productName ? $productManager->findOneProductByName($productName) : null;
  334. $user = (count($userArray) > 1) ? $userManager->findOneUserById((int)$userArray[1]) : null;
  335. $userGroup = $userGroupName ? $userGroupManager->findOneUserGroupByName($userGroupName) : null;
  336. $pointSale = $pointSaleName ? $pointSaleManager->findOnePointSaleByName($pointSaleName) : null;
  337. if (($productName && !$product)
  338. || (count($userArray) > 1 && !$user)
  339. || ($userGroupName && !$userGroup)
  340. || ($pointSaleName && !$pointSale)) {
  341. $dataNotFound = true;
  342. continue;
  343. }
  344. if ($product) {
  345. // prix de base
  346. if (!$user && !$userGroup && !$pointSale && !$quantityFrom) {
  347. $product->price = $price;
  348. $productManager->saveUpdate($product);
  349. $countUpdate++;
  350. } // prix spécifique
  351. else {
  352. $productPrice = $productPriceManager->findOneProductPriceBy($product, $user, $userGroup, $pointSale, $quantityFrom);
  353. if ($productPrice) {
  354. $productPrice->price = $price;
  355. $productPriceManager->saveUpdate($productPrice);
  356. $countUpdate++;
  357. } else {
  358. $dataNotFound = true;
  359. }
  360. }
  361. }
  362. }
  363. if ($dataNotFound) {
  364. $this->addFlash('error', "Attention, certaines lignes du fichier n'ont pas été prises en compte. Veuillez réessayer en repartant du fichier d'export.<br />Contacter l'administrateur du site si le problème persiste.");
  365. }
  366. if ($countUpdate) {
  367. $this->addFlash('success', $countUpdate . ' prix produits mis à jour.');
  368. }
  369. }
  370. }
  371. }
  372. return $this->render('price_import', [
  373. 'model' => $model
  374. ]);
  375. }
  376. /**
  377. * Export des prix produits au format CSV.
  378. *
  379. * @return mixed
  380. */
  381. public function actionPriceExport()
  382. {
  383. $productManager = $this->getProductManager();
  384. $productPriceManager = $this->getProductPriceManager();
  385. $userManager = $this->getUserManager();
  386. $data = [];
  387. $data[] = [
  388. "Produit",
  389. "Utilisateur",
  390. "Groupe d'utilisateur",
  391. "Point de vente",
  392. "À partir de la quantité",
  393. "Prix HT"
  394. ];
  395. $productArray = $productManager->findProducts();
  396. foreach($productArray as $product) {
  397. // prix produit
  398. $data[] = [
  399. $product->name,
  400. '',
  401. '',
  402. '',
  403. '',
  404. $product->price
  405. ];
  406. // prix spécifiques
  407. foreach($product->productPrice as $productPrice) {
  408. $productPrice = $productPriceManager->findOneProductPriceById($productPrice->id);
  409. if($productPrice->user || $productPrice->userGroup || $productPrice->pointSale || $productPrice->from_quantity) {
  410. $data[] = [
  411. $product->name,
  412. $productPrice->user ? str_replace('#', '', $userManager->getUsername($productPrice->user)).' #'.$productPrice->user->id : '',
  413. $productPrice->userGroup ? $productPrice->userGroup->name : '',
  414. $productPrice->pointSale ? $productPrice->pointSale->name : '',
  415. $productPrice->from_quantity ?? '',
  416. $productPrice->price
  417. ];
  418. }
  419. }
  420. }
  421. CSV::downloadSendHeaders('prix_produits.csv');
  422. echo CSV::array2csv($data);
  423. die();
  424. }
  425. /**
  426. * Recherche un produit en fonction de son ID.
  427. */
  428. protected function findModel(int $id)
  429. {
  430. $productManager = $this->getProductManager();
  431. if (($product = $productManager->findOneProductById($id)) !== null) {
  432. return $product;
  433. } else {
  434. throw new NotFoundHttpException('The requested page does not exist.');
  435. }
  436. }
  437. protected function findModelProductPrice($id)
  438. {
  439. $productPriceManager = $this->getProductPriceManager();
  440. if (($productPrice = $productPriceManager->findOneProductPriceById($id)) !== null) {
  441. return $productPrice;
  442. } else {
  443. throw new NotFoundHttpException('The requested page does not exist.');
  444. }
  445. }
  446. }