from datetime import date, datetime from decimal import Decimal from typing import Any, Dict, List, Literal, Optional from pydantic import BaseModel, Field ALSO_SOURCE_TYPES = Literal["api", "xml", "json_export", "xml_export", "csv", "xlsx_export"] ALSO_QUEUE_STATUSES = Literal[ "new", "matching_products", "matching_customers", "ready_for_approval", "approved", "invoiced", "error", ] class AlsoImportJobCreate(BaseModel): source_type: ALSO_SOURCE_TYPES source_label: Optional[str] = None file_name: Optional[str] = None import_version: Optional[str] = None raw_payload: Dict[str, Any] = Field(default_factory=dict) class AlsoImportLineInput(BaseModel): line_no: Optional[int] = None source_line_ref: Optional[str] = None company: Optional[str] = None customer_id: Optional[str] = None account_id: Optional[str] = None vat: Optional[str] = None also_company_id: Optional[str] = None material_number: Optional[str] = None product_name: Optional[str] = None vendor: Optional[str] = None cost_amount: Optional[Decimal] = None sales_price: Optional[Decimal] = None unit_price: Optional[Decimal] = None total_price: Optional[Decimal] = None currency: Optional[str] = None billing_start: Optional[date] = None charge_interval: Optional[str] = None billing_interval: Optional[str] = None billable_parameters: Optional[Decimal] = None queue_status: ALSO_QUEUE_STATUSES = "new" raw_line: Dict[str, Any] = Field(default_factory=dict) class AlsoImportLinesRequest(BaseModel): lines: List[AlsoImportLineInput] = Field(default_factory=list) class AlsoQueueProcessRequest(BaseModel): import_job_id: Optional[int] = Field(default=None, gt=0) line_ids: List[int] = Field(default_factory=list) limit: int = Field(default=500, ge=1, le=5000) class AlsoQueueApproveRequest(BaseModel): import_job_id: Optional[int] = Field(default=None, gt=0) line_ids: List[int] = Field(default_factory=list) class AlsoQueueProcessResult(BaseModel): processed: int updated: int ready_for_approval: int errored: int class AlsoApproveResult(BaseModel): approved_lines: int created_drafts: int draft_ids: List[int] = Field(default_factory=list) class AlsoCompanyMappingUpsert(BaseModel): also_company_id: str = Field(min_length=1) also_customer_id: Optional[str] = None customer_id: int = Field(gt=0) match_confidence: Optional[Decimal] = None notes: Optional[str] = None class AlsoProductMappingUpsert(BaseModel): material_number: str = Field(min_length=1) vendor: str = Field(min_length=1) hub_product_id: int = Field(gt=0) product_name_snapshot: Optional[str] = None class AlsoManualCompanyMapRequest(BaseModel): customer_id: int = Field(gt=0) notes: Optional[str] = None class AlsoManualProductMapRequest(BaseModel): product_id: int = Field(gt=0) notes: Optional[str] = None class AlsoImportJobResponse(BaseModel): id: int source_type: str source_label: Optional[str] = None status: str file_name: Optional[str] = None import_version: Optional[str] = None imported_by_user_id: Optional[int] = None imported_at: datetime line_count: int = 0 class AlsoQueueLineResponse(BaseModel): id: int import_job_id: int queue_status: str company: Optional[str] = None customer_id: Optional[str] = None material_number: Optional[str] = None product_name: Optional[str] = None vendor: Optional[str] = None total_price: Optional[Decimal] = None cost_amount: Optional[Decimal] = None effective_cost_amount: Optional[Decimal] = None effective_margin_amount: Optional[Decimal] = None currency: Optional[str] = None matched_customer_id: Optional[int] = None matched_product_id: Optional[int] = None order_draft_id: Optional[int] = None validation_errors_json: List[Dict[str, Any]] = Field(default_factory=list) class AlsoDashboardSummaryResponse(BaseModel): monthly_revenue: Decimal monthly_cost: Decimal monthly_margin: Decimal unmatched_products: int unmatched_customers: int pending_approvals: int invoiced_customers: int class AlsoDifferenceItem(BaseModel): customer_id: Optional[int] = None customer_name: Optional[str] = None material_number: Optional[str] = None product_name: Optional[str] = None vendor: Optional[str] = None previous_month_qty: Decimal current_month_qty: Decimal change_percent: Optional[Decimal] = None warning: bool = False