''], [ [ 'id_user', 'id_point_sale', 'id_distribution', 'id_subscription', 'id_invoice', 'id_quotation', 'id_delivery_note' ], 'integer' ], [['auto_payment', 'tiller_synchronization', 'delivery_home'], 'boolean'], [['status', 'reference', 'delivery_address', 'online_payment_url', 'tiller_external_id'], 'string'], [['date', 'date_update', 'comment', 'comment_point_sale', 'mean_payment', 'tiller_external_id'], 'safe'] ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'id_user' => 'Id User', 'date' => 'Date', 'date_update' => 'Date de modification', 'id_point_sale' => 'Point de vente', 'id_distribution' => 'Date de distribution', 'id_subscription' => 'Abonnement', 'status' => 'Statut', 'id_invoice' => 'Facture', 'id_quotation' => 'Devis', 'id_delivery_note' => 'Bon de livraison', 'reference' => 'Référence', 'delivery_home' => 'Livraison à domicile', 'delivery_address' => 'Adresse de livraison', 'online_payment_url' => 'URL de paiement', 'tiller_external_id' => 'Tiller : externalId', ]; } /* * Relations */ public function getUser() { return $this->hasOne(User::className(), ['id' => 'id_user']); } public function getProductOrder() { return $this->hasMany(ProductOrder::className(), ['id_order' => 'id']) ->orderBy(['product.order' => SORT_ASC]) ->joinWith('product'); } public function getDistribution() { return $this->hasOne(Distribution::className(), ['id' => 'id_distribution']) ->with('producer'); } public function getPointSale() { return $this->hasOne(PointSale::className(), ['id' => 'id_point_sale']) ->with('userPointSale'); } public function getCreditHistory() { return $this->hasMany(CreditHistory::className(), ['id_order' => 'id']); } public function getSubscription() { return $this->hasOne(Subscription::className(), ['id' => 'id_subscription']) ->with('productSubscription'); } public function getInvoice() { return $this->hasOne(Invoice::className(), ['id' => 'id_invoice']); } public function getQuotation() { return $this->hasOne(Quotation::className(), ['id' => 'id_quotation']); } public function getDeliveryNote() { return $this->hasOne(DeliveryNote::className(), ['id' => 'id_delivery_note']); } /** * Retourne les options de base nécessaires à la fonction de recherche. * * @return array */ public static function defaultOptionsSearch() { return [ 'with' => [ 'productOrder', 'productOrder.product', 'creditHistory', 'creditHistory.userAction', 'pointSale' ], 'join_with' => ['distribution', 'user', 'user.userProducer'], 'orderby' => 'order.date ASC', 'attribute_id_producer' => 'distribution.id_producer' ]; } /** * Initialise le montant total, le montant déjà payé et le poids de la * commande. */ public function init($taxCalculationMethod = Document::TAX_CALCULATION_METHOD_DEFAULT) { $this->initAmount($taxCalculationMethod); $this->initPaidAmount(); return $this; } /** * Initialise le montant de la commande. * */ public function initAmount($taxCalculationMethod = Document::TAX_CALCULATION_METHOD_DEFAULT) { $this->amount = 0; $this->amount_with_tax = 0; $this->amount_vat = []; $this->invoice_amount = 0; $this->invoice_amount_with_tax = 0; $this->invoice_amount_vat = []; $this->weight = 0; if (isset($this->productOrder)) { foreach ($this->productOrder as $productOrder) { $this->addAmount(self::AMOUNT_TOTAL, $productOrder, $taxCalculationMethod); $this->addAmount(self::INVOICE_AMOUNT_TOTAL, $productOrder, $taxCalculationMethod); $this->addWeight($productOrder); } } } public function addWeight($productOrder) { if ($productOrder->unit == 'piece') { if (isset($productOrder->product)) { $this->weight += ($productOrder->quantity * $productOrder->product->weight) / 1000; } } else { $this->weight += $productOrder->quantity; } } public function addAmount($typeTotal, $productOrder, $taxCalculationMethod) { $fieldNameAmount = $this->getFieldNameAmount($typeTotal); $fieldNameAmountWithTax = $this->getFieldNameAmount($typeTotal, 'with_tax'); $price = $productOrder->getPriceByTypeTotal($typeTotal); $this->$fieldNameAmount += $price * $productOrder->quantity; $this->$fieldNameAmountWithTax += Price::getPriceWithTax( $price, $productOrder->taxRate->value, $taxCalculationMethod ) * $productOrder->quantity; $this->addVat($typeTotal, $price * $productOrder->quantity, $productOrder->taxRate, $taxCalculationMethod); } public function addVat($typeTotal, $priceTotalWithoutTax, $taxRate, $taxCalculationMethod) { $fieldName = $this->getFieldNameAmount($typeTotal, 'vat'); if(!isset($this->$fieldName[$taxRate->id])) { $this->$fieldName[$taxRate->id] = 0; } $this->$fieldName[$taxRate->id] += Price::getVat($priceTotalWithoutTax, $taxRate->value, $taxCalculationMethod); } public function getTotalVat($typeTotal = self::AMOUNT_TOTAL) { $fieldName = $this->getFieldNameAmount($typeTotal, 'vat'); $totalVat = 0; foreach($this->$fieldName as $vat) { $totalVat += $vat; } return $totalVat; } public function getFieldNameAmount($typeTotal = self::AMOUNT_TOTAL, $typeField = '') { $fieldName = 'amount'; if($typeTotal == self::INVOICE_AMOUNT_TOTAL) { $fieldName = 'invoice_amount'; } if($typeField == 'vat') { $fieldName = $fieldName.'_vat'; } elseif($typeField == 'with_tax') { $fieldName = $fieldName.'_with_tax'; } return $fieldName; } /** * Initialise le montant payé de la commande et le retourne. * * @return float */ public function initPaidAmount() { if (isset($this->creditHistory)) { $history = $this->creditHistory; } else { $history = CreditHistory::find() ->where(['id_order' => $this->id]) ->all(); } $this->paid_amount = 0; if (count($history)) { foreach ($history as $ch) { if ($ch->type == CreditHistory::TYPE_PAYMENT) { $this->paid_amount += $ch->amount; } elseif ($ch->type == CreditHistory::TYPE_REFUND) { $this->paid_amount -= $ch->amount; } } } } public function delete() { // remboursement si l'utilisateur a payé pour cette commande $amountPaid = $this->getAmount(Order::AMOUNT_PAID); if ($amountPaid > 0.01) { $this->saveCreditHistory( CreditHistory::TYPE_REFUND, $amountPaid, GlobalParam::getCurrentProducerId(), $this->id_user, User::getCurrentId() ); } // delete if (Producer::getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_DELETE || (Producer::getConfig( 'option_behavior_cancel_order' ) == Producer::BEHAVIOR_DELETE_ORDER_STATUS && strlen($this->date_delete))) { ProductOrder::deleteAll(['id_order' => $this->id]); return parent::delete(); } // status 'delete' elseif (Producer::getConfig('option_behavior_cancel_order') == Producer::BEHAVIOR_DELETE_ORDER_STATUS) { $this->date_delete = date('Y-m-d H:i:s'); return $this->save(); } } /** * Changement de statut d'une commande * * @param $newStatus */ public function changeOrderStatus($newStatus, $origin) { $orderStatusArray = GlobalParam::get('orderStatus'); switch ($newStatus) { case 'new-order' : $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); break; case 'waiting-paiement-on-delivery': if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) { $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); } break; case 'waiting-paiement-by-credit': if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) { $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); } break; case 'paid-by-credit': if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) { $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); } break; case 'waiting-delevery' : if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) { $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); } break; case 'delivered': if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) { $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); } break; case 'refunded': if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) { $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); } break; case 'cancel': if (in_array($newStatus, $orderStatusArray[$this->status]['nextStatusAllow'])) { $this->addOrderStatusHistory($newStatus, $origin); $this->status = $newStatus; $this->save(); } break; default: throw new NotFoundHttpException('Statut de commande inconnu.'); break; } } public function addOrderStatusHistory($newStatus, $origin) { $orderStatusHistory = new OrderStatusHistory(); $orderStatusHistory->id_user = User::getCurrentId(); $orderStatusHistory->id_order = $this->id; $orderStatusHistory->status = $newStatus; $orderStatusHistory->origin = $origin; $orderStatusHistory->date = date('Y-m-d H:i:s'); $orderStatusHistory->save(); } /** * Retourne le montant de la commande (total, payé, restant, ou en surplus). * * @param boolean $format * @return float */ public function getAmount($type = self::AMOUNT_TOTAL, $format = false) { $amount = $this->amount; if ($type == self::INVOICE_AMOUNT_TOTAL && $this->invoice_amount) { $amount = $this->invoice_amount; } return $this->_getAmountGeneric($type, $amount, $format); } public function getAmountWithTax($type = self::AMOUNT_TOTAL, $format = false) { $amount = $this->amount_with_tax; if ($type == self::INVOICE_AMOUNT_TOTAL && $this->invoice_amount) { $amount = $this->invoice_amount_with_tax; } return $this->_getAmountGeneric($type, $amount, $format); } protected function _getAmountGeneric($type, $amountOrder, $format) { switch ($type) { case self::AMOUNT_TOTAL : case self::INVOICE_AMOUNT_TOTAL : $amount = $amountOrder; break; case self::AMOUNT_PAID : $this->initPaidAmount(); $amount = $this->paid_amount; break; case self::AMOUNT_REMAINING : $amount = $this->getAmountWithTax(self::AMOUNT_TOTAL) - $this->getAmountWithTax(self::AMOUNT_PAID); break; case self::AMOUNT_SURPLUS : $amount = $this->getAmountWithTax(self::AMOUNT_PAID) - $this->getAmountWithTax(self::AMOUNT_TOTAL); break; } if ($format) { return Price::format($amount); } else { return $amount; } } /** * Retourne les informations relatives à la commande au format JSON. * * @return string */ public function getDataJson() { $order = Order::searchOne(['order.id' => $this->id]); $jsonOrder = []; if ($order) { $jsonOrder = [ 'products' => [], 'amount' => $order->amount, 'str_amount' => $order->getAmountWithTax(self::AMOUNT_TOTAL, true), 'paid_amount' => $order->getAmount(self::AMOUNT_PAID), 'comment' => $order->comment, ]; foreach ($order->productOrder as $productOrder) { $jsonOrder['products'][$productOrder->id_product] = $productOrder->quantity; } } return json_encode($jsonOrder); } /** * Enregistre un modèle de type CreditHistory. * * @param string $type * @param float $montant * @param integer $idProducer * @param integer $idUser * @param integer $idUserAction */ public function saveCreditHistory($type, $amount, $idProducer, $idUser, $idUserAction) { $creditHistory = new CreditHistory; $creditHistory->id_user = $this->id_user; $creditHistory->id_order = $this->id; $creditHistory->amount = $amount; $creditHistory->type = $type; $creditHistory->id_producer = $idProducer; $creditHistory->id_user_action = $idUserAction; $creditHistory->populateRelation('order', $this); $creditHistory->populateRelation('user', User::find()->where(['id' => $this->id_user])->one()); $creditHistory->save(); } /** * Ajuste le crédit pour que la commande soit payée. * * @return boolean */ public function processCredit() { if ($this->id_user) { $paymentStatus = $this->getPaymentStatus(); if ($paymentStatus == self::PAYMENT_PAID) { return true; } elseif ($paymentStatus == self::PAYMENT_SURPLUS) { $type = CreditHistory::TYPE_REFUND; $amount = $this->getAmount(self::AMOUNT_SURPLUS); } elseif ($paymentStatus == self::PAYMENT_UNPAID) { $type = CreditHistory::TYPE_PAYMENT; $amount = $this->getAmount(self::AMOUNT_REMAINING); } $this->saveCreditHistory( $type, $amount, GlobalParam::getCurrentProducerId(), $this->id_user, User::getCurrentId() ); } } public function setTillerSynchronization() { $order = Order::searchOne(['id' => $this->id]); $paymentStatus = $order->getPaymentStatus(); if ($paymentStatus == self::PAYMENT_PAID) { $order->tiller_synchronization = 1; } else { $order->tiller_synchronization = 0; } $order->save(); return $order; } /** * Retourne le statut de paiement de la commande (payée, surplus, ou impayée). * * @return string */ public function getPaymentStatus() { // payé if ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) < 0.01 && $this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) > -0.01) { return self::PAYMENT_PAID; } // à rembourser elseif ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) <= -0.01) { return self::PAYMENT_SURPLUS; } // reste à payer elseif ($this->getAmountWithtax() - $this->getAmount(self::AMOUNT_PAID) >= 0.01) { return self::PAYMENT_UNPAID; } } /** * Retourne le résumé du panier au format HTML. * * @return string */ public function getCartSummary($htmlFormat = true) { if (!isset($this->productOrder)) { $this->productOrder = productOrder::find()->where(['id_order' => $this->id])->all(); } $html = ''; $count = count($this->productOrder); $i = 0; foreach ($this->productOrder as $p) { if (isset($p->product)) { $html .= Html::encode($p->product->name) . ' (' . $p->quantity . ' ' . Product::strUnit( $p->unit, 'wording_short', true ) . ')'; if (++$i != $count) { if ($htmlFormat) { $html .= '
'; } else { $html .= "\n"; } } } } return $html; } /** * Retourne le résumé du point de vente lié à la commande au format HTML. * * @return string */ public function getPointSaleSummary() { $html = ''; if (isset($this->pointSale)) { $html .= '' . Html::encode($this->pointSale->name) . '' . '
' . Html::encode($this->pointSale->locality) . ''; if (strlen($this->comment_point_sale)) { $html .= '
' . Html::encode($this->comment_point_sale) . '
'; } } else { $html .= 'Point de vente supprimé'; } return $html; } /** * Retourne le résumé du paiement (montant, statut). * * @return string */ public function getAmountSummary() { $html = ''; $creditActive = Producer::getConfig('credit'); $html .= $this->getAmountWithTax(self::AMOUNT_TOTAL, true); if ($creditActive) { $html .= '
'; if ($this->paid_amount) { if ($this->getPaymentStatus() == Order::PAYMENT_PAID) { $html .= 'Payée'; } elseif ($this->getPaymentStatus() == Order::PAYMENT_UNPAID) { $html .= 'Non payée
Reste ' . $this->getAmount( Order::AMOUNT_REMAINING, true ) . ' à payer'; } elseif ($this->getPaymentStatus() == Order::PAYMENT_SURPLUS) { $html .= 'Payée'; } } else { $html .= 'Non réglé'; } } return $html; } /** * Retourne une chaine de caractère décrivant l'utilisateur lié à la commande. * * @return string */ public function getStrUser() { if (isset($this->user)) { if (isset($this->user->name_legal_person) && strlen($this->user->name_legal_person)) { return Html::encode($this->user->name_legal_person); } else { $strUser = $this->user->lastname; if($this->user->lastname && $this->user->name) { $strUser .= ' '.$this->user->name; } return Html::encode($strUser); } } elseif (strlen($this->username)) { return Html::encode($this->username); } else { return 'Client introuvable'; } } /** * Retourne l'état de la commande (livrée, modifiable ou en préparation) * * @return string */ public function getState() { $orderDate = strtotime($this->distribution->date); $today = strtotime(date('Y-m-d')); $todayHour = date('G'); $dayDistribution = strtolower(date('l', strtotime($this->distribution->date))); $orderDelay = Producer::getConfig( 'order_delay', $this->distribution->id_producer ); $orderDelaySpecific = Producer::getConfig( 'order_delay_' . $dayDistribution, $this->distribution->id_producer ); if ($orderDelaySpecific) { $orderDelay = $orderDelaySpecific; } $orderDeadline = Producer::getConfig( 'order_deadline', $this->distribution->id_producer ); $orderDeadlineSpecific = Producer::getConfig( 'order_deadline_' . $dayDistribution, $this->distribution->id_producer ); if ($orderDeadlineSpecific) { $orderDeadline = $orderDeadlineSpecific; } $nbDays = (int)round((($orderDate - $today) / (24 * 60 * 60))); if ($nbDays <= 0) { return self::STATE_DELIVERED; } elseif ($nbDays >= $orderDelay && ($nbDays != $orderDelay || ($nbDays == $orderDelay && $todayHour < $orderDeadline))) { return self::STATE_OPEN; } return self::STATE_PREPARATION; } /** * Retourne l'origine de la commande (client, automatique ou admin) sous forme * texte ou HTML. * * @param boolean $with_label * @return string */ public function getStrOrigin($withLabel = false) { $classLabel = ''; $str = ''; if ($this->origin == self::ORIGIN_USER) { $classLabel = 'success'; $str = 'Client'; } elseif ($this->origin == self::ORIGIN_AUTO) { $classLabel = 'default'; $str = 'Auto'; } elseif ($this->origin == self::ORIGIN_ADMIN) { $classLabel = 'warning'; $str = 'Vous'; } if ($withLabel) { return '' . $str . ''; } else { return $str; } } /** * Retourne l'historique de la commande (ajoutée, modifiée, supprimée) au * format HTML. * * @return string */ public function getStrHistory() { $arr = [ 'class' => 'create', 'glyphicon' => 'plus', 'str' => 'Ajoutée', 'date' => $this->date ]; if (!is_null($this->date_update)) { $arr = [ 'class' => 'update', 'glyphicon' => 'pencil', 'str' => 'Modifiée', 'date' => $this->date_update ]; } if (!is_null($this->date_delete)) { $arr = [ 'class' => 'delete', 'glyphicon' => 'remove', 'str' => 'Annulée', 'date' => $this->date_delete ]; } $html = '
' . ' ' . $arr['str'] . ' le ' . date('d/m/Y à G\hi', strtotime($arr['date'])) . '
'; return $html; } /** * Retourne une classe identifiant l'historique de la commande (ajoutée, * modifiée, supprimée). * * @return string */ public function getClassHistory() { if (!is_null($this->date_delete)) { return 'commande-delete'; } if (!is_null($this->date_update)) { return 'commande-update'; } return 'commande-create'; } /** * Retourne la quantité d'un produit donné de plusieurs commandes. * * @param integer $idProduct * @param array $orders * @param boolean $ignoreCancel * * @return integer */ public static function getProductQuantity($idProduct, $orders, $ignoreCancel = false, $unit = null) { $quantity = 0; if (isset($orders) && is_array($orders) && count($orders)) { foreach ($orders as $c) { if (is_null($c->date_delete) || $ignoreCancel) { foreach ($c->productOrder as $po) { if ($po->id_product == $idProduct && ((is_null($unit) && $po->product->unit == $po->unit) || (!is_null($unit) && strlen( $unit ) && $po->unit == $unit))) { $quantity += $po->quantity; } } } } } return $quantity; } public static function getProductQuantityPieces($idProduct, $orders) { $quantity = 0; if (isset($orders) && is_array($orders) && count($orders)) { foreach ($orders as $c) { if (is_null($c->date_delete)) { foreach ($c->productOrder as $po) { if ($po->id_product == $idProduct) { if ($po->unit == 'piece') { $quantity += $po->quantity; } else { if (isset($po->product) && $po->product->weight > 0) { $quantity += ($po->quantity * Product::$unitsArray[$po->unit]['coefficient']) / $po->product->weight; } } } } } } } return $quantity; } /** * Recherche et initialise des commandes. * * @param array $params * @param array $conditions * @param string $orderby * @param integer $limit * @return array */ public static function searchBy($params = [], $options = []) { $orders = parent::searchBy($params, $options); /* * Initialisation des commandes */ if (is_array($orders)) { if (count($orders)) { foreach ($orders as $order) { if (is_a($order, 'common\models\Order')) { $order->init(); } } return $orders; } } else { $order = $orders; if (is_a($order, 'common\models\Order')) { return $order->init(); } // count else { return $order; } } return false; } /** * Retourne le nombre de produits commandés * * @return integer */ public function countProducts() { $count = 0; if ($this->productOrder && is_array($this->productOrder)) { return count($this->productOrder); } return 0; } /** * Retourne un bloc html présentant une date. * * @return string */ public function getBlockDate() { return '
' . strftime('%A', strtotime($this->distribution->date)) . '
' . date('d', strtotime($this->distribution->date)) . '
' . strftime('%B', strtotime($this->distribution->date)) . '
'; } public function getUsername() { $username = ''; if ($this->user) { $username = $this->user->getUsername(); } if (strlen($this->username)) { $username = $this->username; } return $username; } public function initInvoicePrices($params = []) { foreach ($this->productOrder as $productOrder) { if ($productOrder->product) { $productOrder->invoice_price = $productOrder->product->getPrice([ 'user' => isset($params['user']) ? $params['user'] : null, 'user_producer' => isset($params['user_producer']) ? $params['user_producer'] : null, 'point_sale' => isset($params['point_sale']) ? $params['point_sale'] : null, 'quantity' => $productOrder->quantity ]); $productOrder->save(); } } } public function initReference() { $idProducer = GlobalParam::getCurrentProducerId(); $producer = Producer::findOne($idProducer); if (!$this->reference && $producer->option_order_reference_type == Producer::ORDER_REFERENCE_TYPE_YEARLY) { $lastOrder = Order::find()->innerJoinWith('distribution', true) ->where(['>=', 'distribution.date', date('Y') . '-01-01']) ->andWhere([ 'distribution.id_producer' => $producer->id ]) ->andWhere(['not', ['order.reference' => null]]) ->orderBy('order.reference DESC') ->one(); if ($lastOrder && $lastOrder->reference && strlen($lastOrder->reference) > 0) { $pattern = '#A([0-9]+)C([0-9]+)#'; preg_match($pattern, $lastOrder->reference, $matches, PREG_OFFSET_CAPTURE); $sizeNumReference = strlen($matches[2][0]); $numReference = ((int)$matches[2][0]) + 1; $numReference = str_pad($numReference, $sizeNumReference, '0', STR_PAD_LEFT); $this->reference = 'A' . $matches[1][0] . 'C' . $numReference; } else { $this->reference = 'A' . date('y') . 'C0001'; } $this->save(); } } public function getCommentReport() { $comment = ''; $hasComment = false; if ($this->comment && strlen($this->comment) > 0) { $hasComment = true; $comment .= $this->comment; } if ($this->delivery_home && $this->delivery_address && strlen($this->delivery_address) > 0) { if ($hasComment) { $comment .= '

'; } $comment .= 'Livraison à domicile :
'; $comment .= nl2br($this->delivery_address); } return $comment; } }