Skip to content
3 changes: 3 additions & 0 deletions flexus_client_kit/ckit_erp.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ def check_record_matches_filter(record: dict, f: str, col_names: set = None) ->
except (ValueError, TypeError):
return False

if val is None:
return op == "!=" and filter_val != ""

if op == "=":
return val == filter_val
if op == "!=":
Expand Down
108 changes: 88 additions & 20 deletions flexus_client_kit/erp_schema.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
import dataclasses
from dataclasses import dataclass, field
from decimal import Decimal
from typing import Optional, Dict, Type, List


@dataclass
class CrmActivity:
ws_id: str
activity_title: str = field(metadata={"importance": 1, "display_name": "Title"})
activity_type: str = field(metadata={"importance": 1, "display_name": "Type", "enum": [
{"value": "WEB_CHAT", "label": "Web Chat"},
{"value": "MESSENGER_CHAT", "label": "Messenger Chat"},
{"value": "EMAIL", "label": "Email"},
{"value": "CALL", "label": "Call"},
{"value": "MEETING", "label": "Meeting"},
]})
activity_direction: str = field(metadata={"importance": 1, "display_name": "Direction", "enum": [
{"value": "INBOUND", "label": "Inbound"},
{"value": "OUTBOUND", "label": "Outbound"},
]})
activity_contact_id: str = field(metadata={"importance": 1, "display_name": "Contact"})
activity_id: str = field(default="", metadata={"pkey": True, "display_name": "Activity ID"})
activity_platform: str = field(default="", metadata={"importance": 1, "display_name": "Channel"})
activity_ft_id: Optional[str] = field(default=None, metadata={"importance": 1, "display_name": "Thread"})
activity_summary: str = field(default="", metadata={"importance": 1, "display": "string_multiline", "display_name": "Summary"})
activity_details: dict = field(default_factory=dict, metadata={"display_name": "Details"})
activity_occurred_ts: float = field(default=0.0, metadata={"importance": 1, "display_name": "Occurred at"})
activity_created_ts: float = field(default=0.0, metadata={"display_name": "Created at"})
activity_modified_ts: float = field(default=0.0, metadata={"display_name": "Modified at"})


@dataclass
class CrmContact:
ws_id: str
Expand Down Expand Up @@ -30,35 +57,65 @@ class CrmContact:
contact_utm_last_campaign: str = field(default="", metadata={"display_name": "UTM Campaign (last touch)"})
contact_utm_last_term: str = field(default="", metadata={"display_name": "UTM Term (last touch)"})
contact_utm_last_content: str = field(default="", metadata={"display_name": "UTM Content (last touch)"})
contact_bant_score: int = field(default=-1, metadata={"display_name": "BANT Qualification Score", "description": "Budget, Authority, Need, Timeline. -1 means not qualified, 0-4 scale"})
contact_bant_score: int = field(default=-1, metadata={"display_name": "BANT Score", "description": "How many of Budget/Authority/Need/Timeline criteria met. -1=unqualified, 0-1=cold, 2-3=warm, 4=hot"})
contact_created_ts: float = field(default=0.0, metadata={"importance": 1, "display_name": "Created at"})
contact_modified_ts: float = field(default=0.0, metadata={"display_name": "Modified at"})
contact_archived_ts: float = field(default=0.0, metadata={"display_name": "Archived at"})


@dataclass
class CrmActivity:
class CrmDeal:
ws_id: str
activity_title: str = field(metadata={"importance": 1, "display_name": "Title"})
activity_type: str = field(metadata={"importance": 1, "display_name": "Type", "enum": ["WEB_CHAT", "MESSENGER_CHAT", "EMAIL", "CALL", "MEETING"]})
activity_direction: str = field(metadata={"importance": 1, "display_name": "Direction", "enum": ["INBOUND", "OUTBOUND"]})
activity_contact_id: str = field(metadata={"importance": 1, "display_name": "Contact"})
activity_id: str = field(default="", metadata={"pkey": True, "display_name": "Activity ID"})
activity_platform: str = field(default="", metadata={"importance": 1, "display_name": "Channel"})
activity_ft_id: Optional[str] = field(default=None, metadata={"importance": 1, "display_name": "Thread"})
activity_summary: str = field(default="", metadata={"importance": 1, "display": "string_multiline", "display_name": "Summary"})
activity_details: dict = field(default_factory=dict, metadata={"display_name": "Details"})
activity_occurred_ts: float = field(default=0.0, metadata={"importance": 1, "display_name": "Occurred at"})
activity_created_ts: float = field(default=0.0, metadata={"display_name": "Created at"})
activity_modified_ts: float = field(default=0.0, metadata={"display_name": "Modified at"})
deal_name: str = field(metadata={"importance": 1, "display_name": "Name"})
deal_pipeline_id: str = field(metadata={"importance": 1, "display_name": "Pipeline"})
deal_stage_id: str = field(metadata={"importance": 1, "display_name": "Stage", "fk_scope": {"stage_pipeline_id": "deal_pipeline_id"}})
deal_id: str = field(default="", metadata={"pkey": True, "display_name": "Deal ID"})
deal_contact_id: Optional[str] = field(default=None, metadata={"importance": 1, "display_name": "Contact"})
deal_value: Decimal = field(default=Decimal(0), metadata={"importance": 1, "display_name": "Value"})
deal_expected_close_ts: float = field(default=0.0, metadata={"importance": 1, "display_name": "Expected Close"})
deal_closed_ts: float = field(default=0.0, metadata={"display_name": "Closed at"})
deal_lost_reason: str = field(default="", metadata={"display_name": "Lost Reason"})
deal_notes: str = field(default="", metadata={"importance": 1, "display": "string_multiline", "display_name": "Notes"})
deal_tags: List[str] = field(default_factory=list, metadata={"importance": 1, "display_name": "Tags"})
deal_details: dict = field(default_factory=dict, metadata={"display_name": "Details", "description": "Custom fields JSON"})
deal_owner_fuser_id: str = field(default="", metadata={"importance": 1, "display_name": "Owner"})
deal_priority: str = field(default="NONE", metadata={"importance": 1, "display_name": "Priority", "enum": [{"value": "NONE", "label": "None"}, {"value": "LOW", "label": "Low"}, {"value": "MEDIUM", "label": "Medium"}, {"value": "HIGH", "label": "High"}]})
deal_created_ts: float = field(default=0.0, metadata={"importance": 1, "display_name": "Created at"})
deal_modified_ts: float = field(default=0.0, metadata={"display_name": "Modified at"})
deal_archived_ts: float = field(default=0.0, metadata={"display_name": "Archived at"})


@dataclass
class CrmPipeline:
ws_id: str
pipeline_name: str = field(metadata={"importance": 1, "display_name": "Name"})
pipeline_id: str = field(default="", metadata={"pkey": True, "display_name": "Pipeline ID"})
pipeline_active: bool = field(default=True, metadata={"importance": 1, "display_name": "Active"})
pipeline_created_ts: float = field(default=0.0, metadata={"display_name": "Created at"})
pipeline_modified_ts: float = field(default=0.0, metadata={"display_name": "Modified at"})
pipeline_archived_ts: float = field(default=0.0, metadata={"display_name": "Archived at"})


@dataclass
class CrmPipelineStage:
ws_id: str
stage_name: str = field(metadata={"importance": 1, "display_name": "Name"})
stage_pipeline_id: str = field(metadata={"importance": 1, "display_name": "Pipeline"})
stage_id: str = field(default="", metadata={"pkey": True, "display_name": "Stage ID"})
stage_sequence: int = field(default=0, metadata={"display_name": "Sequence"})
stage_probability: int = field(default=0, metadata={"importance": 1, "display_name": "Win Probability %", "description": "0-100 win probability percentage"})
stage_status: str = field(default="OPEN", metadata={"importance": 1, "display_name": "Status", "enum": [{"value": "OPEN", "label": "Open"}, {"value": "WON", "label": "Won"}, {"value": "LOST", "label": "Lost"}]})
stage_created_ts: float = field(default=0.0, metadata={"display_name": "Created at"})
stage_modified_ts: float = field(default=0.0, metadata={"display_name": "Modified at"})
stage_archived_ts: float = field(default=0.0, metadata={"display_name": "Archived at"})


@dataclass
class ProductTemplate:
prodt_name: str = field(metadata={"importance": 1, "display_name": "Name"})
prodt_pcat_id: str = field(metadata={"display_name": "Category"})
prodt_list_price: int = field(metadata={"importance": 1, "display_name": "List Price"})
prodt_standard_price: int = field(metadata={"importance": 1, "display_name": "Standard Price"})
prodt_list_price: Decimal = field(metadata={"importance": 1, "display_name": "List Price"})
prodt_standard_price: Decimal = field(metadata={"importance": 1, "display_name": "Standard Price"})
prodt_uom_id: str = field(metadata={"display_name": "Unit of Measure"})
ws_id: str = field(metadata={"display_name": "Workspace ID"})
prodt_id: str = field(default="", metadata={"pkey": True, "display_name": "Product Template ID"})
Expand Down Expand Up @@ -123,8 +180,11 @@ class ProductM2mTemplateTag:


ERP_TABLE_TO_SCHEMA: Dict[str, Type] = {
"crm_contact": CrmContact,
"crm_activity": CrmActivity,
"crm_contact": CrmContact,
"crm_deal": CrmDeal,
"crm_pipeline": CrmPipeline,
"crm_pipeline_stage": CrmPipelineStage,
"product_template": ProductTemplate,
"product_product": ProductProduct,
"product_category": ProductCategory,
Expand All @@ -134,8 +194,11 @@ class ProductM2mTemplateTag:
}

ERP_DISPLAY_NAME_CONFIGS: Dict[str, str] = {
"crm_contact": "{contact_first_name} {contact_last_name}",
"crm_activity": "{activity_title}",
"crm_contact": "{contact_first_name} {contact_last_name}",
"crm_deal": "{deal_name}",
"crm_pipeline": "{pipeline_name}",
"crm_pipeline_stage": "{stage_name}",
"product_template": "{prodt_name}",
"product_product": "{prod_default_code} {prod_barcode}",
"product_category": "{pcat_name}",
Expand Down Expand Up @@ -173,9 +236,9 @@ def get_field_display(cls: Type, field_name: str) -> Optional[str]:
return f.metadata.get("display") if f else None


def get_field_enum(cls: Type, field_name: str) -> Optional[List[str]]:
def get_field_enum(cls: Type, field_name: str) -> Optional[List[Dict[str, str]]]:
f = cls.__dataclass_fields__.get(field_name)
return f.metadata.get("enum") if f else None
return (f.metadata.get("enum") if f else None) or None


def get_field_display_name(cls: Type, field_name: str) -> Optional[str]:
Expand All @@ -186,3 +249,8 @@ def get_field_display_name(cls: Type, field_name: str) -> Optional[str]:
def get_field_description(cls: Type, field_name: str) -> Optional[str]:
f = cls.__dataclass_fields__.get(field_name)
return f.metadata.get("description") if f else None


def get_field_fk_scope(cls: Type, field_name: str) -> Optional[Dict[str, str]]:
f = cls.__dataclass_fields__.get(field_name)
return (f.metadata.get("fk_scope") if f else None) or None
4 changes: 2 additions & 2 deletions flexus_client_kit/gql_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class MyDataclassForResponse:
elif hasattr(field_type, "__origin__") and field_type.__origin__ is list:
if field_type.__args__ and isinstance(field_value, list):
inner_type = field_type.__args__[0]
if hasattr(inner_type, "__annotations__"):
if dataclasses.is_dataclass(inner_type):
# List of dataclasses
filtered_data[field_name] = [dataclass_from_dict(item, inner_type) for item in field_value]
else:
Expand All @@ -84,7 +84,7 @@ class MyDataclassForResponse:
elif field_type is Any:
# Fallback for Any type
filtered_data[field_name] = field_value
elif hasattr(field_type, "__annotations__"):
elif dataclasses.is_dataclass(field_type):
filtered_data[field_name] = dataclass_from_dict(field_value, field_type)
return cls(**filtered_data)

Expand Down
Loading