feat(arch): add API-007 rule to enforce layered architecture
Add architecture rule that detects when API routes import database models directly, enforcing Routes → Services → Models pattern. Changes: - Add API-007 rule to .architecture-rules/api.yaml - Add _check_no_model_imports() validation to validator script - Update customer imports to use canonical module location - Add storefront module restructure implementation plan The validator now detects 81 violations across 67 API files where database models are imported directly instead of going through services. This is Phase 1 of the storefront restructure plan. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -204,3 +204,53 @@ api_endpoint_rules:
|
||||
file_pattern: "app/api/v1/shop/**/*.py"
|
||||
discouraged_patterns:
|
||||
- "db.query(.*).all()"
|
||||
|
||||
- id: "API-007"
|
||||
name: "API endpoints must NOT import database models directly"
|
||||
severity: "error"
|
||||
description: |
|
||||
API endpoints must follow the layered architecture: Routes → Services → Models.
|
||||
Routes should NEVER import database models directly.
|
||||
|
||||
WHY THIS MATTERS:
|
||||
- Layered architecture: Each layer has clear responsibilities
|
||||
- Testability: Routes can be tested without database
|
||||
- Flexibility: Database schema changes don't affect route signatures
|
||||
- Type safety: Schemas define the API contract, not database structure
|
||||
|
||||
For dependency injection (e.g., current user/customer), use schemas instead
|
||||
of database models as return types.
|
||||
|
||||
WRONG:
|
||||
from models.database.customer import Customer
|
||||
from models.database.order import Order
|
||||
from app.modules.customers.models.customer import Customer
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: Customer = Depends(get_current_customer)):
|
||||
...
|
||||
|
||||
RIGHT:
|
||||
from app.modules.customers.schemas import CustomerContext
|
||||
from app.modules.orders.services import order_service
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: CustomerContext = Depends(get_current_customer)):
|
||||
return order_service.get_orders(db, customer.id, customer.vendor_id)
|
||||
|
||||
ALLOWED IMPORTS IN API ROUTES:
|
||||
- Schemas: from models.schema.* or from app.modules.*.schemas
|
||||
- Services: from app.services.* or from app.modules.*.services
|
||||
- Dependencies: from app.api.deps
|
||||
- Database session: from app.core.database import get_db
|
||||
|
||||
NOT ALLOWED:
|
||||
- from models.database.*
|
||||
- from app.modules.*.models.*
|
||||
pattern:
|
||||
file_pattern: "app/api/**/*.py"
|
||||
anti_patterns:
|
||||
- "from models\\.database\\."
|
||||
- "from app\\.modules\\.[a-z_]+\\.models\\."
|
||||
exclude_files:
|
||||
- "app/api/deps.py" # Dependencies may need model access for queries
|
||||
|
||||
@@ -145,7 +145,7 @@ except ImportError as e:
|
||||
# CUSTOMER MODELS
|
||||
# ----------------------------------------------------------------------------
|
||||
try:
|
||||
from models.database.customer import Customer, CustomerAddress
|
||||
from app.modules.customers.models.customer import Customer, CustomerAddress
|
||||
|
||||
print(" ✓ Customer models imported (2 models)")
|
||||
print(" - Customer")
|
||||
|
||||
@@ -740,7 +740,7 @@ def _validate_customer_token(token: str, request: Request, db: Session):
|
||||
|
||||
from jose import JWTError, jwt
|
||||
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
|
||||
# Decode and validate customer JWT token
|
||||
try:
|
||||
|
||||
@@ -19,7 +19,7 @@ from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.exceptions import AdminOperationException, VendorNotFoundException
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from models.database.inventory import Inventory
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
|
||||
@@ -144,7 +144,7 @@ class CardService:
|
||||
Returns:
|
||||
(cards, total_count)
|
||||
"""
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
|
||||
query = (
|
||||
db.query(LoyaltyCard)
|
||||
|
||||
@@ -25,7 +25,7 @@ from app.modules.messaging.models.message import (
|
||||
MessageAttachment,
|
||||
ParticipantType,
|
||||
)
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from models.database.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1122,7 +1122,7 @@ class EmailService:
|
||||
|
||||
# 2. Customer's preferred language
|
||||
if customer_id:
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
|
||||
customer = (
|
||||
self.db.query(Customer).filter(Customer.id == customer_id).first()
|
||||
|
||||
344
docs/proposals/PLAN_storefront-module-restructure.md
Normal file
344
docs/proposals/PLAN_storefront-module-restructure.md
Normal file
@@ -0,0 +1,344 @@
|
||||
# Implementation Plan: Storefront Module Restructure
|
||||
|
||||
## Overview
|
||||
|
||||
Restructure the platform to properly separate **core platform concerns** (customer auth/identity) from **e-commerce concerns** (cart, checkout, catalog), and rename "shop" to "storefront" throughout.
|
||||
|
||||
## Current Issues
|
||||
|
||||
1. **API routes import database models directly** - violates layered architecture
|
||||
2. **No architecture rule enforces this** - validator doesn't catch it
|
||||
3. **"shop" naming is misleading** - not all platforms sell items
|
||||
4. **Customer auth is bundled with e-commerce** - should be core/platform concern
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Add Missing Architecture Rule
|
||||
|
||||
### Task 1.1: Add API-007 rule to prevent direct model imports
|
||||
|
||||
**File:** `.architecture-rules/api.yaml`
|
||||
|
||||
Add new rule:
|
||||
```yaml
|
||||
- id: "API-007"
|
||||
name: "API endpoints must NOT import database models directly"
|
||||
severity: "error"
|
||||
description: |
|
||||
API endpoints should import from services, not directly from database models.
|
||||
This enforces the layered architecture: Routes → Services → Models.
|
||||
|
||||
The only exception is for type hints in FastAPI dependency injection,
|
||||
which should use schemas or services that return the appropriate types.
|
||||
|
||||
WRONG:
|
||||
from models.database.customer import Customer
|
||||
from models.database.order import Order
|
||||
|
||||
RIGHT:
|
||||
from app.modules.customers.services import customer_service
|
||||
from models.schema.customer import CustomerResponse
|
||||
pattern:
|
||||
file_pattern: "app/api/**/*.py"
|
||||
anti_patterns:
|
||||
- "from models\\.database\\."
|
||||
- "from app\\.modules\\..*\\.models\\."
|
||||
```
|
||||
|
||||
### Task 1.2: Update validator to check this rule
|
||||
|
||||
**File:** `scripts/validate_architecture.py`
|
||||
|
||||
Add validation for API-007.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Rename "shop" to "storefront"
|
||||
|
||||
### Task 2.1: Rename API directory
|
||||
```
|
||||
app/api/v1/shop/ → app/api/v1/storefront/
|
||||
```
|
||||
|
||||
### Task 2.2: Update all imports referencing shop
|
||||
- Update router registrations in `app/api/v1/__init__.py`
|
||||
- Update any references in middleware
|
||||
- Update documentation
|
||||
|
||||
### Task 2.3: Rename routes/pages if applicable
|
||||
```
|
||||
app/routes/shop_pages.py → app/routes/storefront_pages.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Create New E-commerce Modules
|
||||
|
||||
### Task 3.1: Create `cart` module
|
||||
```
|
||||
app/modules/cart/
|
||||
├── __init__.py
|
||||
├── definition.py
|
||||
├── config.py
|
||||
├── exceptions.py
|
||||
├── models/
|
||||
│ ├── __init__.py
|
||||
│ └── cart.py # Cart, CartItem models (if persistent)
|
||||
├── schemas/
|
||||
│ ├── __init__.py
|
||||
│ └── cart.py # Move from models/schema/cart.py
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── cart_service.py # Move from app/services/cart_service.py
|
||||
└── routes/
|
||||
└── api/
|
||||
├── __init__.py
|
||||
└── storefront.py # Cart endpoints for customers
|
||||
```
|
||||
|
||||
### Task 3.2: Create `checkout` module
|
||||
```
|
||||
app/modules/checkout/
|
||||
├── __init__.py
|
||||
├── definition.py
|
||||
├── config.py
|
||||
├── exceptions.py
|
||||
├── schemas/
|
||||
│ ├── __init__.py
|
||||
│ └── checkout.py # CheckoutRequest, CheckoutResponse
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── checkout_service.py # Orchestrates cart → order conversion
|
||||
└── routes/
|
||||
└── api/
|
||||
├── __init__.py
|
||||
└── storefront.py # Place order endpoint
|
||||
```
|
||||
|
||||
### Task 3.3: Create `catalog` module
|
||||
```
|
||||
app/modules/catalog/
|
||||
├── __init__.py
|
||||
├── definition.py
|
||||
├── config.py
|
||||
├── schemas/
|
||||
│ ├── __init__.py
|
||||
│ └── catalog.py # ProductListResponse, ProductDetailResponse
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ └── catalog_service.py # Customer-facing product queries
|
||||
└── routes/
|
||||
└── api/
|
||||
├── __init__.py
|
||||
└── storefront.py # Product browsing endpoints
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Move Storefront Routes to Modules
|
||||
|
||||
### Task 4.1: Move customer auth routes to `customers` module
|
||||
|
||||
**From:** `app/api/v1/storefront/auth.py`
|
||||
**To:** `app/modules/customers/routes/api/storefront.py`
|
||||
|
||||
Endpoints:
|
||||
- `POST /auth/register` - Customer registration
|
||||
- `POST /auth/login` - Customer login
|
||||
- `POST /auth/logout` - Customer logout
|
||||
- `POST /auth/password-reset/request` - Request password reset
|
||||
- `POST /auth/password-reset/confirm` - Confirm password reset
|
||||
|
||||
### Task 4.2: Move customer profile routes to `customers` module
|
||||
|
||||
**From:** `app/api/v1/storefront/profile.py`
|
||||
**To:** `app/modules/customers/routes/api/storefront.py` (append)
|
||||
|
||||
Endpoints:
|
||||
- `GET /profile` - Get customer profile
|
||||
- `PUT /profile` - Update customer profile
|
||||
|
||||
### Task 4.3: Move customer address routes to `customers` module
|
||||
|
||||
**From:** `app/api/v1/storefront/addresses.py`
|
||||
**To:** `app/modules/customers/routes/api/storefront.py` (append)
|
||||
|
||||
Endpoints:
|
||||
- `GET /addresses` - List customer addresses
|
||||
- `POST /addresses` - Create address
|
||||
- `PUT /addresses/{id}` - Update address
|
||||
- `DELETE /addresses/{id}` - Delete address
|
||||
|
||||
### Task 4.4: Move cart routes to `cart` module
|
||||
|
||||
**From:** `app/api/v1/storefront/carts.py`
|
||||
**To:** `app/modules/cart/routes/api/storefront.py`
|
||||
|
||||
### Task 4.5: Move order placement to `checkout` module
|
||||
|
||||
**From:** `app/api/v1/storefront/orders.py` (POST /orders only)
|
||||
**To:** `app/modules/checkout/routes/api/storefront.py`
|
||||
|
||||
### Task 4.6: Move order viewing to `orders` module
|
||||
|
||||
**From:** `app/api/v1/storefront/orders.py` (GET endpoints)
|
||||
**To:** `app/modules/orders/routes/api/storefront.py`
|
||||
|
||||
### Task 4.7: Move product browsing to `catalog` module
|
||||
|
||||
**From:** `app/api/v1/storefront/products.py`
|
||||
**To:** `app/modules/catalog/routes/api/storefront.py`
|
||||
|
||||
### Task 4.8: Move messaging routes to `messaging` module
|
||||
|
||||
**From:** `app/api/v1/storefront/messages.py`
|
||||
**To:** `app/modules/messaging/routes/api/storefront.py`
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Fix Direct Model Imports
|
||||
|
||||
### Task 5.1: Update dependency injection pattern
|
||||
|
||||
**Current pattern (violates layered architecture):**
|
||||
```python
|
||||
from models.database.customer import Customer
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: Customer = Depends(get_current_customer_api)):
|
||||
...
|
||||
```
|
||||
|
||||
**New pattern (proper layered architecture):**
|
||||
```python
|
||||
from app.modules.customers.schemas import CustomerContext
|
||||
|
||||
@router.get("/orders")
|
||||
def get_orders(customer: CustomerContext = Depends(get_current_customer_api)):
|
||||
...
|
||||
```
|
||||
|
||||
### Task 5.2: Create `CustomerContext` schema
|
||||
|
||||
**File:** `app/modules/customers/schemas/context.py`
|
||||
```python
|
||||
class CustomerContext(BaseModel):
|
||||
"""Customer context for dependency injection in storefront routes."""
|
||||
id: int
|
||||
vendor_id: int
|
||||
email: str
|
||||
first_name: str | None
|
||||
last_name: str | None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
```
|
||||
|
||||
### Task 5.3: Update `get_current_customer_api` dependency
|
||||
|
||||
**File:** `app/api/deps.py`
|
||||
|
||||
Change return type from `Customer` (database model) to `CustomerContext` (schema).
|
||||
|
||||
### Task 5.4: Update all storefront routes
|
||||
|
||||
Replace all `Customer` type hints with `CustomerContext`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Delete Legacy Files
|
||||
|
||||
### Task 6.1: Delete `models/database/customer.py`
|
||||
|
||||
After all imports are updated to use `app.modules.customers.models.customer`.
|
||||
|
||||
### Task 6.2: Delete `app/api/v1/storefront/` directory
|
||||
|
||||
After all routes are moved to their respective modules.
|
||||
|
||||
### Task 6.3: Delete `app/services/cart_service.py`
|
||||
|
||||
After migrated to `app/modules/cart/services/cart_service.py`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Update Documentation
|
||||
|
||||
### Task 7.1: Update API documentation
|
||||
- Rename all "shop" references to "storefront"
|
||||
- Update endpoint paths
|
||||
|
||||
### Task 7.2: Update architecture documentation
|
||||
- Document the layered architecture rule
|
||||
- Document module structure
|
||||
|
||||
---
|
||||
|
||||
## Module Dependency Graph (Final State)
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ core │
|
||||
│ (tenancy) │
|
||||
└──────┬──────┘
|
||||
│
|
||||
┌────────────┼────────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│customers │ │ payments │ │messaging │
|
||||
│ (auth) │ │ │ │ │
|
||||
└────┬─────┘ └────┬─────┘ └──────────┘
|
||||
│ │
|
||||
│ ┌────────┴────────┐
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ cart │ │ orders │
|
||||
│ │ │ │
|
||||
└────┬─────┘ └────┬─────┘
|
||||
│ │
|
||||
└──────────┬──────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────┐
|
||||
│ checkout │
|
||||
│ │
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. **Phase 1** - Add architecture rule (enables detection)
|
||||
2. **Phase 2** - Rename shop → storefront (terminology)
|
||||
3. **Phase 3** - Create new modules (cart, checkout, catalog)
|
||||
4. **Phase 4** - Move routes to modules
|
||||
5. **Phase 5** - Fix direct model imports
|
||||
6. **Phase 6** - Delete legacy files
|
||||
7. **Phase 7** - Update documentation
|
||||
|
||||
---
|
||||
|
||||
## Files Modified Summary
|
||||
|
||||
### New Files
|
||||
- `.architecture-rules/api.yaml` (modified - add API-007)
|
||||
- `app/modules/cart/**` (new module)
|
||||
- `app/modules/checkout/**` (new module)
|
||||
- `app/modules/catalog/**` (new module)
|
||||
- `app/modules/customers/routes/api/storefront.py`
|
||||
- `app/modules/customers/schemas/context.py`
|
||||
- `app/modules/orders/routes/api/storefront.py`
|
||||
- `app/modules/messaging/routes/api/storefront.py`
|
||||
|
||||
### Deleted Files
|
||||
- `app/api/v1/shop/` (entire directory)
|
||||
- `app/routes/shop_pages.py`
|
||||
- `models/database/customer.py`
|
||||
- `app/services/cart_service.py`
|
||||
|
||||
### Modified Files
|
||||
- `app/api/deps.py` (CustomerContext return type)
|
||||
- `scripts/validate_architecture.py` (add API-007 check)
|
||||
- All files currently importing from legacy locations
|
||||
@@ -54,7 +54,7 @@ from middleware.auth import AuthManager
|
||||
from app.modules.cms.models import ContentPage
|
||||
from models.database.admin import PlatformAlert
|
||||
from models.database.company import Company
|
||||
from models.database.customer import Customer, CustomerAddress
|
||||
from app.modules.customers.models.customer import Customer, CustomerAddress
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.marketplace_product_translation import (
|
||||
|
||||
@@ -1771,6 +1771,9 @@ class ArchitectureValidator:
|
||||
# API-005: Check vendor_id scoping for vendor/shop endpoints
|
||||
self._check_vendor_scoping(file_path, content, lines)
|
||||
|
||||
# API-007: Check for direct model imports
|
||||
self._check_no_model_imports(file_path, content, lines)
|
||||
|
||||
def _check_pydantic_usage(self, file_path: Path, content: str, lines: list[str]):
|
||||
"""API-001: Ensure endpoints use Pydantic models"""
|
||||
rule = self._get_rule("API-001")
|
||||
@@ -2061,6 +2064,59 @@ class ArchitectureValidator:
|
||||
)
|
||||
return # Only report once per file
|
||||
|
||||
def _check_no_model_imports(
|
||||
self, file_path: Path, content: str, lines: list[str]
|
||||
):
|
||||
"""API-007: Check that API endpoints do NOT import database models directly.
|
||||
|
||||
Routes should follow layered architecture: Routes → Services → Models.
|
||||
Database models should only be imported in services, not in API routes.
|
||||
|
||||
Allowed: schemas, services, deps, database session
|
||||
Not allowed: models.database.*, app.modules.*.models.*
|
||||
"""
|
||||
rule = self._get_rule("API-007")
|
||||
if not rule:
|
||||
return
|
||||
|
||||
# Skip deps.py - it may need model access for queries
|
||||
file_path_str = str(file_path)
|
||||
if file_path_str.endswith("deps.py"):
|
||||
return
|
||||
|
||||
# Check for noqa
|
||||
if "noqa: api-007" in content.lower():
|
||||
return
|
||||
|
||||
# Patterns that indicate direct model imports
|
||||
model_import_patterns = [
|
||||
(r"from models\.database\.", "Importing from legacy models.database location"),
|
||||
(r"from app\.modules\.[a-z_]+\.models\.", "Importing from module models"),
|
||||
]
|
||||
|
||||
for i, line in enumerate(lines, 1):
|
||||
# Skip comments
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("#"):
|
||||
continue
|
||||
|
||||
# Check for noqa on this specific line
|
||||
if "noqa: api-007" in line.lower():
|
||||
continue
|
||||
|
||||
for pattern, message in model_import_patterns:
|
||||
if re.search(pattern, line):
|
||||
self._add_violation(
|
||||
rule_id="API-007",
|
||||
rule_name="API endpoints must NOT import database models directly",
|
||||
severity=Severity.ERROR,
|
||||
file_path=file_path,
|
||||
line_number=i,
|
||||
message=message,
|
||||
context=line.strip()[:80],
|
||||
suggestion="Import from services or schemas instead. Use CustomerContext schema for dependency injection.",
|
||||
)
|
||||
|
||||
def _validate_service_layer(self, target_path: Path):
|
||||
"""Validate service layer rules (SVC-001 to SVC-007)"""
|
||||
print("🔧 Validating service layer...")
|
||||
|
||||
2
tests/fixtures/customer_fixtures.py
vendored
2
tests/fixtures/customer_fixtures.py
vendored
@@ -8,7 +8,7 @@ See tests/conftest.py for details on fixture best practices.
|
||||
|
||||
import pytest
|
||||
|
||||
from models.database.customer import Customer, CustomerAddress
|
||||
from app.modules.customers.models.customer import Customer, CustomerAddress
|
||||
from models.database.order import Order
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
from jose import jwt
|
||||
|
||||
from models.database.customer import Customer, CustomerAddress
|
||||
from app.modules.customers.models.customer import Customer, CustomerAddress
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -12,7 +12,7 @@ from unittest.mock import patch, MagicMock
|
||||
import pytest
|
||||
from jose import jwt
|
||||
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from models.database.invoice import Invoice, InvoiceStatus, VendorInvoiceSettings
|
||||
from models.database.order import Order, OrderItem
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from models.database.password_reset_token import PasswordResetToken
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from models.database.invoice import Invoice, InvoiceStatus, VendorInvoiceSettings
|
||||
from models.database.order import Order
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from models.database.customer import Customer, CustomerAddress
|
||||
from app.modules.customers.models.customer import Customer, CustomerAddress
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
@@ -9,7 +9,7 @@ import pytest
|
||||
|
||||
from app.exceptions.customer import CustomerNotFoundException
|
||||
from app.services.admin_customer_service import AdminCustomerService
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -7,7 +7,7 @@ import pytest
|
||||
|
||||
from app.exceptions import AddressLimitExceededException, AddressNotFoundException
|
||||
from app.services.customer_address_service import CustomerAddressService
|
||||
from models.database.customer import CustomerAddress
|
||||
from app.modules.customers.models.customer import CustomerAddress
|
||||
from models.schema.customer import CustomerAddressCreate, CustomerAddressUpdate
|
||||
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ from app.services.order_service import (
|
||||
OrderService,
|
||||
order_service,
|
||||
)
|
||||
from models.database.customer import Customer
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from models.database.order import Order, OrderItem
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user