- Renamed customs-related schemas in line_items to LineCustomCreate, LineCustomUpdate, and LineCustomResponse. - Adjusted models to streamline invoice_id mapping in Item model. - Enhanced item routes to include company_id in summary statistics endpoint. - Refactored ItemService to improve item creation and update logic, removing redundant methods. - Updated frontend components for item management, including new item creation and editing functionalities. - Added API client for items with CRUD operations and improved error handling.
213 lines
8.0 KiB
Python
213 lines
8.0 KiB
Python
"""
|
|
Normalized Database Schema for SCAF (Fixed Assets) and SCAII (Parts Inventory)
|
|
SQLAlchemy v2 - Annex 24 Compliance
|
|
"""
|
|
|
|
from typing import Optional, TYPE_CHECKING, List
|
|
from sqlalchemy import Boolean, String, Integer, ForeignKey
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
|
|
from core.database import Base
|
|
|
|
if TYPE_CHECKING:
|
|
from .line_items.models import LineItem
|
|
|
|
# ============================================================================
|
|
# CORE ENTITIES
|
|
# ============================================================================
|
|
|
|
|
|
class Item(Base, TenantScopedMixin, TimestampMixin):
|
|
"""
|
|
Unified item header table for all import/export operations
|
|
Consolidates headers from both SCAF and SCAII systems
|
|
"""
|
|
__tablename__ = "items"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id")) # CONSECUTIVO
|
|
# Type: IMPORT_TEMP, IMPORT_DEF, EXPORT, REPAIR, etc.
|
|
item_type: Mapped[str] = mapped_column(String(20))
|
|
system_origin: Mapped[str] = mapped_column(String(10)) # SCAF or SCAII
|
|
|
|
# Item references
|
|
invoice_number: Mapped[Optional[str]] = mapped_column(
|
|
String(15)) # FACTURAIMPO/FACTURAEXPO
|
|
reference_number: Mapped[Optional[str]] = mapped_column(
|
|
String(20)) # NUMREFERENCIA
|
|
order: Mapped[Optional[str]] = mapped_column(
|
|
String(50)) # ORDENCOMPRA / ORDENVENTA
|
|
guide_number: Mapped[Optional[str]] = mapped_column(
|
|
String(50)) # NUMEROGUIA/NUMERODEGUIA
|
|
|
|
# Dates
|
|
invoice_date: Mapped[Optional[int]] = mapped_column(
|
|
Integer) # FECHAFACTURA
|
|
depreciation_date: Mapped[Optional[int]] = mapped_column(
|
|
Integer) # FECHADEPRECIACION
|
|
|
|
# Administrative fields
|
|
rectification: Mapped[Optional[bool]] = mapped_column(
|
|
Boolean) # RECTIFICACION
|
|
warehouse: Mapped[Optional[str]] = mapped_column(String(30)) # BODEGA
|
|
location: Mapped[Optional[str]] = mapped_column(
|
|
String(200)) # LOCALIZACION
|
|
|
|
# Relationships (one-to-many)
|
|
lines: Mapped[List["LineItem"]] = relationship(
|
|
"LineItem", back_populates="item", cascade="all, delete-orphan")
|
|
|
|
# ============================================================================
|
|
# SUPPORTING TABLES
|
|
# ============================================================================
|
|
|
|
|
|
class PackingList(Base, TenantScopedMixin, TimestampMixin):
|
|
"""
|
|
Packing list items
|
|
From: SPartidasPackingList
|
|
"""
|
|
__tablename__ = "packing_lists"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
item_line_id: Mapped[int] = mapped_column(Integer) # LINEA
|
|
packing_list_number: Mapped[Optional[str]] = mapped_column(
|
|
String(100)) # NUMPACKINGLIST
|
|
|
|
|
|
class CTMReceipt(Base, TenantScopedMixin, TimestampMixin):
|
|
"""
|
|
CTM Receipt lines (temporary manufacturing)
|
|
From: SPartidasReciboCTM
|
|
"""
|
|
__tablename__ = "ctm_receipts"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
receipt_line: Mapped[int] = mapped_column(
|
|
ForeignKey("a76.item_lines.id")) # LINEARECIBO
|
|
|
|
option: Mapped[Optional[str]] = mapped_column(String(3)) # OPCION
|
|
exit_invoice: Mapped[Optional[str]] = mapped_column(
|
|
String(19)) # FACTURASALIDA
|
|
|
|
|
|
class SubassemblyEntry(Base, TenantScopedMixin, TimestampMixin):
|
|
"""
|
|
Subassembly/Submanufacturing Entry lines
|
|
From: SPartidasEntradaSM
|
|
"""
|
|
__tablename__ = "subassembly_entries"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
remission_line: Mapped[int] = mapped_column(Integer) # LINEAREMISION
|
|
exit_invoice: Mapped[Optional[str]] = mapped_column(
|
|
String(15)) # FACTURASALIDA
|
|
exit_line: Mapped[Optional[int]] = mapped_column(Integer) # LINEASALIDA
|
|
|
|
# ============================================================================
|
|
# INDEXES AND CONSTRAINTS
|
|
# ============================================================================
|
|
|
|
|
|
"""
|
|
Recommended indexes for optimal query performance:
|
|
|
|
CREATE INDEX idx_items_consecutive ON items(consecutive);
|
|
CREATE INDEX idx_items_invoice ON items(invoice_number);
|
|
CREATE INDEX idx_items_type_system ON items(item_type, system_origin);
|
|
CREATE INDEX idx_items_dates ON items(invoice_date, depreciation_date);
|
|
|
|
CREATE INDEX idx_lines_item ON item_lines(item_id);
|
|
CREATE INDEX idx_lines_part ON item_lines(part_number);
|
|
CREATE INDEX idx_lines_class ON item_lines(class_code);
|
|
CREATE INDEX idx_lines_invoice_refs ON item_lines(import_invoice, export_invoice);
|
|
CREATE INDEX idx_lines_fractions ON item_lines(import_fraction, export_fraction);
|
|
|
|
CREATE INDEX idx_packing_consecutive ON packing_lists(consecutive);
|
|
CREATE INDEX idx_packing_part ON packing_lists(part_number);
|
|
|
|
CREATE INDEX idx_repair_invoice ON repair_parts(import_invoice, import_line);
|
|
CREATE INDEX idx_ctm_ship_consec ON ctm_shipments(consecutive);
|
|
CREATE INDEX idx_ctm_rcpt_consec ON ctm_receipts(consecutive);
|
|
CREATE INDEX idx_sub_entry_consec ON subassembly_entries(consecutive);
|
|
CREATE INDEX idx_sub_exit_consec ON subassembly_exits(consecutive);
|
|
CREATE INDEX idx_imposition_consec ON imposition_parts(consecutive);
|
|
"""
|
|
|
|
|
|
# ============================================================================
|
|
# MIGRATION NOTES
|
|
# ============================================================================
|
|
|
|
"""
|
|
MIGRATION STRATEGY FROM ORIGINAL TABLES TO NORMALIZED SCHEMA:
|
|
|
|
1. DOCUMENT MAPPING:
|
|
- QEqeMaq (Equipment Import Temp) → items (type='EQUIPMENT_IMPORT_TEMP', system='SCAF')
|
|
- QEqeMaqRep (Equipment Repair Export) → items (type='EQUIPMENT_REPAIR_EXPORT', system='SCAF')
|
|
- QEqiDef (Equipment Import Definitive) → items (type='EQUIPMENT_IMPORT_DEF', system='SCAF')
|
|
- QEqiMaq (Equipment Machinery) → items (type='EQUIPMENT_MACHINERY', system='SCAF')
|
|
- QEqiMaqRep (Equipment Machinery Repair) → items (type='EQUIPMENT_REPAIR_IMPORT', system='SCAF')
|
|
- SPartidasCM (Common Commerce) → items (type='COMMON_COMMERCE', system='SCAII')
|
|
- SPartidasExpo (Export) → items (type='EXPORT', system='SCAII')
|
|
- SPartidasImpo (Import) → items (type='IMPORT', system='SCAII')
|
|
|
|
2. LINE MAPPING:
|
|
All line items from Q* and SPartidas* tables map to item_lines with appropriate
|
|
field mapping based on the original column names (preserved as comments).
|
|
|
|
3. FIELD CONSOLIDATION RULES:
|
|
- Costs: Unified under unit_cost_* with currency suffix (usd/mxn/mc)
|
|
- Values: Unified under value_* with currency suffix
|
|
- Quantities: Unified under quantity_* with specific purpose suffixes
|
|
- Descriptions: Consolidated into description_spanish/english/extra
|
|
- Fractions: All fraction fields preserved with clear naming
|
|
|
|
4. DATA INTEGRITY:
|
|
- Original CONSECUTIVO + LINE number preserved for traceability
|
|
- Foreign key relationships established via item_id
|
|
- All original fields retained to prevent data loss
|
|
|
|
5. SPECIAL TABLES:
|
|
- PackingList, RepairPart, CTM*, Subassembly*, ImpositionPart remain separate
|
|
as they serve specific purposes and don't fit the main item/line pattern
|
|
|
|
6. BENEFITS:
|
|
- Eliminates redundancy across 13 original tables
|
|
- Unified query interface for all operations
|
|
- Maintains full audit trail with original field names
|
|
- Enables cross-system reporting (SCAF + SCAII)
|
|
- Simplifies maintenance with single schema
|
|
|
|
7. QUERYING EXAMPLES:
|
|
```python
|
|
# Get all imports (both systems)
|
|
session.query(Item).filter(
|
|
Item.item_type.in_(['IMPORT', 'EQUIPMENT_IMPORT_TEMP', 'EQUIPMENT_IMPORT_DEF'])
|
|
)
|
|
|
|
# Get all lines for a specific part across all items
|
|
session.query(LineItem).filter(
|
|
LineItem.part_number == 'ABC123'
|
|
)
|
|
|
|
# Get SCAF equipment with depreciation
|
|
session.query(Item).join(LineItem).filter(
|
|
Item.system_origin == 'SCAF',
|
|
LineItem.value_depreciated_usd.isnot(None)
|
|
)
|
|
```
|
|
"""
|