Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
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()
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