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.

510 lines
21KB

  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 common\models;
  38. use Yii;
  39. use common\components\ActiveRecordCommon;
  40. use common\helpers\Departments;
  41. use yii\helpers\Html;
  42. /**
  43. * This is the model class for table "etablissement".
  44. *
  45. * @property integer $id
  46. * @property string $name
  47. * @property string $siret
  48. * @property string $logo
  49. * @property string $photo
  50. * @property string $description
  51. * @property string $postcode
  52. * @property string $city
  53. * @property float credit_limit_reminder
  54. * @property boolean online_payment
  55. * @property string mentions
  56. * @property string gcs
  57. * @property boolean option_allow_user_gift
  58. * @property string credit_functioning
  59. * @property boolean use_credit_checked_default
  60. * @property float credit_limit
  61. * @property string background_color_logo
  62. *
  63. */
  64. class Producer extends ActiveRecordCommon
  65. {
  66. const CREDIT_FUNCTIONING_MANDATORY = 'mandatory';
  67. const CREDIT_FUNCTIONING_OPTIONAL = 'optional';
  68. const CREDIT_FUNCTIONING_USER = 'user';
  69. const HINT_CREDIT_FUNCTIONING = '<ul>'
  70. . '<li>Optionnelle : l\'utilisateur choisit s\'il utilise son Crédit ou non. Les commandes peuvent être payées ou impayées.</li>'
  71. . '<li>Obligatoire : toutes les commandes de l\'utilisateur son comptabilisées au niveau du Crédit. Toutes les commandes sont payées.</li>'
  72. . '<li>Basée sur l\'utilisateur : Crédit obligatoire si l\'utilisateur a le crédit activé au niveau de son compte, système de Crédit non affiché sinon.</li>'
  73. . '</ul>';
  74. public static $creditFunctioningArray = [
  75. self::CREDIT_FUNCTIONING_MANDATORY => 'Obligatoire',
  76. self::CREDIT_FUNCTIONING_OPTIONAL => 'Optionnelle',
  77. self::CREDIT_FUNCTIONING_USER => 'Basée sur l\'utilisateur',
  78. ];
  79. const BEHAVIOR_DELETE_ORDER_DELETE = 'delete';
  80. const BEHAVIOR_DELETE_ORDER_STATUS = 'status';
  81. var $secret_key_payplug;
  82. /**
  83. * @inheritdoc
  84. */
  85. public static function tableName()
  86. {
  87. return 'producer';
  88. }
  89. /**
  90. * @inheritdoc
  91. */
  92. public function rules()
  93. {
  94. return [
  95. [['name', 'type', 'id_tax_rate_default'], 'required'],
  96. [['tiller_provider_token', 'tiller_restaurant_token'], 'required', 'when' => function ($model) {
  97. return $model->tiller == true;
  98. }],
  99. [['order_deadline', 'order_delay', 'id_tax_rate_default', 'document_quotation_duration'], 'integer'],
  100. ['order_deadline', 'in', 'range' => [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]],
  101. ['order_delay', 'in', 'range' => [1, 2, 3, 4, 5, 6, 7]],
  102. ['code', function ($attribute, $params) {
  103. $code = $this->$attribute;
  104. $producer = Producer::findOne(['code' => $code]);
  105. if ($producer && $producer->id != $this->id) {
  106. $this->addError($attribute, 'Ce code est déjà utilisé par un autre producteur.');
  107. }
  108. }],
  109. [['description', 'mentions', 'gcs', 'order_infos', 'slug', 'secret_key_payplug', 'background_color_logo', 'option_behavior_cancel_order', 'tiller_provider_token', 'tiller_restaurant_token', 'status',
  110. 'document_infos_bottom', 'document_infos_quotation', 'document_infos_invoice', 'document_infos_delivery_note'], 'string'],
  111. [['negative_balance', 'credit', 'active', 'online_payment', 'user_manage_subscription', 'option_allow_user_gift', 'use_credit_checked_default', 'tiller'], 'boolean'],
  112. [['name', 'siret', 'logo', 'photo', 'postcode', 'city', 'code', 'type', 'credit_functioning', 'option_behavior_cancel_order', 'document_quotation_prefix', 'document_quotation_first_reference', 'document_invoice_prefix', 'document_invoice_first_reference', 'document_delivery_note_prefix', 'document_delivery_note_first_reference'], 'string', 'max' => 255],
  113. [['free_price', 'credit_limit_reminder', 'credit_limit'], 'double'],
  114. ['free_price', 'compare', 'compareValue' => 0, 'operator' => '>=', 'type' => 'number', 'message' => 'Prix libre doit être supérieur ou égal à 0'],
  115. ];
  116. }
  117. /**
  118. * @inheritdoc
  119. */
  120. public function attributeLabels()
  121. {
  122. return [
  123. 'id' => 'ID',
  124. 'name' => 'Nom',
  125. 'siret' => 'Siret',
  126. 'logo' => 'Logo',
  127. 'photo' => 'Photo',
  128. 'description' => 'Description',
  129. 'postcode' => 'Code postal',
  130. 'city' => 'Ville',
  131. 'code' => 'Code',
  132. 'order_deadline' => 'Heure limite de commande',
  133. 'order_delay' => 'Délai de commande',
  134. 'negative_balance' => 'Solde négatif',
  135. 'credit' => 'Crédit pain',
  136. 'active' => 'Actif',
  137. 'date_creation' => 'Date de création',
  138. 'order_infos' => 'Informations',
  139. 'slug' => 'Slug',
  140. 'type' => 'Type de producteur',
  141. 'credit_limit_reminder' => 'Seuil de crédit limite avant relance',
  142. 'online_payment' => 'Activer le paiement en ligne',
  143. 'secret_key_payplug' => 'Clé secrète Payplug',
  144. 'user_manage_subscription' => 'Autoriser les utilisateurs à gérer leurs abonnements',
  145. 'mentions' => 'Mentions légales',
  146. 'gcs' => 'Conditions générales de vente',
  147. 'option_allow_user_gift' => 'Autoriser les utilisateurs à effectuer un don à la plateforme lors de leur commande',
  148. 'credit_functioning' => 'Utilisation du Crédit par l\'utilisateur',
  149. 'credit_limit' => 'Crédit limite',
  150. 'use_credit_checked_default' => 'Cocher par défaut l\'option "Utiliser mon crédit" lors de la commande de l\'utilisateur',
  151. 'background_color_logo' => 'Couleur de fond du logo',
  152. 'option_behavior_cancel_order' => 'Comportement lors de la suppression d\'une commande',
  153. 'tiller' => 'Tiller',
  154. 'tiller_provider_token' => 'Token provider',
  155. 'tiller_restaurant_token' => 'Token restaurant',
  156. 'status' => 'Statut',
  157. 'id_tax_rate_default' => 'Taxe',
  158. 'document_quotation_prefix' => 'Préfixe des devis',
  159. 'document_quotation_first_reference' => 'Première référence des devis',
  160. 'document_quotation_duration' => 'Durée du devis',
  161. 'document_invoice_prefix' => 'Préfixe des factures',
  162. 'document_invoice_first_reference' => 'Première référence des factures',
  163. 'document_delivery_note_prefix' => 'Préfixe des bons de livraison',
  164. 'document_delivery_note_first_reference' => 'Première référence des bons de livraison',
  165. 'document_infos_bottom' => 'Informations affichées en bas des documents',
  166. 'document_infos_quotation' => 'Informations affichées en bas des devis',
  167. 'document_infos_invoice' => 'Informations affichées en bas des factures',
  168. 'document_infos_delivery_note' => 'Informations affichées en bas des bons de livraison',
  169. ];
  170. }
  171. /*
  172. * Relations
  173. */
  174. public function getUserProducer()
  175. {
  176. return $this->hasMany(
  177. UserProducer::className(),
  178. ['id_producer' => 'id']
  179. );
  180. }
  181. public function getUser()
  182. {
  183. return $this->hasMany(User::className(), ['id_producer' => 'id']);
  184. }
  185. public function getContact()
  186. {
  187. return $this->hasMany(User::className(), ['id_producer' => 'id'])
  188. ->where(['status' => User::STATUS_PRODUCER]);
  189. }
  190. public function getTaxRate()
  191. {
  192. return $this->hasOne(TaxRate::className(), ['id' => 'id_tax_rate_default']);
  193. }
  194. /**
  195. * Retourne les options de base nécessaires à la fonction de recherche.
  196. *
  197. * @return array
  198. */
  199. public static function defaultOptionsSearch()
  200. {
  201. return [
  202. 'with' => ['taxRate'],
  203. 'join_with' => [],
  204. 'orderby' => 'name ASC',
  205. 'attribute_id_producer' => 'id'
  206. ];
  207. }
  208. /**
  209. * Retourne la liste des établissements pour l'initialisation d'une liste
  210. * sélective.
  211. *
  212. * @return array
  213. */
  214. public static function getProducerPopulateDropdown()
  215. {
  216. $producers = Producer::find()
  217. ->where([
  218. 'active' => true,
  219. ])
  220. ->orderBy('postcode, city ASC')
  221. ->all();
  222. $departments = Departments::get();
  223. $dataProducers = [];
  224. $optionsProducers = [];
  225. foreach ($producers as $p) {
  226. if (!key_exists('d' . substr($p->postcode, 0, 2), $dataProducers)) {
  227. $dataProducers['d' . substr($p->postcode, 0, 2)] = '<strong>' . $departments[substr($p->postcode, 0, 2)] . '</strong>';
  228. $optionsProducers['d' . substr($p->postcode, 0, 2)] = ['disabled' => true];
  229. }
  230. $dataProducers[$p->id] = '<span class="glyphicon glyphicon-lock"></span> ' . Html::encode($p->name) . ' - ' . Html::encode($p->postcode) . ' ' . Html::encode($p->city) . ' <span class="glyphicon glyphicon-lock"></span>';
  231. if (strlen($p->code)) {
  232. $optionsProducers[$p->id] = ['class' => 'lock'];
  233. }
  234. }
  235. return ['data' => $dataProducers, 'options' => $optionsProducers];
  236. }
  237. /**
  238. * Retourne le CA de l'établissement pour un mois donné.
  239. *
  240. * @param string $period
  241. * @param boolean $format
  242. * @return string
  243. */
  244. public function getTurnover($period = '', $format = false)
  245. {
  246. if (!$period) {
  247. $period = date('Y-m');
  248. }
  249. $connection = Yii::$app->getDb();
  250. $command = $connection->createCommand('
  251. SELECT SUM(product_order.price * product_order.quantity) AS turnover
  252. FROM `order`, product_order, distribution, product
  253. WHERE `order`.id = product_order.id_order
  254. AND distribution.id_producer = :id_producer
  255. AND `order`.id_distribution = distribution.id
  256. AND product_order.id_product = product.id
  257. AND distribution.date > :date_begin
  258. AND distribution.date < :date_end', [
  259. ':date_begin' => date('Y-m-31', strtotime("-1 month", strtotime($period))),
  260. ':date_end' => date('Y-m-01', strtotime("+1 month", strtotime($period))),
  261. ':id_producer' => $this->id
  262. ]);
  263. $result = $command->queryOne();
  264. $turnover = $result['turnover'];
  265. if ($format) {
  266. return number_format($turnover, 2) . ' €';
  267. } else {
  268. return $turnover;
  269. }
  270. }
  271. /**
  272. * Retourne le montant à facturer pour une période donnée.
  273. *
  274. * @param string $periode
  275. * @param float $ca
  276. * @param boolean $format
  277. * @return string
  278. */
  279. public function getMAmountBilled($format = false)
  280. {
  281. if ($format) {
  282. return number_format($this->free_price, 2) . ' €';
  283. } else {
  284. return $this->free_price;
  285. }
  286. }
  287. /**
  288. * Retourne la facture d'une période donnée.
  289. *
  290. * @param string $periode
  291. * @return Facture
  292. */
  293. public function getInvoice($period = '')
  294. {
  295. if (!$period) {
  296. $period = date('Y-m', strtotime('-1 month'));
  297. }
  298. $invoice = Invoice::searchOne(
  299. ['id_producer' => $this->id, 'period' => ':period'],
  300. ['params' => [':period' => $period]]
  301. );
  302. return $invoice;
  303. }
  304. /**
  305. * Retourne la facture du mois dernier.
  306. *
  307. * @return Facture
  308. */
  309. public function getInvoiceLastMonth()
  310. {
  311. return $this->getInvoice(date('Y-m', strtotime('-1 month')));
  312. }
  313. /**
  314. * Retourne une configuration d'un établissement donné.
  315. *
  316. * @param string $config
  317. * @param integer $id_etablissement
  318. * @return mixed
  319. */
  320. public static function getConfig($config = '', $idProducer = 0)
  321. {
  322. if (strlen($config)) {
  323. if (!$idProducer) {
  324. $idProducer = GlobalParam::getCurrentProducerId();
  325. }
  326. $producer = self::findOne($idProducer);
  327. if ($producer) {
  328. return $producer->$config;
  329. }
  330. }
  331. return false;
  332. }
  333. /**
  334. * Retourne le montant de l'abonnement à prix libre définit par
  335. * le producteur.
  336. *
  337. * @param boolean $format
  338. * @return mixed
  339. */
  340. public function getFreePrice($format = true)
  341. {
  342. if (!is_null($this->free_price)) {
  343. if ($format) {
  344. return number_format($this->free_price, 2, ',', false) . ' € HT';
  345. } else {
  346. return $this->free_price;
  347. }
  348. }
  349. }
  350. /**
  351. * Lie un utilisateur à un producteur.
  352. *
  353. * @param integer $id_user
  354. * @param integer $id_producer
  355. * @return UserProducer
  356. */
  357. public static function addUser($idUser, $idProducer, $bookmark = 1)
  358. {
  359. $userProducer = UserProducer::searchOne([
  360. 'user_producer.id_user' => $idUser,
  361. 'user_producer.id_producer' => $idProducer
  362. ]);
  363. if (!$userProducer) {
  364. $newUserProducer = new UserProducer;
  365. $newUserProducer->id_producer = $idProducer;
  366. $newUserProducer->id_user = $idUser;
  367. $newUserProducer->credit = 0;
  368. $newUserProducer->active = 1;
  369. $newUserProducer->bookmark = (int)$bookmark;
  370. $newUserProducer->save();
  371. } else {
  372. if (!$userProducer->active) {
  373. $userProducer->active = 1;
  374. $userProducer->save();
  375. }
  376. }
  377. return $userProducer;
  378. }
  379. /**
  380. * Retourne la date la plus proche où il est possible de commander.
  381. * Attention, cette méthode ne garantit pas qu'une distribution a été initialisée
  382. * à cette date.
  383. *
  384. * @return string
  385. */
  386. public function getEarliestDateOrder()
  387. {
  388. $deadline = 20;
  389. $earliestDateOrder = date('Y-m-d');
  390. if ($this->order_deadline) {
  391. $deadline = $this->order_deadline;
  392. }
  393. if (date('H') >= $deadline) {
  394. $earliestDateOrder = date('Y-m-d', strtotime(date('Y-m-d')) + ($this->order_delay) * (24 * 60 * 60));
  395. } else {
  396. $earliestDateOrder = date('Y-m-d', strtotime(date('Y-m-d')) + ($this->order_delay - 1) * (24 * 60 * 60));
  397. }
  398. return $earliestDateOrder;
  399. }
  400. /**
  401. * Retourne le chemin vers le fichier contenant la clé secrète d'API de Payplug
  402. *
  403. * @return string
  404. */
  405. public function getFilenameSecretKeyPayplug()
  406. {
  407. return '../../common/config/payplug/' . $this->id . '.key';
  408. }
  409. /**
  410. * Enregistre la clé secrète d'API de Payplug dans un fichier
  411. */
  412. public function saveSecretKeyPayplug()
  413. {
  414. if ($this->online_payment) {
  415. $handle = fopen($this->getFilenameSecretKeyPayplug(), "w");
  416. fwrite($handle, $this->secret_key_payplug);
  417. fclose($handle);
  418. }
  419. }
  420. /**
  421. * Retourne la clé secrète d'API de Payplug.
  422. *
  423. * @return string
  424. */
  425. public function getSecretKeyPayplug()
  426. {
  427. if (file_exists($this->getFilenameSecretKeyPayplug())) {
  428. $handle = fopen($this->getFilenameSecretKeyPayplug(), "r");
  429. $filesize = filesize($this->getFilenameSecretKeyPayplug());
  430. if ($handle && $filesize) {
  431. $secretKey = fread($handle, $filesize);
  432. fclose($handle);
  433. return $secretKey;
  434. }
  435. }
  436. return '';
  437. }
  438. /**
  439. * Retourne le compte producteur de démonstration.
  440. *
  441. * @return Producer
  442. */
  443. public static function getDemoAccount()
  444. {
  445. $producer = Producer::find()->where('name LIKE \'Démo\'')->one();
  446. return $producer;
  447. }
  448. /**
  449. * Retourne true si le compte est un compte de démo.
  450. *
  451. * @return boolean
  452. */
  453. public function isDemo()
  454. {
  455. if (strpos($this->name, 'Démo') !== false) {
  456. return true;
  457. }
  458. return false;
  459. }
  460. }