Skip to content

Comments

HP-2869: add installment page#583

Open
bladeroot wants to merge 11 commits intohiqdev:masterfrom
bladeroot:HP-2869
Open

HP-2869: add installment page#583
bladeroot wants to merge 11 commits intohiqdev:masterfrom
bladeroot:HP-2869

Conversation

@bladeroot
Copy link
Member

@bladeroot bladeroot commented Feb 1, 2026

Summary by CodeRabbit

  • New Features
    • Added Installments section in Finance with a sidebar entry and route alias.
    • New Installments listing page with sortable columns (id, start, finish) and representation options.
    • Advanced search for installments with seller, client and month range filters.
    • Grid view enhanced to show serial, model, device, start, finish, period, pagination and bulk actions.

@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2026

📝 Walkthrough

Walkthrough

Adds an Installments feature to the finance module: routing alias, controller, models (search), grid view and representations, menu entry, and index/search views for listing and filtering installments.

Changes

Cohort / File(s) Summary
Configuration
config/web.php
Added @installment alias mapped to /finance/installment.
Controller
src/controllers/InstallmentController.php
New CRUD-style controller; merges parent behaviors with EasyAccessControl ('sale.read') and maps a custom index action.
Models
src/models/Installment.php, src/models/InstallmentSearch.php
New Installment model with rules and attribute labels; InstallmentSearch extends it, aliases searchAttributes and adds month, time_from, time_till.
Grid & Representations
src/grid/InstallmentGridView.php, src/grid/InstallmentRepresentations.php
New InstallmentGridView (extends BoxedGridView) captures current user and adds serial, model, device columns; InstallmentRepresentations defines a 'common' column set.
Views
src/views/installment/_search.php, src/views/installment/index.php
New search partial (seller/client filters, MonthRangePicker) and index view wiring IndexPage, sorter, representations, and InstallmentGridView inside a bulk form.
Navigation
src/menus/SidebarMenu.php
Added "Installments" submenu entry linking to @installment/index.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Ctrl as InstallmentController
    participant Search as InstallmentSearch
    participant Repr as InstallmentRepresentations
    participant Grid as InstallmentGridView
    participant View as index.php

    User->>Ctrl: GET /finance/installment/index
    Ctrl->>Search: build search model with params
    Search-->>Ctrl: return dataProvider
    Ctrl->>Repr: request available representations
    Repr-->>Ctrl: return 'common' columns
    Ctrl->>Grid: instantiate with dataProvider & columns
    Grid->>Grid: init() captures current user (Yii::$app->user)
    Ctrl->>View: render index with Grid and representations
    View->>User: render page (sorter, repr selector, installment table)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • SilverFire

Poem

🐰 I nibble keys and hop on strings,
New Installments sprout with tiny springs,
Grids and searches set in line,
Menus point where ledgers shine,
A rabbit cheers — the code now sings!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an installment page with supporting infrastructure (controller, grid, model, views).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/models/Installment.php (2)

13-15: Remove unused imports.

SaleQuery, Server, and ActiveQuery are imported but none are referenced in this class.

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

38-61: Missing attribute labels for monetary sum fields.

paid_sum, monthly_sum, total_sum, and left_sum don't have labels defined, so they'll render as raw attribute names (e.g., "Paid Sum") in the grid/forms. Consider adding translated labels for these.

src/models/InstallmentSearch.php (2)

31-35: Remove empty attributeLabels() override.

This method just calls parent::attributeLabels() with no additions — it's dead code.

Proposed fix
-    public function attributeLabels()
-    {
-        return array_merge(parent::attributeLabels(), [
-        ]);
-    }

14-15: Unused imports.

Yii is imported but not used in this file. ArrayHelper is used, so that's fine.

Proposed fix
 use hipanel\helpers\ArrayHelper;
-use Yii;

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@src/controllers/InstallmentController.php`:
- Around line 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.

In `@src/menus/SidebarMenu.php`:
- Around line 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.

In `@src/models/InstallmentSearch.php`:
- Around line 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.

In `@src/views/installment/index.php`:
- Around line 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.
🧹 Nitpick comments (1)
src/models/InstallmentSearch.php (1)

31-35: Drop the redundant attributeLabels() override.

It merges with an empty array and adds no labels, so it can be removed for clarity.

♻️ Simplify
-    public function attributeLabels()
-    {
-        return array_merge(parent::attributeLabels(), [
-        ]);
-    }

Comment on lines +29 to +47
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,
],
]);
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.

Comment on lines +109 to +112
'installment' => [
'label' => Yii::t('hipanel:finance', 'Installments'),
'url' => ['@installment/index'],
],
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.

Comment on lines 24 to 29
public function searchAttributes()
{
return ArrayHelper::merge($this->defaultSearchAttributes(), [
'month',
]);
}
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.

Comment on lines +3 to +19
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
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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/grid/InstallmentGridView.php`:
- Around line 29-35: The serial column's link uses the installment model id
instead of the related part id; update the closure in InstallmentGridView that
builds the link (the anonymous function assigned to 'value' for the 'serial'
column) to use $model->part_id instead of $model->id and guard for null/empty
part_id (e.g., render plain encoded serial if part_id is missing) so the anchor
targets the correct `@part/view` record.
🧹 Nitpick comments (1)
src/grid/InstallmentGridView.php (1)

37-44: Silence unused-parameter warning in filter closure.

Line 38 declares $column but doesn’t use it; PHPMD flags this. Consider renaming to $_column to document intentional unusedness.

♻️ Suggested tweak
-                'filter' => function ($column, $model, $attribute) {
+                'filter' => function ($_column, $model, $attribute) {

Comment on lines +29 to +35
'serial' => [
'label' => Yii::t('hipanel:stock', 'Serial'),
'filterOptions' => ['class' => 'narrow-filter'],
'filterAttribute' => 'serial_ilike',
'format' => 'raw',
'value' => fn($model) => Html::a(Html::encode($model->serial), ['@part/view', 'id' => $model->id], ['class' => 'text-bold']),
],
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

Fix serial link to target the part ID (not installment ID).

Line 34 links to @part/view using $model->id, which appears to be the installment ID, not the part ID. This can open the wrong record or 404. Use $model->part_id (and optionally guard if it’s nullable).

✅ Suggested fix
-                'value' => fn($model) => Html::a(Html::encode($model->serial), ['@part/view', 'id' => $model->id], ['class' => 'text-bold']),
+                'value' => fn($model) => Html::a(Html::encode($model->serial), ['@part/view', 'id' => $model->part_id], ['class' => 'text-bold']),
📝 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
'serial' => [
'label' => Yii::t('hipanel:stock', 'Serial'),
'filterOptions' => ['class' => 'narrow-filter'],
'filterAttribute' => 'serial_ilike',
'format' => 'raw',
'value' => fn($model) => Html::a(Html::encode($model->serial), ['@part/view', 'id' => $model->id], ['class' => 'text-bold']),
],
'serial' => [
'label' => Yii::t('hipanel:stock', 'Serial'),
'filterOptions' => ['class' => 'narrow-filter'],
'filterAttribute' => 'serial_ilike',
'format' => 'raw',
'value' => fn($model) => Html::a(Html::encode($model->serial), ['@part/view', 'id' => $model->part_id], ['class' => 'text-bold']),
],
🤖 Prompt for AI Agents
In `@src/grid/InstallmentGridView.php` around lines 29 - 35, The serial column's
link uses the installment model id instead of the related part id; update the
closure in InstallmentGridView that builds the link (the anonymous function
assigned to 'value' for the 'serial' column) to use $model->part_id instead of
$model->id and guard for null/empty part_id (e.g., render plain encoded serial
if part_id is missing) so the anchor targets the correct `@part/view` record.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant