Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'@consumption' => '/finance/consumption',
'@pay/check-return' => '/finance/pay/check-return',
'@billing-registry' => '/finance/billing-registry',
'@installment' => '/finance/installment',
],
'modules' => [
'finance' => [
Expand Down
49 changes: 49 additions & 0 deletions src/controllers/InstallmentController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
/**
* Finance module for HiPanel
*
* @link https://github.com/hiqdev/hipanel-module-finance
* @package hipanel-module-finance
* @license BSD-3-Clause
* @copyright Copyright (c) 2015-2019, HiQDev (http://hiqdev.com/)
*/

namespace hipanel\modules\finance\controllers;

use hipanel\actions\IndexAction;
use hipanel\actions\SmartCreateAction;
use hipanel\actions\SmartDeleteAction;
use hipanel\actions\SmartUpdateAction;
use hipanel\actions\ValidateFormAction;
use hipanel\actions\ViewAction;
use hipanel\filters\EasyAccessControl;
use hipanel\modules\client\models\stub\ClientRelationFreeStub;
use hipanel\modules\finance\actions\ChangeBuyerAction;
use hipanel\modules\finance\models\Plan;
use hipanel\modules\finance\models\query\SaleQuery;
use Yii;
use yii\base\Event;

class InstallmentController extends \hipanel\base\CrudController
{
public function behaviors()
{
return array_merge(parent::behaviors(), [
[
'class' => EasyAccessControl::class,
'actions' => [
'*' => 'sale.read',
],
],
]);
}

public function actions()
{
return array_merge(parent::actions(), [
'index' => [
'class' => IndexAction::class,
],
]);
Comment on lines +29 to +47
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Step 1: Find the CrudController class
echo "=== Searching for CrudController class definition ==="
rg -l 'class\s+CrudController\b' --type php

echo -e "\n=== Inspecting CrudController actions method ==="
# Search for the actions() method in CrudController
rg -B 2 -A 20 'public\s+function\s+actions\s*\(' --type php | head -100

Repository: hiqdev/hipanel-module-finance

Length of output: 7106


🏁 Script executed:

#!/bin/bash
# Step 1: Check composer.json for hipanel dependencies
echo "=== Checking composer.json for hipanel dependencies ==="
cat composer.json | grep -A 10 '"require"'

# Step 2: Search for CrudController in vendor (if available) or documentation
echo -e "\n=== Searching for CrudController in repository ==="
find . -name 'CrudController.php' -type f 2>/dev/null | head -5

# Step 3: Look for base controller references in comments/imports
echo -e "\n=== Checking imports in InstallmentController ==="
head -30 src/controllers/InstallmentController.php

Repository: hiqdev/hipanel-module-finance

Length of output: 1564


🌐 Web query:

hipanel-core CrudController default actions

💡 Result:

In hipanel-core, CrudController follows Yii2’s standard CRUD convention: the “default” CRUD actions are:

  • index (list)
  • view (read one)
  • create (create)
  • update (update)
  • delete (delete)

This matches Yii2’s own CRUD generator/guide expectations (CRUD = Create/Read/Update/Delete, plus the listing page as index). [1][2]

Sources:
[1] Yii2 Guide – Generating CRUD code with Gii: https://www.yiiframework.com/doc/guide/2.0/en/start-gii
[2] Yii2 Guide – Controller/action conventions (action IDs like view, update, etc.): https://www.yiiframework.com/doc/guide/2.0/en/structure-controllers


Remove write action exposure and restrict access to index only.

The EasyAccessControl with '*' => 'sale.read' combined with array_merge(parent::actions(), ...) exposes create/update/delete actions with only read-level permissions. This grants write access based on a read-only permission and should be restricted to the index action.

🔧 Suggested change
 public function behaviors()
 {
     return array_merge(parent::behaviors(), [
         [
             'class' => EasyAccessControl::class,
             'actions' => [
-                '*' => 'sale.read',
+                'index' => 'sale.read',
             ],
         ],
     ]);
 }

 public function actions()
 {
-    return array_merge(parent::actions(), [
-        'index' => [
-            'class' => IndexAction::class,
-        ],
-    ]);
+    return [
+        'index' => [
+            'class' => IndexAction::class,
+        ],
+    ];
 }
🤖 Prompt for AI Agents
In `@src/controllers/InstallmentController.php` around lines 29 - 47, The
behaviors() currently maps '*' => 'sale.read' on EasyAccessControl which grants
read permission to all actions; change it to only map the index action (e.g.,
'index' => 'sale.read') and remove the wildcard entry; also update actions() so
you do not merge parent::actions() (which may expose create/update/delete) and
instead return only the index action definition (keep IndexAction::class) to
ensure only the index route is exposed and protected by sale.read.

}
}
30 changes: 30 additions & 0 deletions src/grid/InstallmentGridView.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace hipanel\modules\finance\grid;

use hipanel\components\User;
use hipanel\grid\BoxedGridView;
use hipanel\modules\finance\models\Sale;
use hipanel\modules\finance\models\Target;
use hipanel\modules\server\models\Server;
use Yii;
use yii\helpers\Html;

class InstallmentGridView extends BoxedGridView
{
private User $user;

public function init()
{
parent::init();
$this->user = Yii::$app->user;
}

public function columns(): array
{
return array_merge(parent::columns(), [
]);
}
}
32 changes: 32 additions & 0 deletions src/grid/InstallmentRepresentations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* Finance module for HiPanel
*
* @link https://github.com/hiqdev/hipanel-module-finance
* @package hipanel-module-finance
* @license BSD-3-Clause
* @copyright Copyright (c) 2015-2019, HiQDev (http://hiqdev.com/)
*/

namespace hipanel\modules\finance\grid;

use hiqdev\higrid\representations\RepresentationCollection;
use Yii;

class InstallmentRepresentations extends RepresentationCollection
{
protected function fillRepresentations()
{
$this->representations = array_filter([
'common' => [
'label' => Yii::t('hipanel', 'common'),
'columns' => [
'checkbox',
'client', 'seller',
'serial', 'model', 'device',
'start', 'finish', 'period',
],
],
]);
}
}
6 changes: 5 additions & 1 deletion src/menus/SidebarMenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,15 @@ public function items()
'url' => ['@consumption/index'],
'visible' => $user->can('consumption.read'),
],
'installment' => [
'label' => Yii::t('hipanel:finance', 'Installments'),
'url' => ['@installment/index'],
],
Comment on lines +109 to +112
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add permission-based visibility for the Installments menu item.

Without a visible guard, users lacking sale.read may see the item but get a 403 when opening it. Align with the controller permission.

🔧 Suggested change
 'installment' => [
     'label'   => Yii::t('hipanel:finance', 'Installments'),
     'url'     => ['@installment/index'],
+    'visible' => $user->can('sale.read'),
 ],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'installment' => [
'label' => Yii::t('hipanel:finance', 'Installments'),
'url' => ['@installment/index'],
],
'installment' => [
'label' => Yii::t('hipanel:finance', 'Installments'),
'url' => ['@installment/index'],
'visible' => $user->can('sale.read'),
],
🤖 Prompt for AI Agents
In `@src/menus/SidebarMenu.php` around lines 109 - 112, The 'installment' menu
item in SidebarMenu.php is missing a visibility guard and should be shown only
to users with the controller's permission; update the 'installment' array entry
to include a visible check that calls the RBAC helper (e.g. use
Yii::$app->user->can('sale.read')) so the menu only renders when the user has
the 'sale.read' permission.

'billing-registry' => [
'label' => Yii::t('hipanel:finance', 'Billing registry'),
'url' => ['@billing-registry/index'],
'visible' => $user->can('owner-staff'),
]
],
],
],
];
Expand Down
61 changes: 61 additions & 0 deletions src/models/Installment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
/**
* Finance module for HiPanel
*
* @link https://github.com/hiqdev/hipanel-module-finance
* @package hipanel-module-finance
* @license BSD-3-Clause
* @copyright Copyright (c) 2015-2026, HiQDev (http://hiqdev.com/)
*/

namespace hipanel\modules\finance\models;

use hipanel\modules\finance\models\query\SaleQuery;
use hipanel\modules\server\models\Server;
use hiqdev\hiart\ActiveQuery;
use Yii;

/**
* Class Installment.
*
* @author Yurii Myronchuk <bladeroot@gmail.com>
*/
class Installment extends \hipanel\base\Model
{
use \hipanel\base\ModelTrait;

public function rules()
{
return array_merge(parent::rules(), [
[['id', 'client_id', 'seller_id', 'part_id', 'model_id', 'model_type_id', 'model_brand_id', 'device_id'], 'integer'],
[['client', 'seller', 'serial', 'model', 'model_type', 'model_type_label', 'model_brand', 'model_brand_label', 'device'], 'string'],
[['start', 'finish'], 'datetime', 'format' => 'php:Y-m-d'],
[['period'], 'integer'],
]);
}

public function attributeLabels()
{
return array_merge(parent::attributeLabels(), [
'client' => Yii::t('hipanel:finance:sale', 'Buyer'),
'client_id' => Yii::t('hipanel:finance:sale', 'Buyer'),
'seller' => Yii::t('hipanel:finance:sale', 'Seller'),
'seller_id' => Yii::t('hipanel:finance:sale', 'Seller'),
'part_id' => Yii::t('hipanel:stock', 'Part No.'),
'serial' => Yii::t('hipanel:stock', 'Serial'),
'model_id' => Yii::t('hipanel:stock', 'Part No.'),
'model' => Yii::t('hipanel:stock', 'Part No.'),
'model_type_id' => Yii::t('hipanel', 'Type'),
'model_type' => Yii::t('hipanel', 'Type'),
'model_type_label' => Yii::t('hipanel', 'Type'),
'model_brand_id' => Yii::t('hipanel:stock', 'Manufacturer'),
'model_brand' => Yii::t('hipanel:stock', 'Manufacturer'),
'model_brand_label' => Yii::t('hipanel:stock', 'Manufacturer'),
'device_id' => Yii::t('hipanel:finance:sale', 'Device'),
'device' => Yii::t('hipanel:finance:sale', 'Device'),
'start' => Yii::t('hipanel:finance:sale', 'Installment start'),
'finish' => Yii::t('hipanel:finance:sale', 'Installment end'),
'period' => Yii::t('hipanel:finance:sale', 'Periods left'),
]);
}
}
36 changes: 36 additions & 0 deletions src/models/InstallmentSearch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/**
* Finance module for HiPanel
*
* @link https://github.com/hiqdev/hipanel-module-finance
* @package hipanel-module-finance
* @license BSD-3-Clause
* @copyright Copyright (c) 2015-2019, HiQDev (http://hiqdev.com/)
*/

namespace hipanel\modules\finance\models;

use hipanel\base\SearchModelTrait;
use hipanel\helpers\ArrayHelper;
use Yii;

class InstallmentSearch extends Installment
{

use SearchModelTrait {
searchAttributes as defaultSearchAttributes;
}

public function searchAttributes()
{
return ArrayHelper::merge($this->defaultSearchAttributes(), [
'month',
]);
}
Comment on lines 24 to 29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "InstallmentSearch.php" | head -20

Repository: hiqdev/hipanel-module-finance

Length of output: 107


🏁 Script executed:

find . -type f -name "Installment.php" | grep -E "models|src" | head -20

Repository: hiqdev/hipanel-module-finance

Length of output: 101


🏁 Script executed:

cat -n ./src/models/InstallmentSearch.php

Repository: hiqdev/hipanel-module-finance

Length of output: 1096


🏁 Script executed:

cat -n ./src/models/Installment.php

Repository: hiqdev/hipanel-module-finance

Length of output: 2949


🏁 Script executed:

find . -type f -name "SearchModelTrait.php" | head -10

Repository: hiqdev/hipanel-module-finance

Length of output: 55


🏁 Script executed:

rg -A 20 "class SearchModelTrait" --type php

Repository: hiqdev/hipanel-module-finance

Length of output: 55


🏁 Script executed:

find . -type f -name "*.php" -exec grep -l "searchAttributes" {} \; | head -20

Repository: hiqdev/hipanel-module-finance

Length of output: 523


🏁 Script executed:

rg "use SearchModelTrait" --type php -A 10 | head -50

Repository: hiqdev/hipanel-module-finance

Length of output: 2796


🏁 Script executed:

cat -n ./src/models/RequisiteSearch.php

Repository: hiqdev/hipanel-module-finance

Length of output: 1775


🏁 Script executed:

cat -n ./src/models/SaleSearch.php

Repository: hiqdev/hipanel-module-finance

Length of output: 2123


Add a rules() method for the new month search filter.

In Yii2 search models, attributes must be declared in rules() to be mass-assigned from request data. Without this, the month filter added to searchAttributes() won't be populated. See RequisiteSearch.php for the correct pattern. Also remove the empty attributeLabels() override on lines 31–35.

Suggested fix
 class InstallmentSearch extends Installment
 {
+    public function rules()
+    {
+        return array_merge(parent::rules(), [
+            [['month'], 'safe'],
+        ]);
+    }
+
     use SearchModelTrait {
         searchAttributes as defaultSearchAttributes;
     }
🤖 Prompt for AI Agents
In `@src/models/InstallmentSearch.php` around lines 24 - 29, The InstallmentSearch
class added 'month' to searchAttributes() but did not declare it in rules(), so
add a public function rules() to this search model that merges/returns
validation rules including ['month', 'safe'] (following the pattern used in
RequisiteSearch::rules()) so the month field can be mass-assigned from request
data; also remove the empty attributeLabels() override (delete the
attributeLabels() method) to avoid redundant code.


public function attributeLabels()
{
return array_merge(parent::attributeLabels(), [
]);
}
}
22 changes: 22 additions & 0 deletions src/views/installment/_search.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/** @var \hipanel\widgets\AdvancedSearch $search */

use hipanel\modules\client\widgets\combo\ClientCombo;
use hipanel\modules\client\widgets\combo\SellerCombo;
use hipanel\modules\finance\widgets\TariffCombo;
use hiqdev\combo\StaticCombo;
use hiqdev\yii2\daterangepicker\DateRangePicker;
use yii\bootstrap\Html;

?>

<div class="col-md-4 col-sm-6 col-xs-12">
<?= $search->field('seller_id')->widget(SellerCombo::class) ?>
</div>

<div class="col-md-4 col-sm-6 col-xs-12">
<?= $search->field('client_id')->widget(ClientCombo::class) ?>
</div>


50 changes: 50 additions & 0 deletions src/views/installment/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

use hipanel\models\IndexPageUiOptions;
use hipanel\modules\finance\grid\InstallmentGridView;
use hipanel\modules\finance\grid\InstallmentRepresentations;
use hipanel\modules\finance\models\SInstallmentSearch;
use hipanel\widgets\gridLegend\GridLegend;
use hipanel\widgets\IndexPage;
use yii\bootstrap\Dropdown;
use yii\bootstrap\Html;
use yii\data\ActiveDataProvider;
use yii\web\View;

/**
* @var View $this
* @var SaleSearch $model
* @var IndexPageUiOptions $uiModel
* @var SaleRepresentations $representationCollection
* @var ActiveDataProvider $dataProvider
Comment on lines +3 to +19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the import/type hints to match Installment classes.

The use statement and docblock still reference Sale types and likely a typo SInstallmentSearch, which can trip IDEs/static analysis.

🔧 Suggested change
-use hipanel\modules\finance\models\SInstallmentSearch;
+use hipanel\modules\finance\models\InstallmentSearch;

 /**
  * `@var` View $this
- * `@var` SaleSearch $model
+ * `@var` InstallmentSearch $model
  * `@var` IndexPageUiOptions $uiModel
- * `@var` SaleRepresentations $representationCollection
+ * `@var` InstallmentRepresentations $representationCollection
  * `@var` ActiveDataProvider $dataProvider
  */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
use hipanel\models\IndexPageUiOptions;
use hipanel\modules\finance\grid\InstallmentGridView;
use hipanel\modules\finance\grid\InstallmentRepresentations;
use hipanel\modules\finance\models\SInstallmentSearch;
use hipanel\widgets\gridLegend\GridLegend;
use hipanel\widgets\IndexPage;
use yii\bootstrap\Dropdown;
use yii\bootstrap\Html;
use yii\data\ActiveDataProvider;
use yii\web\View;
/**
* @var View $this
* @var SaleSearch $model
* @var IndexPageUiOptions $uiModel
* @var SaleRepresentations $representationCollection
* @var ActiveDataProvider $dataProvider
use hipanel\models\IndexPageUiOptions;
use hipanel\modules\finance\grid\InstallmentGridView;
use hipanel\modules\finance\grid\InstallmentRepresentations;
use hipanel\modules\finance\models\InstallmentSearch;
use hipanel\widgets\gridLegend\GridLegend;
use hipanel\widgets\IndexPage;
use yii\bootstrap\Dropdown;
use yii\bootstrap\Html;
use yii\data\ActiveDataProvider;
use yii\web\View;
/**
* `@var` View $this
* `@var` InstallmentSearch $model
* `@var` IndexPageUiOptions $uiModel
* `@var` InstallmentRepresentations $representationCollection
* `@var` ActiveDataProvider $dataProvider
🤖 Prompt for AI Agents
In `@src/views/installment/index.php` around lines 3 - 19, The file imports and
docblock still reference Sale types and a likely-incorrect SInstallmentSearch;
update the use statements and PHPDoc to the correct Installment types: replace
SInstallmentSearch with InstallmentSearch (or the actual search class name used
in hipanel\modules\finance\models), and change the docblock `@var` SaleSearch and
`@var` SaleRepresentations to `@var` InstallmentSearch and `@var`
InstallmentRepresentations (and ensure IndexPageUiOptions, InstallmentGridView,
InstallmentRepresentations, and the ActiveDataProvider/View types remain
correct); adjust any mismatched class names so static analysis/IDEs see
consistent Installment* types.

*/

$user = Yii::$app->user;
$this->title = Yii::t('hipanel:finance:sale', 'Installment');
$this->params['breadcrumbs'][] = $this->title;
$subtitle = array_filter(Yii::$app->request->get($model->formName(), [])) ? Yii::t('hipanel', 'filtered list') : Yii::t('hipanel', 'full list');

?>

<?php $page = IndexPage::begin(['model' => $model, 'dataProvider' => $dataProvider]) ?>

<?php $page->beginContent('sorter-actions') ?>
<?= $page->renderSorter(['attributes' => ['id', 'start', 'finish']]) ?>
<?php $page->endContent() ?>

<?php $page->beginContent('representation-actions') ?>
<?= $page->renderRepresentations($representationCollection) ?>
<?php $page->endContent() ?>
<?php $page->beginContent('table') ?>
<?php $page->beginBulkForm() ?>
<?= InstallmentGridView::widget([
'boxed' => false,
'dataProvider' => $dataProvider,
'filterModel' => $model,
'columns' => $representationCollection->getByName($uiModel->representation)->getColumns(),
]) ?>
<?php $page->endBulkForm() ?>
<?php $page->endContent() ?>
<?php $page->end() ?>