Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5c32801
Addition: Product search results display page
MohamedAbdulsalam96 May 13, 2025
4adad70
Better CSs
MohamedAbdulsalam96 May 16, 2025
2aa8afd
Merge branch 'frappe:develop' into Fadi
alaalsalam May 16, 2025
3fa1669
fix get shipping rule from another company
alaalsalam May 16, 2025
9c1ded5
add stock valiabel
MohamedAbdulsalam96 May 18, 2025
df5c753
Merge branch 'frappe:develop' into Fadi
alaalsalam May 18, 2025
45b440e
Merge remote-tracking branch 'upstream/develop' into Fadi
MohamedAbdulsalam96 May 18, 2025
f45096c
add hide Hide Unavailable Items and migrate branch
alaalsalam May 18, 2025
ca65be1
fix delete website item error
alaalsalam May 18, 2025
fe5181f
add Sort By to view product
alaalsalam May 19, 2025
7b2d6a0
add style to mobile view with fotter as mobile applicant
alaalsalam May 22, 2025
202c15a
Merge branch 'frappe:develop' into Fadi
alaalsalam May 22, 2025
440da2e
add confirm before request a quotation
alaalsalam May 22, 2025
8492063
Merge branch 'frappe:develop' into develop
alaalsalam May 31, 2025
a968777
add filter to phone view and add new style for phone view
alaalsalam Jun 1, 2025
d79e977
Merge remote-tracking branch 'upstream/develop' into develop
alaalsalam Jun 12, 2025
de3d7f4
megrate by alaalsalam
alaalsalam Jun 12, 2025
e23ce2b
get all stock Qty if no warhouse in website item and or get all qty f…
alaalsalam Jun 18, 2025
cc23b22
fix search in product page
alaalsalam Jun 21, 2025
25e3fdf
Merge branch 'frappe:develop' into develop
alaalsalam Jul 16, 2025
771b0e0
fix search
alaalsalam Jul 19, 2025
d79c1a5
Merge remote-tracking branch 'origin/develop' into develop
alaalsalam Jul 19, 2025
fc92319
Merge branch 'frappe:develop' into develop
alaalsalam Aug 14, 2025
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
Empty file added webshop/api/__init__.py
Empty file.
25 changes: 25 additions & 0 deletions webshop/api/product_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# apps/webshop/webshop/api/product_search.py

import frappe
from frappe import _

# @frappe.whitelist()
@frappe.whitelist(allow_guest = True)
def product_search(q):
if not q:
return []

or_filters = [
["item_code", "like", f"%{q}%"],
["item_name", "like", f"%{q}%"],
["web_long_description", "like", f"%{q}%"],
["web_item_name", "like", f"%{q}%"]
]

return frappe.get_all(
"Website Item",
fields=["name", "item_code", "item_name", "item_group", "website_image", "web_item_name", "route"],
or_filters=or_filters,
filters={"published": 1},
limit_page_length=20
)
23 changes: 23 additions & 0 deletions webshop/api/search Temp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import frappe
import json
from webshop.api.search import product_search
from webshop.api import get_webshop_settings


def get_context(context):
# query = frappe.form_dict.get("query") or ""
query = frappe.form_dict.get("query") or frappe.form_dict.get("q") or ""

context.query = query
context.products = []
context.webshop_settings = get_webshop_settings()

if query:
context.products = product_search(query)

# تمرير المنتجات كـ JSON إلى JavaScript مع حماية
context.products_json = json.dumps(context.products or [], default=str)
context.webshop_settings_json = json.dumps(context.webshop_settings or {}, default=str)

return context

52 changes: 52 additions & 0 deletions webshop/api/search copy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# apps/webshop/webshop/api/search.py

import frappe
from frappe import _
import json
from frappe.utils.data import flt

@frappe.whitelist()
def product_search(q):
if not q:
return []

or_filters = [
["item_code", "like", f"%{q}%"],
["item_name", "like", f"%{q}%"],
["web_long_description", "like", f"%{q}%"],
["web_item_name", "like", f"%{q}%"]
]

items = frappe.get_all(
"Website Item",
fields=["name", "item_code", "item_name", "item_group", "website_image", "web_item_name", "route"],
or_filters=or_filters,
filters={"published": 1},
limit_page_length=20
)

for item in items:
item_doc = frappe.get_doc("Item", item.item_code)

# جلب المخزون من الـ Website Item نفسه أو إعدادات النظام
warehouse = frappe.db.get_value("Website Item", item.name, "website_warehouse") or \
frappe.db.get_single_value("Stock Settings", "default_warehouse")

# جلب الكمية المتوفرة
actual_qty = 0
if item.item_code and warehouse:
actual_qty = frappe.db.get_value("Bin", {
"item_code": item.item_code,
"warehouse": warehouse
}, "actual_qty") or 0

# تحديث خصائص المنتج بالمخزون
item.update({
"stock_qty": flt(actual_qty),
"in_stock": actual_qty > 0,
"is_stock": item_doc.is_stock_item,
"on_backorder": item_doc.delivered_by_supplier,
"has_variants": item_doc.has_variants
})

return items
60 changes: 60 additions & 0 deletions webshop/api/search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# apps/webshop/webshop/api/search.py

import frappe
from frappe import _
import json
from frappe.utils.data import flt

@frappe.whitelist(allow_guest = True)
def product_search(q):
if not q:
return []

or_filters = [
["item_code", "like", f"%{q}%"],
["item_name", "like", f"%{q}%"],
["web_long_description", "like", f"%{q}%"],
["web_item_name", "like", f"%{q}%"]
]

raw_items = frappe.get_all(
"Website Item",
fields=["name", "item_code", "item_name", "item_group", "website_image", "web_item_name", "route"],
or_filters=or_filters,
filters={"published": 1},
limit_page_length=20
)

hide_unavailable = frappe.db.get_single_value("Webshop Settings", "hide_unavailable_items") or 0

visible_items = []

for item in raw_items:
item_doc = frappe.get_doc("Item", item.item_code)

warehouse = frappe.db.get_value("Website Item", item.name, "website_warehouse") or \
frappe.db.get_single_value("Stock Settings", "default_warehouse")

actual_qty = 0
if item.item_code and warehouse:
actual_qty = frappe.db.get_value("Bin", {
"item_code": item.item_code,
"warehouse": warehouse
}, "actual_qty") or 0

updated_item = item.copy()
updated_item.update({
"stock_qty": flt(actual_qty),
"in_stock": actual_qty > 0,
"is_stock": item_doc.is_stock_item,
"on_backorder": item_doc.delivered_by_supplier,
"has_variants": item_doc.has_variants
})

if hide_unavailable:
if updated_item["in_stock"] or updated_item["on_backorder"]:
visible_items.append(updated_item)
else:
visible_items.append(updated_item)

return visible_items
18 changes: 16 additions & 2 deletions webshop/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,23 @@

required_apps = ["payments", "erpnext"]

web_include_css = "webshop-web.bundle.css"
# web_include_css = "webshop-web.bundle.css"

web_include_css = [
"webshop-web.bundle.css",
"/assets/webshop/css/custom_style.css"
]

web_include_js = "web.bundle.js"

# app_include_css = [
# "/assets/webshop/css/custom_style.css"
# ]

app_include_js = [
"/assets/webshop/js/search.js"
]

after_install = "webshop.setup.install.after_install"
on_logout = "webshop.webshop.shopping_cart.utils.clear_cart_count"
on_session_creation = [
Expand All @@ -23,6 +36,7 @@
update_website_context = [
"webshop.webshop.shopping_cart.utils.update_website_context",
]
my_account_context = "webshop.webshop.shopping_cart.utils.update_my_account_context"

website_generators = ["Website Item", "Item Group"]

Expand Down Expand Up @@ -75,4 +89,4 @@
has_website_permission = {
"Website Item": "webshop.webshop.doctype.website_item.website_item.has_website_permission_for_website_item",
"Item Group": "webshop.webshop.doctype.website_item.website_item.has_website_permission_for_item_group"
}
}
124 changes: 124 additions & 0 deletions webshop/public/css/custom_style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
.page-header-wrapper {
display: flex;
justify-content: center;
align-items: center;
}

.filters-section {
background-color: white;
padding: 20px;
}

/* ========== Mobile Bottom Nav ========== */

@media (min-width: 992px) {
.mobile-bottom-nav {
display: none !important;
}
}
.mobile-bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 9999;
display: flex;
justify-content: space-around;
align-items: center;
background: #ffffff;
border-top: 1px solid #ddd;
box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.05);
padding: 6px 0;
font-size: 12px;
}

.mobile-bottom-nav .nav-item {
text-align: center;
flex: 1;
color: #444;
text-decoration: none;
position: relative;
}

.mobile-bottom-nav .nav-item svg.icon,
.mobile-bottom-nav .nav-item .icon-sm,
.mobile-bottom-nav .nav-item .icon-svg {
width: 20px;
height: 20px;
fill: #333;
margin-bottom: 2px;
display: block;
}

/* ========== Badges ========== */
.cart-badge,
.shopping-badge {
position: absolute;
top: 0;
background-color: #e60023;
color: white;
font-size: 10px;
font-weight: bold;
padding: 2px 6px;
border-radius: 999px;
line-height: 1;
min-width: 18px;
text-align: center;
display: none;
width: fit-content;
left: 50%;
transform: translate(-30px, 0);
}

/* ========== Animation on Add ========== */
.bounce {
animation: bounce 0.4s ease;
}

@keyframes bounce {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}


.product-paging-area {
margin-bottom: 60px;
}

.breadcrumb-container {
margin-top: 2.5rem;
}

#filters-btn {
display: block;
position: fixed;
bottom: 80px;
left: 11px;
z-index: 100;
background-color: #0289f7;
border-radius: 50%;
padding: 7px;
}

#filters-btn svg {
stroke: #ffffff;
height: 29px;
width: 20px;
}

.btn-add-to-cart-list {
text-wrap: auto;
height: auto;
max-height: unset;
width: fit-content;
min-width: 100px !important;
padding: 0.25rem 0.2rem !important;
}
Loading