Search code examples
yii2rbac

How can Manager see their own customers in yii2 using RBAC


I have this a customer table where I have customername, address and salesmanagername. The invoice data is stored in another table - invoice, where I have Invoice details,customername. I have a relation between customer.customername and invoice.customername. I have a user table through which users log in. SalesManager's fullname is stored in the user table. My query is SalesManager can view his customer related data only when he logs in. I am using RBAC with DbManager & Yii framework. I've created a module,model and crud application - It shows invoice details for all the customers along with the respective salesmanager name. I want SalesManager to see only data related to him. Please let me know if any more information needed. I've read http://www.yiiframework.com/doc-2.0/guide-security-authorization.html but couldn't make it out. Please help.

Here is my Bills Model

<?php

namespace frontend\modules\salebills\models;

use Yii;

/**
 * This is the model class for table "bills".
 *
 * @property string $billid
 * @property integer $bills_ebillid
 * @property string $bills_year
 * @property string $console
 * @property string $billno
 * @property string $billdate
 * @property string $bills_partyname
 * @property integer $billamount
 * @property string $pdate1
 * @property integer $payment1
 * @property string $details1
 * @property string $pdate2
 * @property integer $payment2
 * @property string $details2
 * @property string $pdate3
 * @property integer $payment3
 * @property string $details3
 * @property string $pdate4
 * @property integer $payment4
 * @property string $details4
 * @property integer $totalpayment
 * @property integer $bills_tc
 * @property integer $bills_tc_approval
 * @property integer $doctorsgift
 * @property integer $mrcommision
 * @property string $mrname
 * @property integer $bills_other
 * @property string $bills_specify_other
 * @property integer $bills_other_approval
 * @property integer $overdue
 * @property string $cst
 * @property string $wbst
 * @property integer $caseno
 * @property string $amount
 * @property string $discount
 * @property string $tot
 * @property string $surcharge
 * @property string $total
 * @property string $tax
 * @property string $mrpvalue
 * @property string $cstpercent
 * @property string $wbstpercent
 * @property string $surpercent
 * @property string $totpercent
 * @property string $transport
 *
 * @property Parties $billsPartyname
 * @property Productsales[] $productsales
 */
class Bills extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'bills';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['bills_ebillid'], 'required'],
            [['bills_ebillid', 'billamount', 'payment1', 'payment2', 'payment3', 'payment4', 'totalpayment', 'bills_tc', 'bills_tc_approval', 'doctorsgift', 'mrcommision', 'bills_other', 'bills_other_approval', 'overdue', 'caseno'], 'integer'],
            [['billdate', 'pdate1', 'pdate2', 'pdate3', 'pdate4'], 'safe'],
            [['mrname', 'bills_specify_other'], 'string'],
            [['bills_year'], 'string', 'max' => 8],
            [['console', 'cstpercent', 'wbstpercent', 'surpercent', 'totpercent'], 'string', 'max' => 6],
            [['billno'], 'string', 'max' => 10],
            [['bills_partyname'], 'string', 'max' => 60],
            [['details1', 'details2', 'details3', 'details4'], 'string', 'max' => 50],
            [['cst', 'wbst', 'amount', 'discount', 'tot', 'surcharge', 'total', 'tax', 'mrpvalue'], 'string', 'max' => 15],
            [['transport'], 'string', 'max' => 30],
            [['bills_ebillid'], 'unique']
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'billid' => 'Billid',
            'bills_ebillid' => 'Bills Ebillid',
            'bills_year' => 'Bills Year',
            'console' => 'Console',
            'billno' => 'Billno',
            'billdate' => 'Billdate',
            'bills_partyname' => 'Bills Partyname',
            'billamount' => 'Billamount',
            'pdate1' => 'Pdate1',
            'payment1' => 'Payment1',
            'details1' => 'Details1',
            'pdate2' => 'Pdate2',
            'payment2' => 'Payment2',
            'details2' => 'Details2',
            'pdate3' => 'Pdate3',
            'payment3' => 'Payment3',
            'details3' => 'Details3',
            'pdate4' => 'Pdate4',
            'payment4' => 'Payment4',
            'details4' => 'Details4',
            'totalpayment' => 'Totalpayment',
            'bills_tc' => 'Bills Tc',
            'bills_tc_approval' => 'Bills Tc Approval',
            'doctorsgift' => 'Doctorsgift',
            'mrcommision' => 'Mrcommision',
            'mrname' => 'Mrname',
            'bills_other' => 'Bills Other',
            'bills_specify_other' => 'Bills Specify Other',
            'bills_other_approval' => 'Bills Other Approval',
            'overdue' => 'Overdue',
            'cst' => 'Cst',
            'wbst' => 'Wbst',
            'caseno' => 'Caseno',
            'amount' => 'Amount',
            'discount' => 'Discount',
            'tot' => 'Tot',
            'surcharge' => 'Surcharge',
            'total' => 'Total',
            'tax' => 'Tax',
            'mrpvalue' => 'Mrpvalue',
            'cstpercent' => 'Cstpercent',
            'wbstpercent' => 'Wbstpercent',
            'surpercent' => 'Surpercent',
            'totpercent' => 'Totpercent',
            'transport' => 'Transport',
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getBillsPartyname()
    {
        return $this->hasOne(Parties::className(), ['parties_partyname' => 'bills_partyname']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getProductsales()
    {
        return $this->hasMany(Productsales::className(), ['productsales_ebillid' => 'bills_ebillid']);
    }
}

Here is my BillsSearch Model

<?php

namespace frontend\modules\salebills\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use frontend\modules\salebills\models\Bills;

/**
 * BillsSearch represents the model behind the search form about `frontend\modules\salebills\models\Bills`.
 */
class BillsSearch extends Bills
{
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['billid', 'bills_ebillid', 'billamount', 'payment1', 'payment2', 'payment3', 'payment4', 'totalpayment', 'bills_tc', 'bills_tc_approval', 'doctorsgift', 'mrcommision', 'bills_other', 'bills_other_approval', 'overdue', 'caseno'], 'integer'],
            [['bills_year', 'console', 'billno', 'billdate', 'bills_partyname', 'pdate1', 'details1', 'pdate2', 'details2', 'pdate3', 'details3', 'pdate4', 'details4', 'mrname', 'bills_specify_other', 'cst', 'wbst', 'amount', 'discount', 'tot', 'surcharge', 'total', 'tax', 'mrpvalue', 'cstpercent', 'wbstpercent', 'surpercent', 'totpercent', 'transport'], 'safe'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params)
    {
        $query = Bills::find();

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);

        $this->load($params);

        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            // $query->where('0=1');
            return $dataProvider;
        }

        $query->andFilterWhere([
            'billid' => $this->billid,
            'bills_ebillid' => $this->bills_ebillid,
            'billdate' => $this->billdate,
            'billamount' => $this->billamount,
            'pdate1' => $this->pdate1,
            'payment1' => $this->payment1,
            'pdate2' => $this->pdate2,
            'payment2' => $this->payment2,
            'pdate3' => $this->pdate3,
            'payment3' => $this->payment3,
            'pdate4' => $this->pdate4,
            'payment4' => $this->payment4,
            'totalpayment' => $this->totalpayment,
            'bills_tc' => $this->bills_tc,
            'bills_tc_approval' => $this->bills_tc_approval,
            'doctorsgift' => $this->doctorsgift,
            'mrcommision' => $this->mrcommision,
            'bills_other' => $this->bills_other,
            'bills_other_approval' => $this->bills_other_approval,
            'overdue' => $this->overdue,
            'caseno' => $this->caseno,
        ]);

        $query->andFilterWhere(['like', 'bills_year', $this->bills_year])
            ->andFilterWhere(['like', 'console', $this->console])
            ->andFilterWhere(['like', 'billno', $this->billno])
            ->andFilterWhere(['like', 'bills_partyname', $this->bills_partyname])
            ->andFilterWhere(['like', 'details1', $this->details1])
            ->andFilterWhere(['like', 'details2', $this->details2])
            ->andFilterWhere(['like', 'details3', $this->details3])
            ->andFilterWhere(['like', 'details4', $this->details4])
            ->andFilterWhere(['like', 'mrname', $this->mrname])
            ->andFilterWhere(['like', 'bills_specify_other', $this->bills_specify_other])
            ->andFilterWhere(['like', 'cst', $this->cst])
            ->andFilterWhere(['like', 'wbst', $this->wbst])
            ->andFilterWhere(['like', 'amount', $this->amount])
            ->andFilterWhere(['like', 'discount', $this->discount])
            ->andFilterWhere(['like', 'tot', $this->tot])
            ->andFilterWhere(['like', 'surcharge', $this->surcharge])
            ->andFilterWhere(['like', 'total', $this->total])
            ->andFilterWhere(['like', 'tax', $this->tax])
            ->andFilterWhere(['like', 'mrpvalue', $this->mrpvalue])
            ->andFilterWhere(['like', 'cstpercent', $this->cstpercent])
            ->andFilterWhere(['like', 'wbstpercent', $this->wbstpercent])
            ->andFilterWhere(['like', 'surpercent', $this->surpercent])
            ->andFilterWhere(['like', 'totpercent', $this->totpercent])
            ->andFilterWhere(['like', 'transport', $this->transport]);

        return $dataProvider;
    }
}

Here is my Parties model

<?php

namespace frontend\modules\salebills\models;

use Yii;

/**
 * This is the model class for table "parties".
 *
 * @property integer $party_id
 * @property string $parties_partyname
 * @property string $address
 * @property string $district
 * @property string $name_manager
 * @property string $transport
 * @property string $dlno
 * @property string $instruction
 *
 * @property Bills[] $bills
 * @property Productsales[] $productsales
 */
class Parties extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'parties';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['parties_partyname', 'district', 'name_manager'], 'required'],
            [['parties_partyname'], 'string', 'max' => 60],
            [['address', 'instruction'], 'string', 'max' => 100],
            [['district'], 'string', 'max' => 20],
            [['name_manager', 'transport', 'dlno'], 'string', 'max' => 30]
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'party_id' => 'Party ID',
            'parties_partyname' => 'Parties Partyname',
            'address' => 'Address',
            'district' => 'District',
            'name_manager' => 'Name Manager',
            'transport' => 'Transport',
            'dlno' => 'Dlno',
            'instruction' => 'Instruction',
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getBills()
    {
        return $this->hasMany(Bills::className(), ['bills_partyname' => 'parties_partyname']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getProductsales()
    {
        return $this->hasMany(Productsales::className(), ['productsales_partyname' => 'parties_partyname']);
    }
}

Here is my BillsController

<?php

namespace frontend\modules\salebills\controllers;

use Yii;
use frontend\modules\salebills\models\Bills;
use frontend\modules\salebills\models\BillsSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * BillsController implements the CRUD actions for Bills model.
 */
class BillsController extends Controller
{
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['post'],
                ],
            ],
        ];
    }

    /**
     * Lists all Bills models.
     * @return mixed
     */
    public function actionIndex()
    {
        $searchModel = new BillsSearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
       if ( Yii::$app->User->can('s_viewownsalebills')){
         $dataProvider->query->
             andWhere(['billsPartyname.name_manager'=> 'Bibekananda Acharya']);
        return $this->render('index', [
       'dataProvider' => $dataProvider,
    ]);
    }
   }
    //     return $this->render('index', [
    //         'searchModel' => $searchModel,
    //         'dataProvider' => $dataProvider,
    //     ]);
    // }

    /**
     * Displays a single Bills model.
     * @param string $id
     * @return mixed
     */
    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Creates a new Bills model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new Bills();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->billid]);
        } else {
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    }

    /**
     * Updates an existing Bills model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param string $id
     * @return mixed
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->billid]);
        } else {
            return $this->render('update', [
                'model' => $model,
            ]);
        }
    }

    /**
     * Deletes an existing Bills model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     * @param string $id
     * @return mixed
     */
    public function actionDelete($id)
    {
        $this->findModel($id)->delete();

        return $this->redirect(['index']);
    }

    /**
     * Finds the Bills model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param string $id
     * @return Bills the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = Bills::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}

Here is my updated actionindex

public function actionIndex()
    {
        //$salesManager  = Yii::$app->user->identity->fullname;      
        $salesManageModel = User::find()->where(['fullname' => Yii::$app->user->identity->fullname])->one();
        $salesManager  = $salesManageModel->fullname;
        $searchModel = new BillsSearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
       if ( Yii::$app->User->can('s_viewownsalebills')){
         $dataProvider->query->
             andWhere(['billsPartyname.name_manager'=> $salesManager]);
        return $this->render('index', [
       'dataProvider' => $dataProvider,
    ]);
    }
   }

Here is my index file

<?php

use yii\helpers\Html;
use yii\grid\GridView;

/* @var $this yii\web\View */
/* @var $searchModel frontend\modules\salebills\models\BillsSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */

$this->title = 'Bills';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="bills-index">

    <h1><?= Html::encode($this->title) ?></h1>
    <?php // echo $this->render('_search', ['model' => $searchModel]); ?>

    <!-- <p>
        <?= Html::a('Create Bills', ['create'], ['class' => 'btn btn-success']) ?>
    </p> -->

    <?= GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'columns' => [
            ['class' => 'yii\grid\SerialColumn'],

            //'billid',
            //'bills_ebillid',
            //'bills_year',
            //'console',
            'billno',
            'billdate',
            'bills_partyname',
            'billamount',
            'billsPartyname.name_manager'
            // 'pdate1',
            // 'payment1',
            // 'details1',
            // 'pdate2',
            // 'payment2',
            // 'details2',
            // 'pdate3',
            // 'payment3',
            // 'details3',
            // 'pdate4',
            // 'payment4',
            // 'details4',
            // 'totalpayment',
            // 'bills_tc',
            // 'bills_tc_approval',
            // 'doctorsgift',
            // 'mrcommision',
            // 'mrname:ntext',
            // 'bills_other',
            // 'bills_specify_other:ntext',
            // 'bills_other_approval',
            // 'overdue',
            // 'cst',
            // 'wbst',
            // 'caseno',
            // 'amount',
            // 'discount',
            // 'tot',
            // 'surcharge',
            // 'total',
            // 'tax',
            // 'mrpvalue',
            // 'cstpercent',
            // 'wbstpercent',
            // 'surpercent',
            // 'totpercent',
            // 'transport',

            //['class' => 'yii\grid\ActionColumn'],
        ],
    ]); ?>

</div>

Solution

  • For permit to a SalesManager to see only the data related to him you can access to proper view (gridView) based on a dataProvider with a filter fixed on the SalesManager code ..

    You can do this coupling teh RBAC functionality ( Yii::$app->User->can('SalesManager')) whit a proper search function in ModelSearch .. or simply adding a proper where condition to the dataProvider that retrive the sales ..

    eg: in you action ... somethings like this

        $searchModel = new YourSalesSearch(); 
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
        if ( Yii::$app->User->can('SalesManager') ){
              $dataProvider->query->
                  andWhere(['your_sales_manager_key'=> $actual_sales_manager_code]);
             return $this->render('your_sales_manager_view', [
            'dataProvider' => $dataProvider,
         ]);
        }
    
        if ( Yii::$app->User->can('GeneralManager') ){
              // the general manager see all the sales of all the sales manager
             return $this->render('your_general_manager_view', [
            'dataProvider' => $dataProvider,
         ]);
        }
    

    In you case you the indexAction could be

      public function actionIndex()
      {
    
          // You need the proper name for matching the name_manager 
          // If is the same of username you can use 
          $salesManager  = Yii::$app->user->identity->username
          // otherwhise you can find ........ whit proper
          // $salesManagerModel =  YourUserModel::find()->where(['your_column_name' => Yii::$app->user->identity->username])->one();
          // $salesManager  = $salesManagerModel->name;
          $searchModel = new BillsSearch();
          $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
         if ( Yii::$app->User->can('s_viewownsalebills')){
           $dataProvider->query->
               andWhere(['billsPartyname.name_manager'=>  $salesManager  ]);
          return $this->render('index', [
         'dataProvider' => $dataProvider,
      ]);
      }
    

    and for the searchModel undefined you can try commenting in your gridView in index.php

       <?= GridView::widget([
        'dataProvider' => $dataProvider,
        //'filterModel' => $searchModel,
    

    or add searchModel in your render call in actionIndex

               return $this->render('index', [
         'dataProvider' => $dataProvider,
         'searchModel' = $searchModel,
      ]);
    

    you havent't a join in BillsSearch for BillsPartyname i think you should change this way

      .......
    
        $this->load($params);
    
        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            // $query->where('0=1'); 
            $query->joinWith(['billsPartyname']);
            return $dataProvider;
        }
    
        .......