Skip to content

Commit

Permalink
- product categories api
Browse files Browse the repository at this point in the history
- updated adv search dialog
  • Loading branch information
HardMax71 committed Aug 30, 2024
1 parent 16b593b commit 8a0a17f
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 46 deletions.
2 changes: 1 addition & 1 deletion desktop_app/src/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .dashboard import DashboardWidget
from .main_window import MainWindow
from .report_generator import ReportGeneratorWidget
from .search_filter import AdvancedSearchDialog
from .advanced_search import AdvancedSearchDialog
from .settings.system_diagnostics import SystemDiagnosticsWidget
from .views.auth import LoginDialog
from .views.customers import CustomerDialog, CustomerDetailsDialog, CustomerView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from PySide6.QtCore import Qt, QDate
from PySide6.QtWidgets import (QVBoxLayout, QHBoxLayout, QLineEdit, QComboBox, QTableWidget, QTableWidgetItem,
QDialog, QLabel, QCheckBox, QDoubleSpinBox, QDateEdit,
QGroupBox, QGridLayout, QWidget)
QGroupBox, QGridLayout, QWidget, QSpinBox)

from desktop_app.src.ui.components import StyledButton
from desktop_app.src.ui.components.icon_path import IconPath
from public_api.api import APIClient, SearchAPI
from public_api.api import APIClient, SearchAPI, CategoriesAPI
from public_api.shared_schemas.order import OrderStatus


Expand All @@ -17,6 +17,7 @@ def __init__(self, api_client: APIClient, parent=None):
super().__init__(parent)
self.api_client = api_client
self.search_api = SearchAPI(api_client)
self.categories_api = CategoriesAPI(api_client)
self.init_ui()

def init_ui(self):
Expand Down Expand Up @@ -76,10 +77,16 @@ def setup_product_search_options(self):

self.product_search_term = QLineEdit()
self.product_category = QComboBox()
self.product_category.addItem("All Categories")
self.product_category = QComboBox()
self.product_category.addItem("All Categories", None)
self.load_categories()
self.min_price = QDoubleSpinBox()
self.min_price.setRange(0, 1000000)
self.max_price = QDoubleSpinBox()
self.max_price.setRange(0, 1000000)
self.in_stock = QCheckBox("In Stock Only")
self.min_quantity = QSpinBox()
self.min_quantity.setRange(0, 1000000)

layout.addWidget(QLabel("Search Term:"), 0, 0)
layout.addWidget(self.product_search_term, 0, 1)
Expand All @@ -90,6 +97,8 @@ def setup_product_search_options(self):
layout.addWidget(QLabel("Max Price:"), 3, 0)
layout.addWidget(self.max_price, 3, 1)
layout.addWidget(self.in_stock, 4, 0, 1, 2)
layout.addWidget(QLabel("Min Quantity:"), 5, 0)
layout.addWidget(self.min_quantity, 5, 1)

group_box.setLayout(layout)
self.search_options_layout.addWidget(group_box)
Expand All @@ -102,11 +111,14 @@ def setup_order_search_options(self):
self.order_status = QComboBox()
self.order_status.addItems(["All"] + [status.value for status in OrderStatus])
self.min_total = QDoubleSpinBox()
self.min_total.setRange(0, 1000000)
self.max_total = QDoubleSpinBox()
self.max_total.setRange(0, 1000000)
self.start_date = QDateEdit()
self.start_date.setDate(QDate.currentDate().addDays(-30))
self.end_date = QDateEdit()
self.end_date.setDate(QDate.currentDate())
self.customer_id = QLineEdit()

layout.addWidget(QLabel("Search Term:"), 0, 0)
layout.addWidget(self.order_search_term, 0, 1)
Expand All @@ -120,10 +132,17 @@ def setup_order_search_options(self):
layout.addWidget(self.start_date, 4, 1)
layout.addWidget(QLabel("End Date:"), 5, 0)
layout.addWidget(self.end_date, 5, 1)
layout.addWidget(QLabel("Customer ID:"), 6, 0)
layout.addWidget(self.customer_id, 6, 1)

group_box.setLayout(layout)
self.search_options_layout.addWidget(group_box)

def load_categories(self):
categories = self.categories_api.get_categories()
for category in categories:
self.product_category.addItem(category.name, category.id)

def perform_search(self):
if self.search_type.currentText() == "Products":
results = self.search_products()
Expand All @@ -135,10 +154,11 @@ def perform_search(self):
def search_products(self):
return self.search_api.search_products(
q=self.product_search_term.text(),
category_id=self.product_category.currentIndex() if self.product_category.currentIndex() > 0 else None,
category_id=self.product_category.currentData(),
min_price=self.min_price.value() if self.min_price.value() > 0 else None,
max_price=self.max_price.value() if self.max_price.value() > 0 else None,
in_stock=self.in_stock.isChecked()
in_stock=self.in_stock.isChecked(),
min_quantity=self.min_quantity.value() if self.min_quantity.value() > 0 else None
)

def search_orders(self):
Expand All @@ -151,14 +171,16 @@ def search_orders(self):
min_total=self.min_total.value() if self.min_total.value() > 0 else None,
max_total=self.max_total.value() if self.max_total.value() > 0 else None,
start_date=int(start_date.timestamp()),
end_date=int(end_date.timestamp())
end_date=int(end_date.timestamp()),
customer_id=self.customer_id.text() if self.customer_id.text() else None
)

def display_results(self, results):
self.results_table.clear()
self.results_table.setRowCount(0)
self.results_table.setColumnCount(0)

if not results:
self.results_table.setRowCount(0)
self.results_table.setColumnCount(0)
return

self.results_table.setRowCount(len(results))
Expand All @@ -171,7 +193,9 @@ def display_results(self, results):
value = json.dumps(value, indent=2)
elif isinstance(value, datetime):
value = value.strftime("%Y-%m-%d %H:%M:%S")
self.results_table.setItem(row, col, QTableWidgetItem(str(value)))
table_item = QTableWidgetItem(str(value))
table_item.setFlags(table_item.flags() & ~Qt.ItemIsEditable) # Make item read-only
self.results_table.setItem(row, col, table_item)

self.results_table.resizeColumnsToContents()

Expand All @@ -182,5 +206,4 @@ def on_header_clicked(self, logical_index):
else:
self.results_table.sortItems(logical_index, Qt.AscendingOrder)
else:
self.results_table.sortItems(logical_index, Qt.AscendingOrder)

self.results_table.sortItems(logical_index, Qt.AscendingOrder)
2 changes: 1 addition & 1 deletion desktop_app/src/ui/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from .dashboard import DashboardWidget
from .qtutorial import QTutorialManager
from .report_generator import ReportGeneratorWidget
from .search_filter import AdvancedSearchDialog
from .advanced_search import AdvancedSearchDialog
from .settings import SettingsDialog


Expand Down
3 changes: 2 additions & 1 deletion public_api/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .assets import AssetsAPI
from .audit import AuditAPI
from .carriers import CarriersAPI
from .categories import CategoriesAPI
from .client import APIClient
from .customers import CustomersAPI
from .inventory import InventoryAPI
Expand All @@ -15,10 +16,10 @@
from .purchase_orders import PurchaseOrdersAPI
from .quality import QualityAPI
from .reports import ReportsAPI
from .roles import RolesAPI
from .search import SearchAPI
from .shipments import ShipmentsAPI
from .suppliers import SuppliersAPI
from .tasks import TasksAPI
from .users import UsersAPI
from .warehouse import WarehouseAPI
from .roles import RolesAPI
28 changes: 28 additions & 0 deletions public_api/api/categories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# public_api/api/categories.py

from typing import List
from public_api.shared_schemas import ProductCategory, ProductCategoryCreate, ProductCategoryUpdate
from .client import APIClient

class CategoriesAPI:
def __init__(self, client: APIClient):
self.client = client

def create_category(self, category: ProductCategoryCreate) -> ProductCategory:
response = self.client.post("/product_categories/", json=category.model_dump())
return ProductCategory.model_validate(response)

def get_categories(self, skip: int = 0, limit: int = 100) -> List[ProductCategory]:
response = self.client.get(f"/product_categories/?skip={skip}&limit={limit}")
return [ProductCategory.model_validate(item) for item in response]

def get_category(self, category_id: int) -> ProductCategory:
response = self.client.get(f"/product_categories/{category_id}")
return ProductCategory.model_validate(response)

def update_category(self, category_id: int, category: ProductCategoryUpdate) -> ProductCategory:
response = self.client.put(f"/product_categories/{category_id}", json=category.model_dump())
return ProductCategory.model_validate(response)

def delete_category(self, category_id: int) -> None:
self.client.delete(f"/product_categories/{category_id}")
6 changes: 5 additions & 1 deletion public_api/api/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ def search_products(
category_id: int | None = None,
min_price: float | None = None,
max_price: float | None = None,
in_stock: str | bool = None,
in_stock: bool | None = None,
min_quantity: int | None = None,
sort_by: str | None = None,
sort_order: str | None = "asc"
) -> list[Product]:
Expand All @@ -22,6 +23,7 @@ def search_products(
"min_price": min_price,
"max_price": max_price,
"in_stock": in_stock,
"min_quantity": min_quantity,
"sort_by": sort_by,
"sort_order": sort_order
}
Expand All @@ -36,6 +38,7 @@ def search_orders(
max_total: float | None = None,
start_date: int | None = None,
end_date: int | None = None,
customer_id: int | None = None,
sort_by: str | None = None,
sort_order: str | None = "asc"
) -> list[Order]:
Expand All @@ -46,6 +49,7 @@ def search_orders(
"max_total": max_total,
"start_date": start_date,
"end_date": end_date,
"customer_id": customer_id,
"sort_by": sort_by,
"sort_order": sort_order
}
Expand Down
9 changes: 6 additions & 3 deletions server/app/api/v1/endpoints/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ def search_products(
min_price: float | None = Query(None),
max_price: float | None = Query(None),
in_stock: bool | None = Query(None),
min_quantity: int | None = Query(None),
sort_by: str | None = Query(None),
sort_order: str | None = Query("asc"),
db: Session = Depends(deps.get_db),
current_user: models.User = Depends(deps.get_current_active_user)
):
return crud.product.advanced_search(
db, q=q, category_id=category_id, min_price=min_price,
max_price=max_price, in_stock=in_stock,
max_price=max_price, in_stock=in_stock, min_quantity=min_quantity,
sort_by=sort_by, sort_order=sort_order
)

Expand All @@ -35,6 +36,7 @@ def search_orders(
max_total: float | None = Query(None),
start_date: int | None = Query(None),
end_date: int | None = Query(None),
customer_id: str | None = Query(None),
sort_by: str | None = Query(None),
sort_order: str | None = Query("asc"),
db: Session = Depends(deps.get_db),
Expand All @@ -43,5 +45,6 @@ def search_orders(
return crud.order.advanced_search(
db, q=q, status=status, min_total=min_total,
max_total=max_total, start_date=start_date,
end_date=end_date, sort_by=sort_by, sort_order=sort_order
)
end_date=end_date, customer_id=customer_id,
sort_by=sort_by, sort_order=sort_order
)
88 changes: 60 additions & 28 deletions server/app/crud/order.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from datetime import datetime

from fastapi import HTTPException
from sqlalchemy import func
from sqlalchemy import func, or_, String
from sqlalchemy.orm import Session, joinedload

from public_api.shared_schemas import (
Order as OrderSchema,
OrderWithDetails as OrderWithDetailsSchema,
OrderCreate, OrderUpdate, OrderItemCreate, OrderItemUpdate,
OrderFilter, OrderSummary, ShippingInfo, BulkOrderImportData,
BulkOrderImportResult, OrderProcessingTimes, OrderStatus
BulkOrderImportResult, OrderProcessingTimes, OrderStatus, OrderWithDetails
)
from server.app.models import Order, OrderItem
from server.app.models import Order, OrderItem, Customer
from .base import CRUDBase


Expand Down Expand Up @@ -48,36 +50,66 @@ def get_multi_with_details(self, db: Session, *,
orders = query.offset(skip).limit(limit).all()
return [OrderWithDetailsSchema.model_validate(x) for x in orders]

def advanced_search(self, db: Session, *, q: str = None, status: str = None, min_total: float = None,
max_total: float = None, start_date: int = None, end_date: int = None,
sort_by: str = None, sort_order: str = "asc") -> list[OrderSchema]:
query = db.query(self.model).options(joinedload(Order.customer))
def advanced_search(
self,
db: Session,
*,
q: str | None = None,
status: str | None = None,
min_total: float | None = None,
max_total: float | None = None,
start_date: int | None = None,
end_date: int | None = None,
customer_id: str | None = None,
sort_by: str | None = None,
sort_order: str | None = "asc",
skip: int = 0,
limit: int = 100
) -> list[OrderWithDetails]:
query = db.query(Order).options(
joinedload(Order.customer),
joinedload(Order.order_items).joinedload(OrderItem.product)
)

# Apply filters
if q:
query = query.filter(Order.customer_id.ilike(f"%{q}%"))
query = query.filter(
or_(
Order.id.cast(String).ilike(f"%{q}%"),
Order.customer.has(Customer.name.ilike(f"%{q}%")),
Order.customer.has(Customer.email.ilike(f"%{q}%"))
)
)

if status:
query = query.filter(Order.status == status)
if min_total:

if min_total is not None:
query = query.filter(Order.total_amount >= min_total)
if max_total:

if max_total is not None:
query = query.filter(Order.total_amount <= max_total)
if start_date:
query = query.filter(Order.order_date >= start_date)
if end_date:
query = query.filter(Order.order_date <= end_date)

if start_date is not None:
query = query.filter(Order.order_date >= datetime.fromtimestamp(start_date))

if end_date is not None:
query = query.filter(Order.order_date <= datetime.fromtimestamp(end_date))

if customer_id:
query = query.filter(Order.customer_id == customer_id)

if sort_by:
if sort_by == "total_amount":
if sort_order == "asc":
query = query.order_by(Order.total_amount.asc())
else:
query = query.order_by(Order.total_amount.desc())
elif sort_by == "order_date":
if sort_order == "asc":
query = query.order_by(Order.order_date.asc())
else:
query = query.order_by(Order.order_date.desc())

orders = query.all()
return [OrderSchema.model_validate(order) for order in orders]
sort_column = getattr(Order, sort_by, None)
if sort_column is not None:
if sort_order and sort_order.lower() == "desc":
sort_column = sort_column.desc()
query = query.order_by(sort_column)

# Apply pagination
orders = query.offset(skip).limit(limit).all()

return [OrderWithDetails.model_validate(order) for order in orders]

def get_summary(self, db: Session, date_from: int | None, date_to: int | None) -> OrderSummary:
query = db.query(func.count(Order.id).label("total_orders"),
Expand Down Expand Up @@ -122,7 +154,7 @@ def cancel_item(self, db: Session, *, order_id: int, item_id: int) -> OrderSchem
db.delete(item)

if not order.order_items:
order.status = "cancelled"
order.status = OrderStatus.CANCELLED

db.add(order)
db.commit()
Expand Down
Loading

0 comments on commit 8a0a17f

Please sign in to comment.