init
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// PaymentMethodAnalytics represents payment method analytics data
|
||||
type PaymentMethodAnalytics struct {
|
||||
PaymentMethodID uuid.UUID `json:"payment_method_id"`
|
||||
PaymentMethodName string `json:"payment_method_name"`
|
||||
PaymentMethodType string `json:"payment_method_type"`
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
OrderCount int64 `json:"order_count"`
|
||||
PaymentCount int64 `json:"payment_count"`
|
||||
}
|
||||
|
||||
// SalesAnalytics represents sales analytics data
|
||||
type SalesAnalytics struct {
|
||||
Date time.Time `json:"date"`
|
||||
Sales float64 `json:"sales"`
|
||||
Orders int64 `json:"orders"`
|
||||
Items int64 `json:"items"`
|
||||
Tax float64 `json:"tax"`
|
||||
Discount float64 `json:"discount"`
|
||||
NetSales float64 `json:"net_sales"`
|
||||
}
|
||||
|
||||
// ProductAnalytics represents product analytics data
|
||||
type ProductAnalytics struct {
|
||||
ProductID uuid.UUID `json:"product_id"`
|
||||
ProductName string `json:"product_name"`
|
||||
CategoryID uuid.UUID `json:"category_id"`
|
||||
CategoryName string `json:"category_name"`
|
||||
QuantitySold int64 `json:"quantity_sold"`
|
||||
Revenue float64 `json:"revenue"`
|
||||
AveragePrice float64 `json:"average_price"`
|
||||
OrderCount int64 `json:"order_count"`
|
||||
}
|
||||
|
||||
// DashboardOverview represents dashboard overview data
|
||||
type DashboardOverview struct {
|
||||
TotalSales float64 `json:"total_sales"`
|
||||
TotalOrders int64 `json:"total_orders"`
|
||||
AverageOrderValue float64 `json:"average_order_value"`
|
||||
TotalCustomers int64 `json:"total_customers"`
|
||||
VoidedOrders int64 `json:"voided_orders"`
|
||||
RefundedOrders int64 `json:"refunded_orders"`
|
||||
}
|
||||
|
||||
// ProfitLossAnalytics represents profit and loss analytics data
|
||||
type ProfitLossAnalytics struct {
|
||||
Summary ProfitLossSummary `json:"summary"`
|
||||
Data []ProfitLossData `json:"data"`
|
||||
ProductData []ProductProfitData `json:"product_data"`
|
||||
}
|
||||
|
||||
// ProfitLossSummary represents profit and loss summary data
|
||||
type ProfitLossSummary struct {
|
||||
TotalRevenue float64 `json:"total_revenue"`
|
||||
TotalCost float64 `json:"total_cost"`
|
||||
GrossProfit float64 `json:"gross_profit"`
|
||||
GrossProfitMargin float64 `json:"gross_profit_margin"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
TotalDiscount float64 `json:"total_discount"`
|
||||
NetProfit float64 `json:"net_profit"`
|
||||
NetProfitMargin float64 `json:"net_profit_margin"`
|
||||
TotalOrders int64 `json:"total_orders"`
|
||||
AverageProfit float64 `json:"average_profit"`
|
||||
ProfitabilityRatio float64 `json:"profitability_ratio"`
|
||||
}
|
||||
|
||||
// ProfitLossData represents profit and loss data by time period
|
||||
type ProfitLossData struct {
|
||||
Date time.Time `json:"date"`
|
||||
Revenue float64 `json:"revenue"`
|
||||
Cost float64 `json:"cost"`
|
||||
GrossProfit float64 `json:"gross_profit"`
|
||||
GrossProfitMargin float64 `json:"gross_profit_margin"`
|
||||
Tax float64 `json:"tax"`
|
||||
Discount float64 `json:"discount"`
|
||||
NetProfit float64 `json:"net_profit"`
|
||||
NetProfitMargin float64 `json:"net_profit_margin"`
|
||||
Orders int64 `json:"orders"`
|
||||
}
|
||||
|
||||
// ProductProfitData represents profit data for individual products
|
||||
type ProductProfitData struct {
|
||||
ProductID uuid.UUID `json:"product_id"`
|
||||
ProductName string `json:"product_name"`
|
||||
CategoryID uuid.UUID `json:"category_id"`
|
||||
CategoryName string `json:"category_name"`
|
||||
QuantitySold int64 `json:"quantity_sold"`
|
||||
Revenue float64 `json:"revenue"`
|
||||
Cost float64 `json:"cost"`
|
||||
GrossProfit float64 `json:"gross_profit"`
|
||||
GrossProfitMargin float64 `json:"gross_profit_margin"`
|
||||
AveragePrice float64 `json:"average_price"`
|
||||
AverageCost float64 `json:"average_cost"`
|
||||
ProfitPerUnit float64 `json:"profit_per_unit"`
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Metadata map[string]interface{}
|
||||
|
||||
func (m Metadata) Value() (driver.Value, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (m *Metadata) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*m = make(Metadata)
|
||||
return nil
|
||||
}
|
||||
|
||||
bytes, ok := value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("type assertion to []byte failed")
|
||||
}
|
||||
|
||||
return json.Unmarshal(bytes, m)
|
||||
}
|
||||
|
||||
type Category struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
|
||||
Description *string `gorm:"type:text" json:"description"`
|
||||
BusinessType string `gorm:"size:50;default:'restaurant'" json:"business_type"`
|
||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Products []Product `gorm:"foreignKey:CategoryID" json:"products,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Category) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.ID == uuid.Nil {
|
||||
c.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Category) TableName() string {
|
||||
return "categories"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Customer struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||
Name string `gorm:"not null;size:255" json:"name" validate:"required"`
|
||||
Email *string `gorm:"size:255;uniqueIndex" json:"email,omitempty"`
|
||||
Phone *string `gorm:"size:20" json:"phone,omitempty"`
|
||||
Address *string `gorm:"size:500" json:"address,omitempty"`
|
||||
IsDefault bool `gorm:"default:false" json:"is_default"`
|
||||
IsActive bool `gorm:"default:true" json:"is_active"`
|
||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Orders []Order `gorm:"foreignKey:CustomerID" json:"orders,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Customer) BeforeCreate(tx *gorm.DB) error {
|
||||
if c.ID == uuid.Nil {
|
||||
c.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Customer) TableName() string {
|
||||
return "customers"
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package entities
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
func GetAllEntities() []interface{} {
|
||||
return []interface{}{
|
||||
&Organization{},
|
||||
&Outlet{},
|
||||
&OutletSetting{},
|
||||
&User{},
|
||||
&Category{},
|
||||
&Product{},
|
||||
&ProductVariant{},
|
||||
&Inventory{},
|
||||
&Order{},
|
||||
&OrderItem{},
|
||||
&PaymentMethod{},
|
||||
&Payment{},
|
||||
&Customer{},
|
||||
// Analytics entities are not database tables, they are query results
|
||||
}
|
||||
}
|
||||
|
||||
func AutoMigrate(db *gorm.DB) error {
|
||||
return db.AutoMigrate(GetAllEntities()...)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null" json:"organization_id"`
|
||||
UserID uuid.UUID `gorm:"type:uuid;not null" json:"user_id"`
|
||||
FileName string `gorm:"size:255;not null" json:"file_name"`
|
||||
OriginalName string `gorm:"size:255;not null" json:"original_name"`
|
||||
FileURL string `gorm:"size:500;not null" json:"file_url"`
|
||||
FileSize int64 `gorm:"not null" json:"file_size"`
|
||||
MimeType string `gorm:"size:100;not null" json:"mime_type"`
|
||||
FileType string `gorm:"size:50;not null" json:"file_type"` // image, document, video, etc.
|
||||
UploadPath string `gorm:"size:500;not null" json:"upload_path"`
|
||||
IsPublic bool `gorm:"default:true" json:"is_public"`
|
||||
Metadata Metadata `gorm:"type:jsonb" json:"metadata"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (File) TableName() string {
|
||||
return "files"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Inventory struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id" validate:"required"`
|
||||
ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id" validate:"required"`
|
||||
Quantity int `gorm:"not null;default:0" json:"quantity" validate:"min=0"`
|
||||
ReorderLevel int `gorm:"default:0" json:"reorder_level" validate:"min=0"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
|
||||
Product Product `gorm:"foreignKey:ProductID" json:"product,omitempty"`
|
||||
}
|
||||
|
||||
func (i *Inventory) BeforeCreate(tx *gorm.DB) error {
|
||||
if i.ID == uuid.Nil {
|
||||
i.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Inventory) TableName() string {
|
||||
return "inventory"
|
||||
}
|
||||
|
||||
func (i *Inventory) IsLowStock() bool {
|
||||
return i.Quantity <= i.ReorderLevel
|
||||
}
|
||||
|
||||
func (i *Inventory) UpdateQuantity(delta int) {
|
||||
i.Quantity += delta
|
||||
if i.Quantity < 0 {
|
||||
i.Quantity = 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OrderType string
|
||||
type OrderStatus string
|
||||
type PaymentStatus string
|
||||
|
||||
const (
|
||||
OrderTypeDineIn OrderType = "dine_in"
|
||||
OrderTypeTakeout OrderType = "takeout"
|
||||
OrderTypeDelivery OrderType = "delivery"
|
||||
)
|
||||
|
||||
const (
|
||||
OrderStatusPending OrderStatus = "pending"
|
||||
OrderStatusPreparing OrderStatus = "preparing"
|
||||
OrderStatusReady OrderStatus = "ready"
|
||||
OrderStatusCompleted OrderStatus = "completed"
|
||||
OrderStatusCancelled OrderStatus = "cancelled"
|
||||
)
|
||||
|
||||
const (
|
||||
PaymentStatusPending PaymentStatus = "pending"
|
||||
PaymentStatusCompleted PaymentStatus = "completed"
|
||||
PaymentStatusFailed PaymentStatus = "failed"
|
||||
PaymentStatusRefunded PaymentStatus = "refunded"
|
||||
PaymentStatusPartiallyRefunded PaymentStatus = "partial-refunded"
|
||||
)
|
||||
|
||||
type Order struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||
OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id" validate:"required"`
|
||||
UserID uuid.UUID `gorm:"type:uuid;not null;index" json:"user_id" validate:"required"`
|
||||
CustomerID *uuid.UUID `gorm:"type:uuid;index" json:"customer_id"`
|
||||
OrderNumber string `gorm:"uniqueIndex;not null;size:50" json:"order_number" validate:"required"`
|
||||
TableNumber *string `gorm:"size:20" json:"table_number"`
|
||||
OrderType OrderType `gorm:"not null;size:50" json:"order_type" validate:"required,oneof=dine_in takeout delivery"`
|
||||
Status OrderStatus `gorm:"default:'pending';size:50" json:"status"`
|
||||
Subtotal float64 `gorm:"type:decimal(10,2);not null" json:"subtotal" validate:"required,min=0"`
|
||||
TaxAmount float64 `gorm:"type:decimal(10,2);not null" json:"tax_amount" validate:"required,min=0"`
|
||||
DiscountAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"discount_amount" validate:"min=0"`
|
||||
TotalAmount float64 `gorm:"type:decimal(10,2);not null" json:"total_amount" validate:"required,min=0"`
|
||||
TotalCost float64 `gorm:"type:decimal(10,2);default:0.00" json:"total_cost"`
|
||||
PaymentStatus PaymentStatus `gorm:"default:'pending';size:50" json:"payment_status"`
|
||||
RefundAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"refund_amount"`
|
||||
IsVoid bool `gorm:"default:false" json:"is_void"`
|
||||
IsRefund bool `gorm:"default:false" json:"is_refund"`
|
||||
VoidReason *string `gorm:"size:255" json:"void_reason,omitempty"`
|
||||
VoidedAt *time.Time `gorm:"" json:"voided_at,omitempty"`
|
||||
VoidedBy *uuid.UUID `gorm:"type:uuid" json:"voided_by,omitempty"`
|
||||
RefundReason *string `gorm:"size:255" json:"refund_reason,omitempty"`
|
||||
RefundedAt *time.Time `gorm:"" json:"refunded_at,omitempty"`
|
||||
RefundedBy *uuid.UUID `gorm:"type:uuid" json:"refunded_by,omitempty"`
|
||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
|
||||
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
||||
OrderItems []OrderItem `gorm:"foreignKey:OrderID" json:"order_items,omitempty"`
|
||||
Payments []Payment `gorm:"foreignKey:OrderID" json:"payments,omitempty"`
|
||||
}
|
||||
|
||||
func (o *Order) BeforeCreate(tx *gorm.DB) error {
|
||||
if o.ID == uuid.Nil {
|
||||
o.ID = uuid.New()
|
||||
}
|
||||
|
||||
if o.OrderNumber == "" {
|
||||
timestamp := time.Now().Unix()
|
||||
o.OrderNumber = fmt.Sprintf("ORD/%d", timestamp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Order) TableName() string {
|
||||
return "orders"
|
||||
}
|
||||
|
||||
func (o *Order) CanBeModified() bool {
|
||||
return o.Status == OrderStatusPending
|
||||
}
|
||||
|
||||
func (o *Order) CanBeCancelled() bool {
|
||||
return o.Status != OrderStatusCompleted && o.Status != OrderStatusCancelled
|
||||
}
|
||||
|
||||
func (o *Order) GetTotalPaid() float64 {
|
||||
var total float64
|
||||
for _, payment := range o.Payments {
|
||||
if payment.Status == PaymentTransactionStatusCompleted {
|
||||
total += payment.Amount
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (o *Order) IsFullyPaid() bool {
|
||||
return o.GetTotalPaid() >= o.TotalAmount
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Modifiers []map[string]interface{}
|
||||
|
||||
func (m Modifiers) Value() (driver.Value, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (m *Modifiers) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*m = make(Modifiers, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
bytes, ok := value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("type assertion to []byte failed")
|
||||
}
|
||||
|
||||
return json.Unmarshal(bytes, m)
|
||||
}
|
||||
|
||||
type OrderItemStatus string
|
||||
|
||||
const (
|
||||
OrderItemStatusPending OrderItemStatus = "pending"
|
||||
OrderItemStatusPreparing OrderItemStatus = "preparing"
|
||||
OrderItemStatusReady OrderItemStatus = "ready"
|
||||
OrderItemStatusServed OrderItemStatus = "served"
|
||||
OrderItemStatusCancelled OrderItemStatus = "cancelled"
|
||||
)
|
||||
|
||||
type OrderItem struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrderID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_id" validate:"required"`
|
||||
ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id" validate:"required"`
|
||||
ProductVariantID *uuid.UUID `gorm:"type:uuid;index" json:"product_variant_id"`
|
||||
Quantity int `gorm:"not null" json:"quantity" validate:"required,min=1"`
|
||||
UnitPrice float64 `gorm:"type:decimal(10,2);not null" json:"unit_price" validate:"required,min=0"`
|
||||
TotalPrice float64 `gorm:"type:decimal(10,2);not null" json:"total_price" validate:"required,min=0"`
|
||||
UnitCost float64 `gorm:"type:decimal(10,2);default:0.00" json:"unit_cost"`
|
||||
TotalCost float64 `gorm:"type:decimal(10,2);default:0.00" json:"total_cost"`
|
||||
RefundAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"refund_amount"`
|
||||
RefundQuantity int `gorm:"default:0" json:"refund_quantity"`
|
||||
IsPartiallyRefunded bool `gorm:"default:false" json:"is_partially_refunded"`
|
||||
IsFullyRefunded bool `gorm:"default:false" json:"is_fully_refunded"`
|
||||
RefundReason *string `gorm:"size:255" json:"refund_reason,omitempty"`
|
||||
RefundedAt *time.Time `gorm:"" json:"refunded_at,omitempty"`
|
||||
RefundedBy *uuid.UUID `gorm:"type:uuid" json:"refunded_by,omitempty"`
|
||||
Modifiers Modifiers `gorm:"type:jsonb;default:'[]'" json:"modifiers"`
|
||||
Notes *string `gorm:"size:500" json:"notes,omitempty"`
|
||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||
Status OrderItemStatus `gorm:"default:'pending';size:50" json:"status"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Order Order `gorm:"foreignKey:OrderID" json:"order,omitempty"`
|
||||
Product Product `gorm:"foreignKey:ProductID" json:"product,omitempty"`
|
||||
ProductVariant *ProductVariant `gorm:"foreignKey:ProductVariantID" json:"product_variant,omitempty"`
|
||||
}
|
||||
|
||||
func (oi *OrderItem) BeforeCreate(tx *gorm.DB) error {
|
||||
if oi.ID == uuid.Nil {
|
||||
oi.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (OrderItem) TableName() string {
|
||||
return "order_items"
|
||||
}
|
||||
|
||||
func (oi *OrderItem) CalculateTotalPrice() {
|
||||
oi.TotalPrice = float64(oi.Quantity) * oi.UnitPrice
|
||||
}
|
||||
|
||||
func (oi *OrderItem) CanBeModified() bool {
|
||||
return oi.Status == OrderItemStatusPending
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OrderSequence struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id"`
|
||||
OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id"`
|
||||
Year int `gorm:"not null" json:"year"`
|
||||
Month int `gorm:"not null" json:"month"`
|
||||
SequenceNumber int `gorm:"not null;default:0" json:"sequence_number"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
|
||||
}
|
||||
|
||||
func (os *OrderSequence) BeforeCreate(tx *gorm.DB) error {
|
||||
if os.ID == uuid.Nil {
|
||||
os.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (OrderSequence) TableName() string {
|
||||
return "order_sequences"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Organization struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
|
||||
Email *string `gorm:"size:255" json:"email" validate:"omitempty,email"`
|
||||
PhoneNumber *string `gorm:"size:20" json:"phone_number" validate:"omitempty"`
|
||||
PlanType string `gorm:"not null;size:50" json:"plan_type" validate:"required,oneof=basic premium enterprise"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Outlets []Outlet `gorm:"foreignKey:OrganizationID" json:"outlets,omitempty"`
|
||||
Users []User `gorm:"foreignKey:OrganizationID" json:"users,omitempty"`
|
||||
}
|
||||
|
||||
func (o *Organization) BeforeCreate(tx *gorm.DB) error {
|
||||
if o.ID == uuid.Nil {
|
||||
id := uuid.New()
|
||||
o.ID = id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Organization) TableName() string {
|
||||
return "organizations"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Outlet struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
|
||||
Address *string `gorm:"type:text" json:"address"`
|
||||
Timezone *string `gorm:"size:50" json:"timezone"`
|
||||
Currency string `gorm:"size:3;default:'USD'" json:"currency" validate:"len=3"`
|
||||
TaxRate float64 `gorm:"type:decimal(5,4);default:0.0000" json:"tax_rate" validate:"min=0,max=1"`
|
||||
IsActive bool `gorm:"default:true" json:"is_active"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Users []User `gorm:"foreignKey:OutletID" json:"users,omitempty"`
|
||||
Orders []Order `gorm:"foreignKey:OutletID" json:"orders,omitempty"`
|
||||
Inventory []Inventory `gorm:"foreignKey:OutletID" json:"inventory,omitempty"`
|
||||
Settings []OutletSetting `gorm:"foreignKey:OutletID" json:"settings,omitempty"`
|
||||
}
|
||||
|
||||
func (o *Outlet) BeforeCreate(tx *gorm.DB) error {
|
||||
if o.ID == uuid.Nil {
|
||||
o.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Outlet) TableName() string {
|
||||
return "outlets"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type OutletSetting struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OutletID uuid.UUID `gorm:"type:uuid;not null;index" json:"outlet_id" validate:"required"`
|
||||
Key string `gorm:"not null;size:255;index" json:"key" validate:"required,min=1,max=255"`
|
||||
Value string `gorm:"type:text" json:"value"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Outlet Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
|
||||
}
|
||||
|
||||
func (os *OutletSetting) BeforeCreate(tx *gorm.DB) error {
|
||||
if os.ID == uuid.Nil {
|
||||
os.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (OutletSetting) TableName() string {
|
||||
return "outlet_settings"
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type PaymentMethodType string
|
||||
|
||||
const (
|
||||
PaymentMethodTypeCash PaymentMethodType = "cash"
|
||||
PaymentMethodTypeCard PaymentMethodType = "card"
|
||||
PaymentMethodTypeDigitalWallet PaymentMethodType = "digital_wallet"
|
||||
)
|
||||
|
||||
type PaymentMethod struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||
Name string `gorm:"not null;size:100" json:"name" validate:"required,min=1,max=100"`
|
||||
Type PaymentMethodType `gorm:"not null;size:50" json:"type" validate:"required,oneof=cash card digital_wallet"`
|
||||
Processor *string `gorm:"size:100" json:"processor"`
|
||||
Configuration Metadata `gorm:"type:jsonb;default:'{}'" json:"configuration"`
|
||||
IsActive bool `gorm:"default:true" json:"is_active"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Payments []Payment `gorm:"foreignKey:PaymentMethodID" json:"payments,omitempty"`
|
||||
}
|
||||
|
||||
func (pm *PaymentMethod) BeforeCreate(tx *gorm.DB) error {
|
||||
if pm.ID == uuid.Nil {
|
||||
pm.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (PaymentMethod) TableName() string {
|
||||
return "payment_methods"
|
||||
}
|
||||
|
||||
type PaymentTransactionStatus string
|
||||
|
||||
const (
|
||||
PaymentTransactionStatusPending PaymentTransactionStatus = "pending"
|
||||
PaymentTransactionStatusCompleted PaymentTransactionStatus = "completed"
|
||||
PaymentTransactionStatusFailed PaymentTransactionStatus = "failed"
|
||||
PaymentTransactionStatusRefunded PaymentTransactionStatus = "refunded"
|
||||
)
|
||||
|
||||
type Payment struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrderID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_id" validate:"required"`
|
||||
PaymentMethodID uuid.UUID `gorm:"type:uuid;not null;index" json:"payment_method_id" validate:"required"`
|
||||
Amount float64 `gorm:"type:decimal(10,2);not null" json:"amount" validate:"required,min=0"`
|
||||
Status PaymentTransactionStatus `gorm:"default:'pending';size:50" json:"status"`
|
||||
TransactionID *string `gorm:"size:255" json:"transaction_id"`
|
||||
SplitNumber int `gorm:"default:1" json:"split_number"`
|
||||
SplitTotal int `gorm:"default:1" json:"split_total"`
|
||||
SplitDescription *string `gorm:"size:255" json:"split_description,omitempty"`
|
||||
RefundAmount float64 `gorm:"type:decimal(10,2);default:0.00" json:"refund_amount"`
|
||||
RefundReason *string `gorm:"size:255" json:"refund_reason,omitempty"`
|
||||
RefundedAt *time.Time `gorm:"" json:"refunded_at,omitempty"`
|
||||
RefundedBy *uuid.UUID `gorm:"type:uuid" json:"refunded_by,omitempty"`
|
||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Order Order `gorm:"foreignKey:OrderID" json:"order,omitempty"`
|
||||
PaymentMethod PaymentMethod `gorm:"foreignKey:PaymentMethodID" json:"payment_method,omitempty"`
|
||||
PaymentOrderItems []PaymentOrderItem `gorm:"foreignKey:PaymentID" json:"payment_order_items,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Payment) BeforeCreate(tx *gorm.DB) error {
|
||||
if p.ID == uuid.Nil {
|
||||
p.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Payment) TableName() string {
|
||||
return "payments"
|
||||
}
|
||||
|
||||
func (p *Payment) CanBeRefunded() bool {
|
||||
return p.Status == PaymentTransactionStatusCompleted
|
||||
}
|
||||
|
||||
func (p *Payment) IsSuccessful() bool {
|
||||
return p.Status == PaymentTransactionStatusCompleted
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type PaymentOrderItem struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
PaymentID uuid.UUID `gorm:"type:uuid;not null;index" json:"payment_id"`
|
||||
OrderItemID uuid.UUID `gorm:"type:uuid;not null;index" json:"order_item_id"`
|
||||
Amount float64 `gorm:"type:decimal(10,2);not null" json:"amount"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Payment Payment `gorm:"foreignKey:PaymentID" json:"payment,omitempty"`
|
||||
OrderItem OrderItem `gorm:"foreignKey:OrderItemID" json:"order_item,omitempty"`
|
||||
}
|
||||
|
||||
func (poi *PaymentOrderItem) BeforeCreate(tx *gorm.DB) error {
|
||||
if poi.ID == uuid.Nil {
|
||||
poi.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (PaymentOrderItem) TableName() string {
|
||||
return "payment_order_items"
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Product struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||
CategoryID uuid.UUID `gorm:"type:uuid;not null;index" json:"category_id" validate:"required"`
|
||||
SKU *string `gorm:"size:100;index" json:"sku"`
|
||||
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
|
||||
Description *string `gorm:"type:text" json:"description"`
|
||||
Price float64 `gorm:"type:decimal(10,2);not null" json:"price" validate:"required,min=0"`
|
||||
Cost float64 `gorm:"type:decimal(10,2);default:0.00" json:"cost" validate:"min=0"`
|
||||
BusinessType string `gorm:"size:50;default:'restaurant'" json:"business_type"`
|
||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||
IsActive bool `gorm:"default:true" json:"is_active"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Category Category `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
|
||||
ProductVariants []ProductVariant `gorm:"foreignKey:ProductID" json:"variants,omitempty"`
|
||||
Inventory []Inventory `gorm:"foreignKey:ProductID" json:"inventory,omitempty"`
|
||||
OrderItems []OrderItem `gorm:"foreignKey:ProductID" json:"order_items,omitempty"`
|
||||
}
|
||||
|
||||
func (p *Product) BeforeCreate(tx *gorm.DB) error {
|
||||
if p.ID == uuid.Nil {
|
||||
p.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Product) TableName() string {
|
||||
return "products"
|
||||
}
|
||||
|
||||
type ProductVariant struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
ProductID uuid.UUID `gorm:"type:uuid;not null;index" json:"product_id" validate:"required"`
|
||||
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
|
||||
PriceModifier float64 `gorm:"type:decimal(10,2);default:0.00" json:"price_modifier"`
|
||||
Cost float64 `gorm:"type:decimal(10,2);default:0.00" json:"cost" validate:"min=0"`
|
||||
Metadata Metadata `gorm:"type:jsonb;default:'{}'" json:"metadata"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
|
||||
Product Product `gorm:"foreignKey:ProductID" json:"product,omitempty"`
|
||||
OrderItems []OrderItem `gorm:"foreignKey:ProductVariantID" json:"order_items,omitempty"`
|
||||
}
|
||||
|
||||
func (pv *ProductVariant) BeforeCreate(tx *gorm.DB) error {
|
||||
if pv.ID == uuid.Nil {
|
||||
pv.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ProductVariant) TableName() string {
|
||||
return "product_variants"
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type UserRole string
|
||||
|
||||
const (
|
||||
RoleAdmin UserRole = "admin"
|
||||
RoleManager UserRole = "manager"
|
||||
RoleCashier UserRole = "cashier"
|
||||
RoleWaiter UserRole = "waiter"
|
||||
)
|
||||
|
||||
type Permissions map[string]interface{}
|
||||
|
||||
func (p Permissions) Value() (driver.Value, error) {
|
||||
return json.Marshal(p)
|
||||
}
|
||||
|
||||
func (p *Permissions) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*p = make(Permissions)
|
||||
return nil
|
||||
}
|
||||
|
||||
bytes, ok := value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("type assertion to []byte failed")
|
||||
}
|
||||
|
||||
return json.Unmarshal(bytes, p)
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
OrganizationID uuid.UUID `gorm:"type:uuid;not null;index" json:"organization_id" validate:"required"`
|
||||
OutletID *uuid.UUID `gorm:"type:uuid;index" json:"outlet_id"`
|
||||
Name string `gorm:"not null;size:255" json:"name" validate:"required,min=1,max=255"`
|
||||
Email string `gorm:"uniqueIndex;not null;size:255" json:"email" validate:"required,email"`
|
||||
PasswordHash string `gorm:"not null;size:255" json:"-"`
|
||||
Role UserRole `gorm:"not null;size:50" json:"role" validate:"required,oneof=admin manager cashier waiter"`
|
||||
Permissions Permissions `gorm:"type:jsonb;default:'{}'" json:"permissions"`
|
||||
IsActive bool `gorm:"default:true" json:"is_active"`
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"`
|
||||
Organization Organization `gorm:"foreignKey:OrganizationID" json:"organization,omitempty"`
|
||||
Outlet *Outlet `gorm:"foreignKey:OutletID" json:"outlet,omitempty"`
|
||||
Orders []Order `gorm:"foreignKey:UserID" json:"orders,omitempty"`
|
||||
}
|
||||
|
||||
func (u *User) BeforeCreate(tx *gorm.DB) error {
|
||||
if u.ID == uuid.Nil {
|
||||
u.ID = uuid.New()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (User) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
func (u *User) HasPermission(permission string) bool {
|
||||
if u.Role == RoleAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
if value, exists := u.Permissions[permission]; exists {
|
||||
if hasPermission, ok := value.(bool); ok {
|
||||
return hasPermission
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *User) CanAccessOutlet(outletID uuid.UUID) bool {
|
||||
if u.Role == RoleAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.OutletID != nil && *u.OutletID == outletID {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user