Skip to content

Updated ItemsSelector to switch filter tabs dynamically#163

Open
MohamedAliSmk wants to merge 5 commits intodevelopfrom
brands_filter
Open

Updated ItemsSelector to switch filter tabs dynamically#163
MohamedAliSmk wants to merge 5 commits intodevelopfrom
brands_filter

Conversation

@MohamedAliSmk
Copy link
Collaborator

If sort field is brand, tabs show brands (All Brands + brand list). Otherwise tabs show item groups (All Items + item groups). Replaced the sort option from item_group to brand in ItemsSelector. Added brand-aware filter handling in itemSearch store: New state: selectedBrand, brands
New resource: brandsResource -> calls pos_next.api.items.get_brands New actions: loadBrands(), setSelectedBrand()
Updated server calls to include brand where needed (get_items, get_items_count) for: tab selection
pagination
search
Added brand sorting in computed filteredItems (sortBy === 'brand'). Kept group flow intact; switching between modes clears the opposite filter to avoid conflicts.

MohamedAliSmk and others added 4 commits February 22, 2026 15:44
…de pos with some code change in api that return items after filter pos profile data
If sort field is brand, tabs show brands (All Brands + brand list).
Otherwise tabs show item groups (All Items + item groups).
Replaced the sort option from item_group to brand in ItemsSelector.
Added brand-aware filter handling in itemSearch store:
New state: selectedBrand, brands
New resource: brandsResource -> calls pos_next.api.items.get_brands
New actions: loadBrands(), setSelectedBrand()
Updated server calls to include brand where needed (get_items, get_items_count) for:
tab selection
pagination
search
Added brand sorting in computed filteredItems (sortBy === 'brand').
Kept group flow intact; switching between modes clears the opposite filter to avoid conflicts.
Copy link
Contributor

@engahmed1190 engahmed1190 left a comment

Choose a reason for hiding this comment

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

PR #163 — Full Review

Note: The GitHub diff only shows 2 files (frontend), but the branch includes 4 earlier commits with backend changes (items.py, pos_profile.py, POS Brands Detail doctype). This review covers the full branch diff against develop.


🔴 CRITICAL — Build-Breaking: Duplicate case 'brand' in filteredItems sort switch

POS/src/stores/itemSearch.js lines 590–614

The sort switch block has two identical case 'brand': entries, each declaring const brandA and const brandB. Since all case blocks in a switch share the same lexical scope in JavaScript, this causes:

SyntaxError: Identifier 'brandA' has already been declared

The app will not build. This is a fatal error.

case 'brand':                          // line 590
    const brandA = (a.brand || '').toLowerCase()
    const brandB = (b.brand || '').toLowerCase()
    compareResult = brandA.localeCompare(brandB)
    break

case 'item_group':
    // ...
    break

case 'brand':                          // line 609 — DUPLICATE
    const brandA = (a.brand || '').toLowerCase()   // ← SyntaxError
    const brandB = (b.brand || '').toLowerCase()
    compareResult = brandA.localeCompare(brandB)
    break

Fix: Remove the second case 'brand': block (lines 609–614) entirely.


🔴 CRITICAL — Duplicate brand in SORT_OPTIONS renders two "Brand" buttons

POS/src/components/sale/ItemsSelector.vue lines 853–867

SORT_OPTIONS has field: 'brand' at index 1 (line 854) and again at index 3 (line 864). The UI will render two separate "Brand" buttons in the sort dropdown with different icons. The second entry also replaced what was originally item_group.

SORT_OPTIONS = [
    { field: 'name', ... },
    { field: 'brand', ... },      // line 853 — first brand entry
    { field: 'quantity', ... },
    { field: 'brand', ... },      // line 863 — DUPLICATE, was item_group
    { field: 'price', ... },
    { field: 'item_code', ... },
]

Fix: Remove the duplicate at line 863–867 and restore the item_group sort option.


🔴 CRITICAL — get_items() does not accept or forward brand parameter

pos_next/api/items.py line 1096

Current signature:

def get_items(pos_profile, search_term=None, item_group=None, start=0, limit=20,
              include_variants=0, show_variants_as_items=0):

No brand parameter. The call to _build_item_base_conditions() at lines 1124–1128 never passes brand:

conditions, params, extra_joins = _build_item_base_conditions(
    pos_profile_doc, item_group, exclude_variants=exclude_variants,
    exclude_templates=exclude_templates,
    hide_unavailable=hide_unavailable, warehouse=pos_profile_doc.warehouse,
)

The frontend passes brand in these calls that all target get_items:

  • fetchItemsForBrand() — line 1093: brand: brand
  • searchItems() — line 1626: brand: selectedBrand.value
  • setSelectedBrand() "All Items" — line 2010: brand: undefined
  • setSelectedItemGroup() "All Items" — line 1884: brand: undefined
  • fetchPage() — line 1173: brand: undefined
  • loadMoreItems() — line 1272: brand: undefined

Frappe silently ignores unknown kwargs, so brand filtering is completely non-functional server-side. Users will see unfiltered results when clicking a brand tab.

Fix: Add brand=None to the get_items() signature and pass brand=brand to _build_item_base_conditions().


🔴 CRITICAL — get_items_count() does not accept or forward brand parameter

pos_next/api/items.py line 1642

def get_items_count(pos_profile, item_group=None, include_variants=0, show_variants_as_items=0):

No brand parameter. The frontend passes brand in:

  • setSelectedBrand() count call — line 1995: brand: brand || undefined
  • setSelectedItemGroup() count call — line 1867: brand: undefined

The count will always be the total item count, not brand-filtered, causing incorrect pagination numbers.

Fix: Add brand=None to the signature and pass brand=brand to _build_item_base_conditions().


🔴 CRITICAL — No Custom Field fixture for brands on POS Profile

The POS Brands Detail child doctype is created (istable: 1), and pos_profile.py does pos_profile.append("brands", {"brand": brand_name}) in both create_pos_profile() (line 556) and update_pos_profile() (line 638). However:

  1. hooks.py fixtures — no POS Profile-brands entry exists
  2. custom_field.json — no Table field for brands
  3. No install.py / after_migrate / after_install hook creates the field programmatically
  4. No Property Setter or Client Script adds it dynamically

Without a brands Table field (type Table, options POS Brands Detail) on the POS Profile doctype:

  • pos_profile.append("brands", ...) will raise an error at save time
  • _get_allowed_profile_brands() queries tabPOS Brands Detail with parent = pos_profile — always returns empty
  • get_brands() falls back to returning all brands globally (up to 50), ignoring POS Profile config entirely

Fix: Add a Custom Field entry to the fixtures in hooks.py:

"POS Profile-brands"  # fieldtype: Table, options: POS Brands Detail

And create the corresponding fixture JSON or ensure it gets exported.


🟡 MEDIUM — Broken indentation in loadMoreItems

POS/src/stores/itemSearch.js lines 1254–1282

The if / else if / else block has inconsistent indentation:

[4 tabs] if (selectedItemGroup.value) {
[4 tabs]     list = await fetchItemsForGroup(...)
[4 tabs] } else if (selectedBrand.value) {
[5 tabs]     list = await fetchItemsForBrand(...)    ← extra indent in body
[3 tabs] } else {                                     ← drops one indent level
[4 tabs]     const response = await call(...)
[6 tabs]         brand: undefined,                    ← extra indent vs sibling props
[3 tabs] }                                            ← mismatched closing brace

This won't cause a syntax error (braces still match), but it's confusing and suggests the else if block was pasted without adjusting indentation.


🟡 MEDIUM — item_group sort option removed

SORT_OPTIONS in ItemsSelector.vue no longer has field: 'item_group'. It was replaced by the (duplicate) second brand entry. The case 'item_group' handler still exists in the sort switch (line 602), but it's unreachable from the UI since there's no button for it.

Users lose the ability to sort by item group.

Fix: Keep both item_group and brand as separate sort options.


🟡 MEDIUM — Eager loadBrands() on every POS session start

loadBrands() is called inside setPosProfile() (around line 2094 in the store), meaning every POS session makes an extra API call to get_brands even if the user never uses brand sorting.

Fix: Only call loadBrands() lazily when sortBy switches to 'brand' (the watcher in ItemsSelector.vue already does this — remove the eager call from setPosProfile).


Summary

# Severity Issue Location
1 🔴 Build-breaking Duplicate case 'brand'SyntaxError on const brandA redeclaration itemSearch.js:590,609
2 🔴 Build-breaking Duplicate field: 'brand' in SORT_OPTIONS — two "Brand" buttons ItemsSelector.vue:853,863
3 🔴 Critical get_items() ignores brand param — no server-side filtering items.py:1096
4 🔴 Critical get_items_count() ignores brand param — wrong pagination counts items.py:1642
5 🔴 Critical No Custom Field for brands on POS Profile — brands can't be saved hooks.py fixtures
6 🟡 Medium Broken indentation in loadMoreItems itemSearch.js:1254-1282
7 🟡 Medium item_group sort option removed from UI ItemsSelector.vue:863
8 🟡 Medium Unnecessary loadBrands() call on every session start itemSearch.js setPosProfile

dynamic change of sort option based on the selected filter
Fixed:
1. Removed duplicate case 'brand' from itemSearch.js:590,609
2. Removed duplicate field: 'brand' in SORT_OPTIONS from ItemsSelector.vue:853,863
3. Added brand param to get_items() and get_items_count() in items.py
4. Added Custom Field for brands as export customization and removed all custom fields from POS Profile in hooks.py fixtures
5. Fixed indentation in loadMoreItems in itemSearch.js:1254-1282
6. Added item_group sort option back to UI in ItemsSelector.vue:863
7. Removed unnecessary loadBrands() call on every session start in itemSearch.js setPosProfile
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.

2 participants