package models import ( "time" "github.com/google/uuid" "gorm.io/gorm" ) // ============================================================================ // BASE MODEL // ============================================================================ type BaseModel struct { ID string `json:"id" gorm:"type:text;primaryKey"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"index"` } func (b *BaseModel) BeforeCreate(tx *gorm.DB) error { if b.ID == "" { b.ID = uuid.New().String() } return nil } // ============================================================================ // ROLES & PERMISSIONS // ============================================================================ type RoleType string const ( RoleAdmin RoleType = "admin" RoleManager RoleType = "manager" RoleCashier RoleType = "cashier" RoleCaptain RoleType = "captain" RoleKitchen RoleType = "kitchen" RoleAccountant RoleType = "accountant" ) type Role struct { BaseModel Name RoleType `json:"name" gorm:"type:text;uniqueIndex;not null"` DisplayName string `json:"display_name" gorm:"type:text;not null"` Permissions []Permission `json:"permissions" gorm:"many2many:role_permissions;"` Users []User `json:"users,omitempty" gorm:"foreignKey:RoleID"` } type Permission struct { BaseModel Module string `json:"module" gorm:"type:text;not null"` Action string `json:"action" gorm:"type:text;not null"` Roles []Role `json:"roles,omitempty" gorm:"many2many:role_permissions;"` } // ============================================================================ // USERS // ============================================================================ type User struct { BaseModel Username string `json:"username" gorm:"type:text;uniqueIndex;not null"` Password string `json:"-" gorm:"type:text;not null"` FullName string `json:"full_name" gorm:"type:text;not null"` Email string `json:"email" gorm:"type:text"` Phone string `json:"phone" gorm:"type:text"` RoleID string `json:"role_id" gorm:"type:text;not null"` Role Role `json:"role" gorm:"foreignKey:RoleID"` IsActive bool `json:"is_active" gorm:"default:true"` Pin string `json:"-" gorm:"type:text"` // Quick PIN for POS login LastLoginAt *time.Time `json:"last_login_at"` } // ============================================================================ // RESTAURANT CONFIG // ============================================================================ type RestaurantConfig struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` Address string `json:"address" gorm:"type:text"` Phone string `json:"phone" gorm:"type:text"` Email string `json:"email" gorm:"type:text"` GSTIN string `json:"gstin" gorm:"type:text"` FSSAI string `json:"fssai" gorm:"type:text"` LogoURL string `json:"logo_url" gorm:"type:text"` Currency string `json:"currency" gorm:"type:text;default:'INR'"` TaxRate float64 `json:"tax_rate" gorm:"type:real;default:5.0"` CGSTRate float64 `json:"cgst_rate" gorm:"type:real;default:2.5"` SGSTRate float64 `json:"sgst_rate" gorm:"type:real;default:2.5"` ServiceCharge float64 `json:"service_charge" gorm:"type:real;default:0"` PrinterIP string `json:"printer_ip" gorm:"type:text"` PrinterPort int `json:"printer_port" gorm:"type:integer;default:9100"` KOTPrinterIP string `json:"kot_printer_ip" gorm:"type:text"` WhatsAppToken string `json:"-" gorm:"type:text"` WhatsAppPhoneID string `json:"-" gorm:"type:text"` WhatsAppBaseURL string `json:"whatsapp_base_url" gorm:"type:text"` // Override for testing (default: https://graph.facebook.com/v18.0) SwiggyAPIKey string `json:"-" gorm:"type:text"` ZomatoAPIKey string `json:"-" gorm:"type:text"` // Pine Labs EDC Integration EDCEnabled bool `json:"edc_enabled" gorm:"type:boolean;default:false"` EDCProvider string `json:"edc_provider" gorm:"type:text;default:'pinelabs'"` // pinelabs, razorpay, etc. EDCMerchantID string `json:"edc_merchant_id" gorm:"type:text"` EDCSecurityToken string `json:"-" gorm:"type:text"` EDCStoreID string `json:"edc_store_id" gorm:"type:text"` EDCTerminalID string `json:"edc_terminal_id" gorm:"type:text"` EDCBaseURL string `json:"edc_base_url" gorm:"type:text"` // Override for testing (default: Pine Labs production URL) // KOT Print Format KOTPaperWidth int `json:"kot_paper_width" gorm:"type:integer;default:80"` KOTFontSize string `json:"kot_font_size" gorm:"type:text;default:'medium'"` KOTShowHeader bool `json:"kot_show_header" gorm:"type:boolean;default:true"` KOTHeaderText string `json:"kot_header_text" gorm:"type:text"` KOTShowFooter bool `json:"kot_show_footer" gorm:"type:boolean;default:false"` KOTFooterText string `json:"kot_footer_text" gorm:"type:text"` KOTShowCaptain bool `json:"kot_show_captain" gorm:"type:boolean;default:true"` KOTShowNotes bool `json:"kot_show_notes" gorm:"type:boolean;default:true"` KOTShowTime bool `json:"kot_show_time" gorm:"type:boolean;default:true"` // Bill Print Format BillPaperWidth int `json:"bill_paper_width" gorm:"type:integer;default:80"` BillFontSize string `json:"bill_font_size" gorm:"type:text;default:'medium'"` BillShowAddress bool `json:"bill_show_address" gorm:"type:boolean;default:true"` BillShowGSTIN bool `json:"bill_show_gstin" gorm:"type:boolean;default:true"` BillShowFSSAI bool `json:"bill_show_fssai" gorm:"type:boolean;default:true"` BillShowItemTax bool `json:"bill_show_item_tax" gorm:"type:boolean;default:false"` BillShowCustomer bool `json:"bill_show_customer" gorm:"type:boolean;default:true"` BillHeaderText string `json:"bill_header_text" gorm:"type:text"` BillFooterText string `json:"bill_footer_text" gorm:"type:text;default:'Thank you! Visit again!'"` BillShowQR bool `json:"bill_show_qr" gorm:"type:boolean;default:false"` BillQRData string `json:"bill_qr_data" gorm:"type:text"` } // ============================================================================ // CUSTOMERS // ============================================================================ type Customer struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` Phone string `json:"phone" gorm:"type:text;uniqueIndex"` Email string `json:"email" gorm:"type:text"` Address string `json:"address" gorm:"type:text"` LoyaltyPoints int `json:"loyalty_points" gorm:"type:integer;default:0"` TotalSpent float64 `json:"total_spent" gorm:"type:real;default:0"` TotalOrders int `json:"total_orders" gorm:"type:integer;default:0"` LastVisit *time.Time `json:"last_visit"` Notes string `json:"notes" gorm:"type:text"` Tags string `json:"tags" gorm:"type:text"` // comma-separated } // ============================================================================ // TABLES & SECTIONS // ============================================================================ type TableStatus string const ( TableAvailable TableStatus = "available" TableOccupied TableStatus = "occupied" TableReserved TableStatus = "reserved" TableCleaning TableStatus = "cleaning" ) type TableSection struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` Floor int `json:"floor" gorm:"type:integer;default:0"` Tables []RestaurantTable `json:"tables" gorm:"foreignKey:SectionID"` } type RestaurantTable struct { BaseModel Number int `json:"number" gorm:"type:integer;not null"` Name string `json:"name" gorm:"type:text"` SectionID string `json:"section_id" gorm:"type:text;not null"` Section TableSection `json:"section" gorm:"foreignKey:SectionID"` Capacity int `json:"capacity" gorm:"type:integer;default:4"` Status TableStatus `json:"status" gorm:"type:text;default:'available'"` QRCode string `json:"qr_code" gorm:"type:text"` PositionX int `json:"position_x" gorm:"type:integer;default:0"` PositionY int `json:"position_y" gorm:"type:integer;default:0"` } type TableSession struct { BaseModel TableID string `json:"table_id" gorm:"type:text;not null"` Table RestaurantTable `json:"table" gorm:"foreignKey:TableID"` GuestCount int `json:"guest_count" gorm:"type:integer;default:1"` CaptainID string `json:"captain_id" gorm:"type:text"` Captain User `json:"captain" gorm:"foreignKey:CaptainID"` CustomerID *string `json:"customer_id" gorm:"type:text"` Customer *Customer `json:"customer,omitempty" gorm:"foreignKey:CustomerID"` StartedAt time.Time `json:"started_at" gorm:"not null"` EndedAt *time.Time `json:"ended_at"` IsActive bool `json:"is_active" gorm:"default:true"` Notes string `json:"notes" gorm:"type:text"` } // ============================================================================ // CATEGORIES & PRODUCTS // ============================================================================ type Category struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` Description string `json:"description" gorm:"type:text"` ParentID *string `json:"parent_id" gorm:"type:text"` Parent *Category `json:"parent,omitempty" gorm:"foreignKey:ParentID"` ImageURL string `json:"image_url" gorm:"type:text"` SortOrder int `json:"sort_order" gorm:"type:integer;default:0"` IsActive bool `json:"is_active" gorm:"default:true"` Products []Product `json:"products,omitempty" gorm:"foreignKey:CategoryID"` } type ProductType string const ( ProductVeg ProductType = "veg" ProductNonVeg ProductType = "non_veg" ProductEgg ProductType = "egg" ) type KitchenStation string const ( StationSouthIndian KitchenStation = "south_indian" StationChinese KitchenStation = "chinese" StationTandoor KitchenStation = "tandoor" StationJuice KitchenStation = "juice" StationBakery KitchenStation = "bakery" StationMainKitchen KitchenStation = "main_kitchen" ) type Product struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` ShortName string `json:"short_name" gorm:"type:text"` SKU string `json:"sku" gorm:"type:text;uniqueIndex"` CategoryID string `json:"category_id" gorm:"type:text;not null"` Category Category `json:"category" gorm:"foreignKey:CategoryID"` Price float64 `json:"price" gorm:"type:real;not null"` TaxRate float64 `json:"tax_rate" gorm:"type:real;default:5.0"` ProductType ProductType `json:"product_type" gorm:"type:text;default:'veg'"` KitchenStation KitchenStation `json:"kitchen_station" gorm:"type:text;default:'main_kitchen'"` Description string `json:"description" gorm:"type:text"` ImageURL string `json:"image_url" gorm:"type:text"` IsActive bool `json:"is_active" gorm:"default:true"` IsAvailable bool `json:"is_available" gorm:"default:true"` PrepTime int `json:"prep_time" gorm:"type:integer;default:15"` // minutes SortOrder int `json:"sort_order" gorm:"type:integer;default:0"` ModifierGroups []ModifierGroup `json:"modifier_groups,omitempty" gorm:"many2many:product_modifier_groups;"` Recipes []Recipe `json:"recipes,omitempty" gorm:"foreignKey:ProductID"` } // ============================================================================ // MODIFIERS // ============================================================================ type ModifierGroup struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` MinSelect int `json:"min_select" gorm:"type:integer;default:0"` MaxSelect int `json:"max_select" gorm:"type:integer;default:1"` IsRequired bool `json:"is_required" gorm:"default:false"` Modifiers []Modifier `json:"modifiers" gorm:"foreignKey:GroupID"` Products []Product `json:"products,omitempty" gorm:"many2many:product_modifier_groups;"` } type Modifier struct { BaseModel GroupID string `json:"group_id" gorm:"type:text;not null"` Name string `json:"name" gorm:"type:text;not null"` Price float64 `json:"price" gorm:"type:real;default:0"` } // ============================================================================ // ORDERS // ============================================================================ type OrderSource string const ( SourceDineIn OrderSource = "dine_in" SourceTakeAway OrderSource = "take_away" SourceDelivery OrderSource = "delivery" SourceWebsite OrderSource = "website" SourceWhatsApp OrderSource = "whatsapp" SourceSwiggy OrderSource = "swiggy" SourceZomato OrderSource = "zomato" SourceQR OrderSource = "qr_table" ) type OrderStatus string const ( OrderPending OrderStatus = "pending" OrderConfirmed OrderStatus = "confirmed" OrderPreparing OrderStatus = "preparing" OrderReady OrderStatus = "ready" OrderServed OrderStatus = "served" OrderCompleted OrderStatus = "completed" OrderCancelled OrderStatus = "cancelled" ) type PaymentStatus string const ( PaymentPending PaymentStatus = "pending" PaymentPartial PaymentStatus = "partial" PaymentPaid PaymentStatus = "paid" PaymentRefunded PaymentStatus = "refunded" ) type Order struct { BaseModel OrderNumber string `json:"order_number" gorm:"type:text;uniqueIndex;not null"` Source OrderSource `json:"source" gorm:"type:text;not null"` Status OrderStatus `json:"status" gorm:"type:text;default:'pending'"` PaymentStatus PaymentStatus `json:"payment_status" gorm:"type:text;default:'pending'"` // Table info (for dine-in) TableID *string `json:"table_id" gorm:"type:text"` Table *RestaurantTable `json:"table,omitempty" gorm:"foreignKey:TableID"` TableSessionID *string `json:"table_session_id" gorm:"type:text"` GuestCount int `json:"guest_count" gorm:"type:integer;default:1"` // Customer info CustomerID *string `json:"customer_id" gorm:"type:text"` Customer *Customer `json:"customer,omitempty" gorm:"foreignKey:CustomerID"` CustomerName string `json:"customer_name" gorm:"type:text"` CustomerPhone string `json:"customer_phone" gorm:"type:text"` CustomerAddress string `json:"customer_address" gorm:"type:text"` // Staff CaptainID *string `json:"captain_id" gorm:"type:text"` Captain *User `json:"captain,omitempty" gorm:"foreignKey:CaptainID"` CashierID *string `json:"cashier_id" gorm:"type:text"` Cashier *User `json:"cashier,omitempty" gorm:"foreignKey:CashierID"` // Amounts SubTotal float64 `json:"sub_total" gorm:"type:real;default:0"` TaxAmount float64 `json:"tax_amount" gorm:"type:real;default:0"` CGSTAmount float64 `json:"cgst_amount" gorm:"type:real;default:0"` SGSTAmount float64 `json:"sgst_amount" gorm:"type:real;default:0"` DiscountType string `json:"discount_type" gorm:"type:text"` // percentage, flat DiscountValue float64 `json:"discount_value" gorm:"type:real;default:0"` DiscountAmount float64 `json:"discount_amount" gorm:"type:real;default:0"` ServiceCharge float64 `json:"service_charge" gorm:"type:real;default:0"` RoundOff float64 `json:"round_off" gorm:"type:real;default:0"` GrandTotal float64 `json:"grand_total" gorm:"type:real;default:0"` PaidAmount float64 `json:"paid_amount" gorm:"type:real;default:0"` ChangeAmount float64 `json:"change_amount" gorm:"type:real;default:0"` // External order info ExternalOrderID string `json:"external_order_id" gorm:"type:text"` // Platform-specific (Swiggy/Zomato) PlatformStatus string `json:"platform_status" gorm:"type:text"` // new, accepted, preparing, ready, picked_up, delivered, rejected RiderName string `json:"rider_name" gorm:"type:text"` RiderPhone string `json:"rider_phone" gorm:"type:text"` EstimatedDeliveryTime string `json:"estimated_delivery_time" gorm:"type:text"` PlatformCommission float64 `json:"platform_commission" gorm:"type:real;default:0"` AcceptedAt *time.Time `json:"accepted_at"` ReadyAt *time.Time `json:"ready_at"` PickedUpAt *time.Time `json:"picked_up_at"` DeliveredAt *time.Time `json:"delivered_at"` RejectionReason string `json:"rejection_reason" gorm:"type:text"` // Metadata Notes string `json:"notes" gorm:"type:text"` IsVoid bool `json:"is_void" gorm:"default:false"` VoidReason string `json:"void_reason" gorm:"type:text"` VoidedBy *string `json:"voided_by" gorm:"type:text"` IsPrinted bool `json:"is_printed" gorm:"default:false"` PrintCount int `json:"print_count" gorm:"type:integer;default:0"` CompletedAt *time.Time `json:"completed_at"` // Relations Items []OrderItem `json:"items" gorm:"foreignKey:OrderID"` KOTs []KOT `json:"kots" gorm:"foreignKey:OrderID"` Payments []Payment `json:"payments" gorm:"foreignKey:OrderID"` } type OrderItem struct { BaseModel OrderID string `json:"order_id" gorm:"type:text;not null;index"` ProductID string `json:"product_id" gorm:"type:text;not null"` Product Product `json:"product" gorm:"foreignKey:ProductID"` ProductName string `json:"product_name" gorm:"type:text;not null"` Quantity int `json:"quantity" gorm:"type:integer;not null;default:1"` UnitPrice float64 `json:"unit_price" gorm:"type:real;not null"` TaxRate float64 `json:"tax_rate" gorm:"type:real;default:5.0"` TaxAmount float64 `json:"tax_amount" gorm:"type:real;default:0"` DiscountAmount float64 `json:"discount_amount" gorm:"type:real;default:0"` TotalPrice float64 `json:"total_price" gorm:"type:real;not null"` KitchenStation KitchenStation `json:"kitchen_station" gorm:"type:text"` Notes string `json:"notes" gorm:"type:text"` Status string `json:"status" gorm:"type:text;default:'pending'"` // pending, preparing, ready, served IsCancelled bool `json:"is_cancelled" gorm:"default:false"` CancelReason string `json:"cancel_reason" gorm:"type:text"` CancelledBy string `json:"cancelled_by" gorm:"type:text"` CancelledAt *time.Time `json:"cancelled_at"` Modifiers []OrderItemModifier `json:"modifiers" gorm:"foreignKey:OrderItemID"` } type OrderItemModifier struct { BaseModel OrderItemID string `json:"order_item_id" gorm:"type:text;not null;index"` ModifierID string `json:"modifier_id" gorm:"type:text;not null"` Name string `json:"name" gorm:"type:text;not null"` Price float64 `json:"price" gorm:"type:real;default:0"` } // ============================================================================ // KOT (Kitchen Order Tickets) // ============================================================================ type KOTStatus string const ( KOTPending KOTStatus = "pending" KOTPreparing KOTStatus = "preparing" KOTReady KOTStatus = "ready" KOTServed KOTStatus = "served" KOTCancelled KOTStatus = "cancelled" ) type KOT struct { BaseModel KOTNumber string `json:"kot_number" gorm:"type:text;uniqueIndex;not null"` OrderID string `json:"order_id" gorm:"type:text;not null;index"` Order Order `json:"order" gorm:"foreignKey:OrderID"` KitchenStation KitchenStation `json:"kitchen_station" gorm:"type:text;not null"` Status KOTStatus `json:"status" gorm:"type:text;default:'pending'"` TableNumber int `json:"table_number" gorm:"type:integer"` CaptainName string `json:"captain_name" gorm:"type:text"` IsPrinted bool `json:"is_printed" gorm:"default:false"` IsModified bool `json:"is_modified" gorm:"default:false"` CompletedAt *time.Time `json:"completed_at" gorm:"type:datetime"` Notes string `json:"notes" gorm:"type:text"` Items []KOTItem `json:"items" gorm:"foreignKey:KOTID"` } type KOTItem struct { BaseModel KOTID string `json:"kot_id" gorm:"type:text;not null;index"` OrderItemID string `json:"order_item_id" gorm:"type:text;not null"` ProductName string `json:"product_name" gorm:"type:text;not null"` Quantity int `json:"quantity" gorm:"type:integer;not null"` Notes string `json:"notes" gorm:"type:text"` Modifiers string `json:"modifiers" gorm:"type:text"` // JSON string IsCancelled bool `json:"is_cancelled" gorm:"default:false"` CancelReason string `json:"cancel_reason" gorm:"type:text"` Status string `json:"status" gorm:"type:text;default:'pending'"` } // ============================================================================ // PAYMENTS // ============================================================================ type PaymentMethod string const ( PayCash PaymentMethod = "cash" PayCard PaymentMethod = "card" PayUPI PaymentMethod = "upi" PayWallet PaymentMethod = "wallet" PayOnline PaymentMethod = "online" ) type Payment struct { BaseModel OrderID string `json:"order_id" gorm:"type:text;not null;index"` Method PaymentMethod `json:"method" gorm:"type:text;not null"` Amount float64 `json:"amount" gorm:"type:real;not null"` Reference string `json:"reference" gorm:"type:text"` // transaction ID ReceivedBy string `json:"received_by" gorm:"type:text"` Notes string `json:"notes" gorm:"type:text"` } // ============================================================================ // INVENTORY // ============================================================================ type UnitType string const ( UnitKg UnitType = "kg" UnitGram UnitType = "g" UnitLitre UnitType = "l" UnitMl UnitType = "ml" UnitPiece UnitType = "pc" UnitBox UnitType = "box" ) type InventoryItem struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` SKU string `json:"sku" gorm:"type:text;uniqueIndex"` Unit UnitType `json:"unit" gorm:"type:text;not null"` CurrentStock float64 `json:"current_stock" gorm:"type:real;default:0"` MinStock float64 `json:"min_stock" gorm:"type:real;default:0"` MaxStock float64 `json:"max_stock" gorm:"type:real;default:0"` CostPerUnit float64 `json:"cost_per_unit" gorm:"type:real;default:0"` CategoryName string `json:"category_name" gorm:"type:text"` IsActive bool `json:"is_active" gorm:"default:true"` LastRestocked *time.Time `json:"last_restocked"` } type StockMovementType string const ( StockIn StockMovementType = "stock_in" StockOut StockMovementType = "stock_out" StockWastage StockMovementType = "wastage" StockPurchase StockMovementType = "purchase" StockConsumption StockMovementType = "consumption" // auto-deduct from recipe StockAdjustment StockMovementType = "adjustment" ) type StockMovement struct { BaseModel InventoryItemID string `json:"inventory_item_id" gorm:"type:text;not null;index"` InventoryItem InventoryItem `json:"inventory_item" gorm:"foreignKey:InventoryItemID"` Type StockMovementType `json:"type" gorm:"type:text;not null"` Quantity float64 `json:"quantity" gorm:"type:real;not null"` PreviousStock float64 `json:"previous_stock" gorm:"type:real"` NewStock float64 `json:"new_stock" gorm:"type:real"` CostPerUnit float64 `json:"cost_per_unit" gorm:"type:real;default:0"` TotalCost float64 `json:"total_cost" gorm:"type:real;default:0"` ReferenceType string `json:"reference_type" gorm:"type:text"` // order, purchase_order, manual ReferenceID string `json:"reference_id" gorm:"type:text"` Notes string `json:"notes" gorm:"type:text"` CreatedByID string `json:"created_by_id" gorm:"type:text"` CreatedBy User `json:"created_by" gorm:"foreignKey:CreatedByID"` } // ============================================================================ // RECIPES // ============================================================================ type Recipe struct { BaseModel ProductID string `json:"product_id" gorm:"type:text;not null;index"` Product Product `json:"product" gorm:"foreignKey:ProductID"` Name string `json:"name" gorm:"type:text;not null"` Yield int `json:"yield" gorm:"type:integer;default:1"` // serves N portions Notes string `json:"notes" gorm:"type:text"` Items []RecipeItem `json:"items" gorm:"foreignKey:RecipeID"` } type RecipeItem struct { BaseModel RecipeID string `json:"recipe_id" gorm:"type:text;not null;index"` InventoryItemID string `json:"inventory_item_id" gorm:"type:text;not null"` InventoryItem InventoryItem `json:"inventory_item" gorm:"foreignKey:InventoryItemID"` Quantity float64 `json:"quantity" gorm:"type:real;not null"` Unit UnitType `json:"unit" gorm:"type:text;not null"` } // ============================================================================ // VENDORS & PURCHASE ORDERS // ============================================================================ type Vendor struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` ContactPerson string `json:"contact_person" gorm:"type:text"` Phone string `json:"phone" gorm:"type:text"` Email string `json:"email" gorm:"type:text"` Address string `json:"address" gorm:"type:text"` GSTIN string `json:"gstin" gorm:"type:text"` BankDetails string `json:"bank_details" gorm:"type:text"` IsActive bool `json:"is_active" gorm:"default:true"` Notes string `json:"notes" gorm:"type:text"` } type POStatus string const ( PODraft POStatus = "draft" POSent POStatus = "sent" POReceived POStatus = "received" POPartial POStatus = "partial" POCancelled POStatus = "cancelled" ) type PurchaseOrder struct { BaseModel PONumber string `json:"po_number" gorm:"type:text;uniqueIndex;not null"` VendorID string `json:"vendor_id" gorm:"type:text;not null"` Vendor Vendor `json:"vendor" gorm:"foreignKey:VendorID"` Status POStatus `json:"status" gorm:"type:text;default:'draft'"` TotalAmount float64 `json:"total_amount" gorm:"type:real;default:0"` TaxAmount float64 `json:"tax_amount" gorm:"type:real;default:0"` GrandTotal float64 `json:"grand_total" gorm:"type:real;default:0"` Notes string `json:"notes" gorm:"type:text"` OrderedAt *time.Time `json:"ordered_at"` ReceivedAt *time.Time `json:"received_at"` CreatedByID string `json:"created_by_id" gorm:"type:text"` CreatedBy User `json:"created_by" gorm:"foreignKey:CreatedByID"` Items []PurchaseOrderItem `json:"items" gorm:"foreignKey:PurchaseOrderID"` } type PurchaseOrderItem struct { BaseModel PurchaseOrderID string `json:"purchase_order_id" gorm:"type:text;not null;index"` InventoryItemID string `json:"inventory_item_id" gorm:"type:text;not null"` InventoryItem InventoryItem `json:"inventory_item" gorm:"foreignKey:InventoryItemID"` Quantity float64 `json:"quantity" gorm:"type:real;not null"` Unit UnitType `json:"unit" gorm:"type:text;not null"` UnitPrice float64 `json:"unit_price" gorm:"type:real;not null"` TotalPrice float64 `json:"total_price" gorm:"type:real;not null"` ReceivedQty float64 `json:"received_qty" gorm:"type:real;default:0"` } // ============================================================================ // EXPENSES // ============================================================================ type ExpenseCategory string const ( ExpElectricity ExpenseCategory = "electricity" ExpSalary ExpenseCategory = "salary" ExpAdvertisement ExpenseCategory = "advertisement" ExpPettyCash ExpenseCategory = "petty_cash" ExpMaintenance ExpenseCategory = "maintenance" ExpRent ExpenseCategory = "rent" ExpOther ExpenseCategory = "other" ) type Expense struct { BaseModel Category ExpenseCategory `json:"category" gorm:"type:text;not null"` Description string `json:"description" gorm:"type:text;not null"` Amount float64 `json:"amount" gorm:"type:real;not null"` Date time.Time `json:"date" gorm:"type:datetime;not null"` VendorName string `json:"vendor_name" gorm:"type:text"` Reference string `json:"reference" gorm:"type:text"` Receipt string `json:"receipt" gorm:"type:text"` // file path ApprovedBy *string `json:"approved_by" gorm:"type:text"` CreatedByID string `json:"created_by_id" gorm:"type:text"` CreatedBy User `json:"created_by" gorm:"foreignKey:CreatedByID"` Notes string `json:"notes" gorm:"type:text"` } // ============================================================================ // CRM CAMPAIGNS // ============================================================================ type CampaignType string const ( CampaignSMS CampaignType = "sms" CampaignWhatsApp CampaignType = "whatsapp" CampaignEmail CampaignType = "email" ) type CRMCampaign struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` Type CampaignType `json:"type" gorm:"type:text;not null"` Message string `json:"message" gorm:"type:text;not null"` TargetCount int `json:"target_count" gorm:"type:integer;default:0"` SentCount int `json:"sent_count" gorm:"type:integer;default:0"` Status string `json:"status" gorm:"type:text;default:'draft'"` // draft, sent, scheduled ScheduledAt *time.Time `json:"scheduled_at"` SentAt *time.Time `json:"sent_at"` CreatedByID string `json:"created_by_id" gorm:"type:text"` } // ============================================================================ // PRINTERS & PRINT ROUTING // ============================================================================ type PrinterType string const ( PrinterTypeBill PrinterType = "bill" PrinterTypeKOT PrinterType = "kot" PrinterTypeBoth PrinterType = "both" ) type Printer struct { BaseModel Name string `json:"name" gorm:"type:text;not null"` IPAddress string `json:"ip_address" gorm:"type:text;not null"` Port int `json:"port" gorm:"type:integer;default:9100"` PaperWidth int `json:"paper_width" gorm:"type:integer;default:80"` // 58 or 80 Type PrinterType `json:"type" gorm:"type:text;default:'bill'"` IsActive bool `json:"is_active" gorm:"default:true"` IsDefault bool `json:"is_default" gorm:"default:false"` Routes []PrintRoute `json:"routes,omitempty" gorm:"foreignKey:PrinterID"` } type PrintRouteType string const ( RouteByOrderType PrintRouteType = "order_type" RouteByTableRange PrintRouteType = "table_range" ) type PrintRoute struct { BaseModel PrinterID string `json:"printer_id" gorm:"type:text;not null;index"` Printer Printer `json:"printer,omitempty" gorm:"foreignKey:PrinterID"` RouteType PrintRouteType `json:"route_type" gorm:"type:text;not null"` OrderSource string `json:"order_source" gorm:"type:text"` // for order_type routing TableFrom int `json:"table_from" gorm:"type:integer;default:0"` // for table_range routing TableTo int `json:"table_to" gorm:"type:integer;default:0"` // for table_range routing } // PrintJob stores print history type PrintJob struct { BaseModel PrinterID string `json:"printer_id" gorm:"type:text;not null;index"` Printer Printer `json:"printer,omitempty" gorm:"foreignKey:PrinterID"` OrderID string `json:"order_id" gorm:"type:text;index"` JobType string `json:"job_type" gorm:"type:text;not null"` // bill, kot Status string `json:"status" gorm:"type:text;default:'pending'"` // pending, sent, failed Error string `json:"error" gorm:"type:text"` PrintedByID string `json:"printed_by_id" gorm:"type:text"` PrintedBy User `json:"printed_by,omitempty" gorm:"foreignKey:PrintedByID"` } // ============================================================================ // AUDIT LOG // ============================================================================ type AuditLog struct { BaseModel UserID string `json:"user_id" gorm:"type:text;not null;index"` Username string `json:"username" gorm:"type:text"` Action string `json:"action" gorm:"type:text;not null"` // create, update, delete, void, reprint Module string `json:"module" gorm:"type:text;not null"` // order, kot, product, etc. EntityID string `json:"entity_id" gorm:"type:text"` OldValue string `json:"old_value" gorm:"type:text"` // JSON NewValue string `json:"new_value" gorm:"type:text"` // JSON IPAddress string `json:"ip_address" gorm:"type:text"` Description string `json:"description" gorm:"type:text"` } // ============================================================================ // NOTIFICATION LOG // ============================================================================ type NotificationLog struct { BaseModel Type string `json:"type" gorm:"type:text;not null"` // sms, whatsapp, email, push Recipient string `json:"recipient" gorm:"type:text;not null"` Subject string `json:"subject" gorm:"type:text"` Message string `json:"message" gorm:"type:text;not null"` Status string `json:"status" gorm:"type:text;default:'pending'"` // pending, sent, failed Error string `json:"error" gorm:"type:text"` SentAt *time.Time `json:"sent_at"` } // ============================================================================ // COUNTERS (for sequential numbering) // ============================================================================ type Counter struct { ID string `json:"id" gorm:"type:text;primaryKey"` // e.g., "order", "kot", "po" Prefix string `json:"prefix" gorm:"type:text"` Current int64 `json:"current" gorm:"type:integer;default:0"` Date string `json:"date" gorm:"type:text"` // for daily reset } // ============================================================================ // PLATFORM CONFIG (Swiggy / Zomato / etc.) // ============================================================================ type PlatformConfig struct { BaseModel Platform string `json:"platform" gorm:"type:text;uniqueIndex;not null"` // swiggy, zomato DisplayName string `json:"display_name" gorm:"type:text"` IsActive bool `json:"is_active" gorm:"type:boolean;default:false"` AutoAccept bool `json:"auto_accept" gorm:"type:boolean;default:false"` // API credentials APIKey string `json:"api_key" gorm:"type:text"` APISecret string `json:"-" gorm:"type:text"` MerchantID string `json:"merchant_id" gorm:"type:text"` RestaurantID string `json:"restaurant_id" gorm:"type:text"` WebhookSecret string `json:"-" gorm:"type:text"` // Callback URLs (for pushing status back to platform) StatusCallbackURL string `json:"status_callback_url" gorm:"type:text"` BaseURL string `json:"base_url" gorm:"type:text"` // Commission CommissionRate float64 `json:"commission_rate" gorm:"type:real;default:0"` // percentage // Prep time defaults DefaultPrepTime int `json:"default_prep_time" gorm:"type:integer;default:20"` // minutes } // ============================================================================ // WEBHOOK LOG // ============================================================================ type WebhookLog struct { BaseModel Platform string `json:"platform" gorm:"type:text;not null;index"` EventType string `json:"event_type" gorm:"type:text"` Payload string `json:"payload" gorm:"type:text"` Status string `json:"status" gorm:"type:text;default:'received'"` // received, processed, failed OrderID string `json:"order_id" gorm:"type:text"` ErrorMsg string `json:"error_message" gorm:"type:text"` IPAddress string `json:"ip_address" gorm:"type:text"` } // ============================================================================ // HYBRID SYNC // ============================================================================ // SyncOutbox stores pending events to be pushed to cloud. // Local writes here after every significant operation; sync worker reads and pushes. type SyncOutbox struct { ID uint `json:"id" gorm:"primaryKey;autoIncrement"` EntityType string `json:"entity_type" gorm:"type:text;not null;index"` // order, payment, product, category, inventory, expense, customer EntityID string `json:"entity_id" gorm:"type:text;not null"` Action string `json:"action" gorm:"type:text;not null"` // create, update, delete Payload string `json:"payload" gorm:"type:text"` // JSON snapshot of entity LocationID string `json:"location_id" gorm:"type:text;not null;index"` CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` SyncedAt *time.Time `json:"synced_at" gorm:"index"` // NULL = not synced yet RetryCount int `json:"retry_count" gorm:"default:0"` Error string `json:"error" gorm:"type:text"` } // SyncLocation represents a registered restaurant location (stored on cloud). type SyncLocation struct { BaseModel LocationID string `json:"location_id" gorm:"type:text;uniqueIndex;not null"` Name string `json:"name" gorm:"type:text;not null"` Address string `json:"address" gorm:"type:text"` Phone string `json:"phone" gorm:"type:text"` SyncToken string `json:"-" gorm:"type:text;not null"` // Hashed token for auth LastSyncAt *time.Time `json:"last_sync_at"` IsActive bool `json:"is_active" gorm:"default:true"` TotalOrders int `json:"total_orders" gorm:"default:0"` TotalRevenue float64 `json:"total_revenue" gorm:"default:0"` } // SyncedOrder stores order data received from local instances (cloud-side). type SyncedOrder struct { ID string `json:"id" gorm:"type:text;primaryKey"` LocationID string `json:"location_id" gorm:"type:text;not null;index"` OrderNumber string `json:"order_number" gorm:"type:text"` Source string `json:"source" gorm:"type:text"` Status string `json:"status" gorm:"type:text"` PaymentStatus string `json:"payment_status" gorm:"type:text"` SubTotal float64 `json:"sub_total"` TaxAmount float64 `json:"tax_amount"` DiscountAmount float64 `json:"discount_amount"` GrandTotal float64 `json:"grand_total"` PaymentMethod string `json:"payment_method" gorm:"type:text"` ItemCount int `json:"item_count"` CustomerName string `json:"customer_name" gorm:"type:text"` CustomerPhone string `json:"customer_phone" gorm:"type:text"` TableName string `json:"table_name" gorm:"type:text"` StaffName string `json:"staff_name" gorm:"type:text"` ItemsSummary string `json:"items_summary" gorm:"type:text"` // JSON array of {name, qty, total} OrderDate time.Time `json:"order_date" gorm:"index"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // SyncedDailySummary stores aggregated daily data per location (cloud-side). type SyncedDailySummary struct { ID uint `json:"id" gorm:"primaryKey;autoIncrement"` LocationID string `json:"location_id" gorm:"type:text;not null;index:idx_daily_loc_date"` Date string `json:"date" gorm:"type:text;not null;index:idx_daily_loc_date"` // YYYY-MM-DD TotalOrders int `json:"total_orders"` TotalRevenue float64 `json:"total_revenue"` TotalTax float64 `json:"total_tax"` TotalDiscount float64 `json:"total_discount"` CashRevenue float64 `json:"cash_revenue"` CardRevenue float64 `json:"card_revenue"` UPIRevenue float64 `json:"upi_revenue"` OtherRevenue float64 `json:"other_revenue"` AvgOrderValue float64 `json:"avg_order_value"` TopItems string `json:"top_items" gorm:"type:text"` // JSON OrdersByHour string `json:"orders_by_hour" gorm:"type:text"` // JSON CancelledOrders int `json:"cancelled_orders"` CancelledValue float64 `json:"cancelled_value"` ExpenseTotal float64 `json:"expense_total"` CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"` } // SyncStatus holds the last sync state (stored locally for UI display). type SyncStatus struct { ID uint `json:"id" gorm:"primaryKey;autoIncrement"` LastSyncAt *time.Time `json:"last_sync_at"` LastSyncStatus string `json:"last_sync_status" gorm:"type:text;default:'never'"` // never, success, failed, syncing LastSyncError string `json:"last_sync_error" gorm:"type:text"` PendingCount int `json:"pending_count" gorm:"default:0"` CloudReachable bool `json:"cloud_reachable" gorm:"default:false"` LastMenuPullAt *time.Time `json:"last_menu_pull_at"` } // PendingExternalOrder stores incoming webhook orders on the cloud, waiting for local to pull. // Cloud receives webhooks from Swiggy/Zomato/WhatsApp → stores here → local polls every 15s. type PendingExternalOrder struct { ID uint `json:"id" gorm:"primaryKey;autoIncrement"` LocationID string `json:"location_id" gorm:"type:text;not null;index"` Platform string `json:"platform" gorm:"type:text;not null"` // swiggy, zomato, whatsapp ExternalID string `json:"external_id" gorm:"type:text"` // Platform's order ID Payload string `json:"payload" gorm:"type:text;not null"` // Full webhook payload (JSON) Status string `json:"status" gorm:"type:text;default:'pending';index"` // pending, pulled, processed, failed PulledAt *time.Time `json:"pulled_at"` ProcessedAt *time.Time `json:"processed_at"` ErrorMsg string `json:"error_msg" gorm:"type:text"` CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"` } // ============================================================================ // ALL MODELS for migration // ============================================================================ func AllModels() []interface{} { return []interface{}{ &Role{}, &Permission{}, &User{}, &RestaurantConfig{}, &Customer{}, &TableSection{}, &RestaurantTable{}, &TableSession{}, &Category{}, &Product{}, &ModifierGroup{}, &Modifier{}, &Order{}, &OrderItem{}, &OrderItemModifier{}, &KOT{}, &KOTItem{}, &Payment{}, &InventoryItem{}, &StockMovement{}, &Recipe{}, &RecipeItem{}, &Vendor{}, &PurchaseOrder{}, &PurchaseOrderItem{}, &Expense{}, &CRMCampaign{}, &AuditLog{}, &NotificationLog{}, &Counter{}, &Printer{}, &PrintRoute{}, &PrintJob{}, &PlatformConfig{}, &WebhookLog{}, // Hybrid sync models &SyncOutbox{}, &SyncLocation{}, &SyncedOrder{}, &SyncedDailySummary{}, &SyncStatus{}, &PendingExternalOrder{}, } }