@@ -0,0 +1,90 @@ | |||
<?php | |||
/** | |||
* Copyright distrib (2018) | |||
* | |||
* contact@opendistrib.net | |||
* | |||
* Ce logiciel est un programme informatique servant à aider les producteurs | |||
* à distribuer leur production en circuits courts. | |||
* | |||
* Ce logiciel est régi par la licence CeCILL soumise au droit français et | |||
* respectant les principes de diffusion des logiciels libres. Vous pouvez | |||
* utiliser, modifier et/ou redistribuer ce programme sous les conditions | |||
* de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA | |||
* sur le site "http://www.cecill.info". | |||
* | |||
* En contrepartie de l'accessibilité au code source et des droits de copie, | |||
* de modification et de redistribution accordés par cette licence, il n'est | |||
* offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons, | |||
* seule une responsabilité restreinte pèse sur l'auteur du programme, le | |||
* titulaire des droits patrimoniaux et les concédants successifs. | |||
* | |||
* A cet égard l'attention de l'utilisateur est attirée sur les risques | |||
* associés au chargement, à l'utilisation, à la modification et/ou au | |||
* développement et à la reproduction du logiciel par l'utilisateur étant | |||
* donné sa spécificité de logiciel libre, qui peut le rendre complexe à | |||
* manipuler et qui le réserve donc à des développeurs et des professionnels | |||
* avertis possédant des connaissances informatiques approfondies. Les | |||
* utilisateurs sont donc invités à charger et tester l'adéquation du | |||
* logiciel à leurs besoins dans des conditions permettant d'assurer la | |||
* sécurité de leurs systèmes et ou de leurs données et, plus généralement, | |||
* à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. | |||
* | |||
* Le fait que vous puissiez accéder à cet en-tête signifie que vous avez | |||
* pris connaissance de la licence CeCILL, et que vous en avez accepté les | |||
* termes. | |||
*/ | |||
namespace backend\controllers; | |||
use common\helpers\CSV; | |||
use common\helpers\Price; | |||
use http\Exception\InvalidArgumentException; | |||
use yii\data\ActiveDataProvider; | |||
use yii\filters\AccessControl; | |||
class CreditController extends BackendController | |||
{ | |||
public function behaviors() | |||
{ | |||
return [ | |||
'access' => [ | |||
'class' => AccessControl::class, | |||
'rules' => [ | |||
[ | |||
'allow' => true, | |||
'roles' => ['@'], | |||
'matchCallback' => function ($rule, $action) { | |||
return $this->getUserManager()->hasAccessBackend(); | |||
} | |||
] | |||
], | |||
], | |||
]; | |||
} | |||
public function actionIndex() | |||
{ | |||
$userManager = $this->getUserManager(); | |||
$userProducerManager = $this->getUserProducerManager(); | |||
$dataProviderUsersWithNegativeCredit = new ActiveDataProvider([ | |||
'query' => $userManager->queryUsersWithNegativeCredit(), | |||
'sort' => false, | |||
'pagination' => [ | |||
'pageSize' => 30, | |||
], | |||
]); | |||
return $this->render('index', [ | |||
'sumUserProducerCredits' => $userProducerManager->sumUserProducerCredits(), | |||
'dataProviderUsersWithNegativeCredit' => $dataProviderUsersWithNegativeCredit | |||
]); | |||
} | |||
public function actionExportUsers(string $type) | |||
{ | |||
$this->getUserManager()->exportUsersCreditAsCsv($type); | |||
} | |||
} |
@@ -367,11 +367,13 @@ class UserController extends BackendController | |||
$userProducer = $userProducerManager->findOneUserProducer($user); | |||
if ($userProducer) { | |||
$userProducer->active = 0; | |||
$userProducer->bookmark = 0; | |||
$userProducer->save(); | |||
$this->setFlash('success', 'L\'utilisateur a bien été supprimé de votre établissement.'); | |||
if($userProducerManager->hasOutstandingCredit($userProducer)) { | |||
$this->setFlash('error', "Vous ne pouvez pas supprimer cet utilisateur car il a toujours du crédit en cours."); | |||
} | |||
else { | |||
$userProducerManager->unlinkUserProducer($userProducer); | |||
$this->setFlash('success', 'L\'utilisateur a bien été supprimé de votre établissement.'); | |||
} | |||
} else { | |||
throw new \yii\web\NotFoundHttpException('L\'enregistrement UserProducer est introuvable', 404); | |||
} |
@@ -0,0 +1,87 @@ | |||
<?php | |||
use common\helpers\Price; | |||
use yii\grid\GridView; | |||
$userManager = $this->getUserManager(); | |||
$this->setTitle('Somme en crédit'); | |||
?> | |||
<div class="row"> | |||
<div class="col-md-6"> | |||
</div> | |||
<div class="col-md-6"> | |||
</div> | |||
</div> | |||
<div class="row"> | |||
<div class="col-md-6"> | |||
<div class="info-box"> | |||
<span class="info-box-icon <?= $sumUserProducerCredits >= 0 ? 'bg-green' : 'bg-red' ?>"><i | |||
class="fa fa-euro"></i></span> | |||
<div class="info-box-content"> | |||
<span class="info-box-text">Total</span> | |||
<span class="info-box-number"><?= Price::format($sumUserProducerCredits); ?></span> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="col-md-6"> | |||
<div class="info-box"> | |||
<span class="info-box-icon bg-orange"><i class="fa fa-download"></i></span> | |||
<div class="info-box-content"> | |||
<span class="info-box-text">Exports<br /> | |||
<a class="btn btn-default btn-sm" href="<?= Yii::$app->urlManager->createUrl(['credit/export-users', 'type' => 'negative']); ?>">Clients au crédit négatif (CSV)</a> | |||
<a class="btn btn-default btn-sm" href="<?= Yii::$app->urlManager->createUrl(['credit/export-users', 'type' => 'positive']); ?>">Clients au crédit positif (CSV)</a> | |||
</span> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<h3>Clients avec un crédit négatif</h3> | |||
<?= | |||
GridView::widget([ | |||
'dataProvider' => $dataProviderUsersWithNegativeCredit, | |||
'summary' => '', | |||
'columns' => [ | |||
[ | |||
'label' => 'Client', | |||
'value' => function ($user) use ($userManager) { | |||
return $userManager->getUsernameFromArray($user, true); | |||
} | |||
], | |||
[ | |||
'label' => 'Email', | |||
'format' => 'raw', | |||
'headerOptions' => ['class' => 'column-hide-on-mobile'], | |||
'filterOptions' => ['class' => 'column-hide-on-mobile'], | |||
'contentOptions' => ['class' => 'column-hide-on-mobile'], | |||
'value' => function ($user) { | |||
return $user['email']; | |||
} | |||
], | |||
[ | |||
'label' => 'Téléphone', | |||
'format' => 'raw', | |||
'headerOptions' => ['class' => 'column-hide-on-mobile'], | |||
'filterOptions' => ['class' => 'column-hide-on-mobile'], | |||
'contentOptions' => ['class' => 'column-hide-on-mobile'], | |||
'value' => function ($user) { | |||
return $user['phone']; | |||
} | |||
], | |||
[ | |||
'label' => 'Crédit', | |||
'format' => 'raw', | |||
'value' => function ($user) { | |||
return Price::format($user['credit']); | |||
} | |||
] | |||
] | |||
]); | |||
?> |
@@ -613,7 +613,7 @@ $this->setPageTitle('Distributions') ; | |||
<option value="0">--</option> | |||
<option v-for="user in users" :value="user.id_user"> | |||
<template v-if="user.name_legal_person && user.name_legal_person.length"> | |||
Personne morale / {{ user.name_legal_person }} | |||
{{ user.name_legal_person }} (personne morale) | |||
</template> | |||
<template v-else> | |||
{{ user.lastname +' '+ user.name }} |
@@ -224,10 +224,7 @@ $producer = GlobalParam::getCurrentProducer(); | |||
<?php | |||
$usersNegativeCreditArray = $userManager->queryUsersBy(['id_producer' => GlobalParam::getCurrentProducerId()]) | |||
->andWhere('user_producer.credit < 0') | |||
->orderBy('lastname, name ASC') | |||
->all(); | |||
$usersNegativeCreditArray = $userManager->findUsersWithNegativeCredit(); | |||
?> | |||
@@ -241,7 +238,7 @@ $producer = GlobalParam::getCurrentProducer(); | |||
<ul class="dropdown-menu"> | |||
<?php if (count($usersNegativeCreditArray)): ?> | |||
<li class="header">Utilisateurs au crédit négatif</li> | |||
<li class="header"><a href="<?= Yii::$app->urlManager->createUrl(['credit/index']); ?>">Utilisateurs au crédit négatif</a></li> | |||
<li> | |||
<ul class="menu"> | |||
<?php foreach ($usersNegativeCreditArray as $user): ?> |
@@ -43,6 +43,7 @@ use common\logic\Ticket\Ticket\Wrapper\TicketManager; | |||
$producerManager = $this->getProducerManager(); | |||
$userManager = $this->getUserManager(); | |||
$userProducerManager = $this->getUserProducerManager(); | |||
$ticketManager = $this->getTicketManager(); | |||
$producer = GlobalParam::getCurrentProducer(); | |||
@@ -70,6 +71,9 @@ $producer = GlobalParam::getCurrentProducer(); | |||
$countTicketsAdminUnreadLabel = '<span class="pull-right-container"><small class="label pull-right bg-green">'.$countTicketsAdminUnread.'</small></span>'; | |||
} | |||
$sumUserProducerCredits = $userProducerManager->sumUserProducerCredits(); | |||
$sumUserProducerCreditsLabel = '<span class="pull-right-container"><small class="label pull-right '.($sumUserProducerCredits >= 0 ? 'bg-green' : 'bg-red') .'">'.number_format($sumUserProducerCredits, 2).' €</small></span>'; | |||
?> | |||
<?= dmstr\widgets\Menu::widget( | |||
@@ -106,6 +110,13 @@ $producer = GlobalParam::getCurrentProducer(); | |||
'url' => ['/user/index'], | |||
'items' => [ | |||
['label' => 'Liste', 'icon' => 'th-list', 'url' => ['/user/index'], 'visible' => $userManager->isCurrentProducer()], | |||
[ | |||
'label' => 'Crédit', | |||
'icon' => 'euro', | |||
'url' => ['/credit/index'], | |||
'template' => '<a href="{url}">{icon} {label}' . $sumUserProducerCreditsLabel . '</a>', | |||
'visible' => $userManager->isCurrentProducer() && $producerManager->getConfig('credit') | |||
], | |||
['label' => 'Groupes', 'icon' => 'users', 'url' => ['/user-group/index'], 'visible' => $userManager->isCurrentProducer()], | |||
], | |||
], |
@@ -42,6 +42,13 @@ use common\logic\Producer\Producer\Wrapper\ProducerManager; | |||
class CSV | |||
{ | |||
public static function send(string $filename, array $data) | |||
{ | |||
CSV::downloadSendHeaders($filename); | |||
echo CSV::array2csv($data); | |||
die(); | |||
} | |||
public static function array2csv(array &$array) | |||
{ | |||
$producerManager = ProducerManager::getInstance(); |
@@ -0,0 +1,8 @@ | |||
<?php | |||
namespace common\logic; | |||
abstract class AbstractGenerator extends AbstractService implements GeneratorInterface | |||
{ | |||
} |
@@ -17,6 +17,7 @@ abstract class AbstractService extends AbstractSingleton implements ServiceInter | |||
RepositoryInterface::class, | |||
BuilderInterface::class, | |||
ResolverInterface::class, | |||
GeneratorInterface::class, | |||
UtilsInterface::class, | |||
]; | |||
} |
@@ -0,0 +1,8 @@ | |||
<?php | |||
namespace common\logic; | |||
interface GeneratorInterface | |||
{ | |||
} |
@@ -83,11 +83,11 @@ class UserRepository extends AbstractRepository | |||
public function queryUsersBy(array $params = []) | |||
{ | |||
if (!isset($params['id_producer'])) { | |||
$params['id_producer'] = GlobalParam::getCurrentProducerId(); | |||
$params['id_producer'] = $this->getProducerContextId(); | |||
} | |||
$query = (new Query()) | |||
->select(['user.id AS user_id', 'user.name', 'user.lastname', 'user.phone', 'user.email', 'user.created_at', 'user.date_last_connection', 'user_producer.*', 'user.address', 'user.name_legal_person']) | |||
->select(['user.id AS user_id', 'user.type', 'user.name', 'user.lastname', 'user.phone', 'user.email', 'user.created_at', 'user.date_last_connection', 'user_producer.*', 'user.address', 'user.name_legal_person']) | |||
->from('user'); | |||
$active = (isset($params['inactive']) && $params['inactive']) ? 0 : 1; | |||
@@ -159,6 +159,30 @@ class UserRepository extends AbstractRepository | |||
return $query; | |||
} | |||
public function queryUsersWithNegativeCredit() | |||
{ | |||
return $this->queryUsersBy() | |||
->andWhere('user_producer.credit < 0') | |||
->orderBy('credit ASC'); | |||
} | |||
public function findUsersWithNegativeCredit() | |||
{ | |||
return $this->queryUsersWithNegativeCredit()->all(); | |||
} | |||
public function queryUsersWithPositiveCredit() | |||
{ | |||
return $this->queryUsersBy() | |||
->andWhere('user_producer.credit > 0') | |||
->orderBy('lastname, name ASC'); | |||
} | |||
public function findUsersWithPositiveCredit() | |||
{ | |||
return $this->queryUsersWithPositiveCredit()->all(); | |||
} | |||
/** | |||
* Finds user by password reset token | |||
*/ |
@@ -24,30 +24,49 @@ class UserSolver extends AbstractService implements SolverInterface | |||
$username = ''; | |||
if (isset($modelArray['name_legal_person']) && strlen($modelArray['name_legal_person'])) { | |||
$username = $modelArray['name_legal_person']; | |||
if ($withType) { | |||
$username = 'Personne morale / ' . $username; | |||
} | |||
} else { | |||
$username = $modelArray['lastname'] . ' ' . $modelArray['name']; | |||
} | |||
if ($withType && $modelArray['type'] == User::TYPE_LEGAL_PERSON) { | |||
$username = $username . ' (personne morale)'; | |||
} | |||
return $username; | |||
} | |||
public function getContactSummaryFromArrayAsHtml(array $user): string | |||
{ | |||
$html = ''; | |||
if (strlen($user['phone'])) { | |||
$html .= $user['phone']; | |||
} | |||
if (strlen($user['phone']) && strlen($user['email'])) { | |||
$html .= '<br />'; | |||
} | |||
if (strlen($user['email'])) { | |||
$html .= $user['email']; | |||
} | |||
return $html; | |||
} | |||
public function getUsername(User $user, $withType = false): string | |||
{ | |||
$username = ''; | |||
if (isset($user->name_legal_person) && strlen($user->name_legal_person)) { | |||
$username = $user->name_legal_person; | |||
if ($withType) { | |||
$username = 'Personne morale / ' . $username; | |||
} | |||
} else { | |||
$username = $user->lastname . ' ' . $user->name; | |||
} | |||
if ($withType) { | |||
$username = $username . ' (personne morale)'; | |||
} | |||
return $username; | |||
} | |||
@@ -0,0 +1,50 @@ | |||
<?php | |||
namespace common\logic\User\User\Service; | |||
use common\helpers\CSV; | |||
use common\helpers\Price; | |||
use common\logic\User\User\Repository\UserRepository; | |||
use common\logic\AbstractGenerator; | |||
use yii\base\ErrorException; | |||
class UsersCreditCsvGenerator extends AbstractGenerator | |||
{ | |||
protected UserSolver $userSolver; | |||
protected UserRepository $userRepository; | |||
public function loadDependencies(): void | |||
{ | |||
$this->userSolver = $this->loadService(UserSolver::class); | |||
$this->userRepository = $this->loadService(UserRepository::class); | |||
} | |||
public function exportUsersCreditAsCsv(string $type) | |||
{ | |||
if($type == 'negative') { | |||
$filename = 'Utilisateurs_credit_negatif.csv'; | |||
$usersArray = $this->userRepository->findUsersWithNegativeCredit(); | |||
} | |||
elseif($type == 'positive') { | |||
$filename = 'Utilisateurs_credit_positif.csv'; | |||
$usersArray = $this->userRepository->findUsersWithPositiveCredit(); | |||
} | |||
else { | |||
throw new ErrorException('Le paramètre $type est invalide.'); | |||
} | |||
$data = [ | |||
['Client', 'Email', 'Téléphone', 'Crédit'] | |||
]; | |||
foreach($usersArray as $user) { | |||
$data[] = [ | |||
$this->userSolver->getUsernameFromArray($user, true), | |||
$user['email'], | |||
$user['phone'], | |||
Price::format($user['credit']), | |||
]; | |||
} | |||
CSV::send($filename, $data); | |||
} | |||
} |
@@ -4,8 +4,10 @@ namespace common\logic\User\User\Wrapper; | |||
use common\logic\AbstractContainer; | |||
use common\logic\User\User\Repository\UserRepository; | |||
use common\logic\User\User\Repository\UserRepositoryQuery; | |||
use common\logic\User\User\Service\UserBuilder; | |||
use common\logic\User\User\Service\UserDefinition; | |||
use common\logic\User\User\Service\UsersCreditCsvGenerator; | |||
use common\logic\User\User\Service\UserSolver; | |||
use common\logic\User\User\Service\UserUtils; | |||
@@ -16,8 +18,10 @@ class UserContainer extends AbstractContainer | |||
return [ | |||
UserDefinition::class, | |||
UserSolver::class, | |||
UserRepositoryQuery::class, | |||
UserRepository::class, | |||
UserBuilder::class, | |||
UsersCreditCsvGenerator::class, | |||
UserUtils::class, | |||
]; | |||
} |
@@ -6,6 +6,7 @@ use common\logic\AbstractManager; | |||
use common\logic\User\User\Repository\UserRepository; | |||
use common\logic\User\User\Service\UserBuilder; | |||
use common\logic\User\User\Service\UserDefinition; | |||
use common\logic\User\User\Service\UsersCreditCsvGenerator; | |||
use common\logic\User\User\Service\UserSolver; | |||
use common\logic\User\User\Service\UserUtils; | |||
@@ -15,6 +16,7 @@ use common\logic\User\User\Service\UserUtils; | |||
* @mixin UserRepository | |||
* @mixin UserBuilder | |||
* @mixin UserUtils | |||
* @mixin UsersCreditCsvGenerator | |||
*/ | |||
class UserManager extends AbstractManager | |||
{ |
@@ -47,4 +47,23 @@ class UserProducerRepository extends AbstractRepository | |||
$userProducer = $this->findOneUserProducer($user); | |||
return $userProducer ? $userProducer->bookmark : false; | |||
} | |||
public function findUserProducersWithNegativeOrPositiveCredit() | |||
{ | |||
return $this->createDefaultQuery() | |||
->filterHasNegativeOrPositiveCredit() | |||
->find(); | |||
} | |||
public function sumUserProducerCredits() | |||
{ | |||
$sumUserProducersCredits = 0; | |||
$userProducersWithNegativeOrPositiveCreditArray = $this->findUserProducersWithNegativeOrPositiveCredit(); | |||
foreach ($userProducersWithNegativeOrPositiveCreditArray as $userProducerWithNegativeOrPositiveCredit) { | |||
$sumUserProducersCredits += $userProducerWithNegativeOrPositiveCredit->credit; | |||
} | |||
return $sumUserProducersCredits; | |||
} | |||
} |
@@ -34,4 +34,11 @@ class UserProducerRepositoryQuery extends AbstractRepositoryQuery | |||
$this->andWhere(['bookmark' => $bookmark]); | |||
return $this; | |||
} | |||
public function filterHasNegativeOrPositiveCredit(): self | |||
{ | |||
$this->andWhere('user_producer.credit IS NOT NULL AND user_producer.credit != 0'); | |||
return $this; | |||
} | |||
} |
@@ -48,7 +48,7 @@ class UserProducerBuilder extends AbstractBuilder | |||
public function createUserProducer(User $user, Producer $producer, int $bookmark = 1): UserProducer | |||
{ | |||
$userProducer = $this->instanciateUserProducer($user, $producer, $bookmark); | |||
$this->saveCreate($userProducer); | |||
$this->create($userProducer); | |||
return $userProducer; | |||
} | |||
@@ -79,7 +79,7 @@ class UserProducerBuilder extends AbstractBuilder | |||
$userProducer->setCredit($userProducer->getCredit() - $creditHistory->getAmount()); | |||
} | |||
$this->saveUpdate($userProducer); | |||
$this->update($userProducer); | |||
} | |||
public function initMeanPaymentOrder($creditHistory) | |||
@@ -94,7 +94,7 @@ class UserProducerBuilder extends AbstractBuilder | |||
$order->mean_payment = MeanPayment::CREDIT; | |||
$this->saveUpdate($order); | |||
$this->update($order); | |||
} | |||
} | |||
} | |||
@@ -149,13 +149,13 @@ class UserProducerBuilder extends AbstractBuilder | |||
$userProducer->active = $active; | |||
return $this->saveUpdate($userProducer); | |||
return $this->update($userProducer); | |||
} | |||
public function updateBookmark(UserProducer $userProducer, bool $bookmark) | |||
{ | |||
$userProducer->bookmark = $bookmark; | |||
return $this->saveUpdate($userProducer); | |||
return $this->update($userProducer); | |||
} | |||
public function addProducerBookmark(User $user) | |||
@@ -169,4 +169,11 @@ class UserProducerBuilder extends AbstractBuilder | |||
$userProducer = $this->createUserProducerIfNotExist($user, $this->getProducerContext()); | |||
return $this->updateBookmark($userProducer, false); | |||
} | |||
public function unlinkUserProducer(UserProducer $userProducer) | |||
{ | |||
$userProducer->active = 0; | |||
$userProducer->bookmark = 0; | |||
return $this->update($userProducer); | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
<?php | |||
namespace common\logic\User\UserProducer\Service; | |||
use common\logic\AbstractSolver; | |||
use common\logic\User\UserProducer\Model\UserProducer; | |||
class UserProducerSolver extends AbstractSolver | |||
{ | |||
public function hasOutstandingCredit(UserProducer $userProducer): bool | |||
{ | |||
return $userProducer->credit < 0 || $userProducer->credit > 0; | |||
} | |||
} |
@@ -6,6 +6,7 @@ use common\logic\AbstractContainer; | |||
use common\logic\User\UserProducer\Repository\UserProducerRepository; | |||
use common\logic\User\UserProducer\Service\UserProducerBuilder; | |||
use common\logic\User\UserProducer\Service\UserProducerDefinition; | |||
use common\logic\User\UserProducer\Service\UserProducerSolver; | |||
class UserProducerContainer extends AbstractContainer | |||
{ | |||
@@ -13,6 +14,7 @@ class UserProducerContainer extends AbstractContainer | |||
{ | |||
return [ | |||
UserProducerDefinition::class, | |||
UserProducerSolver::class, | |||
UserProducerRepository::class, | |||
UserProducerBuilder::class, | |||
]; |
@@ -0,0 +1,27 @@ | |||
<?php | |||
use yii\db\Migration; | |||
/** | |||
* Class m230825_090016_active_user_producer_with_credit_positive_or_negative | |||
*/ | |||
class m230825_090016_active_user_producer_with_credit_positive_or_negative extends Migration | |||
{ | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeUp() | |||
{ | |||
$this->execute('UPDATE user_producer SET active = 1 WHERE credit IS NOT NULL AND (credit < 0 OR credit > 0);'); | |||
} | |||
/** | |||
* {@inheritdoc} | |||
*/ | |||
public function safeDown() | |||
{ | |||
echo "m230825_090016_active_user_producer_with_credit_positive_or_negative cannot be reverted.\n"; | |||
return false; | |||
} | |||
} |