init
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// PaymentMethodAnalyticsRequest represents the request for payment method analytics
|
||||
type PaymentMethodAnalyticsRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
OutletID *uuid.UUID `validate:"omitempty"`
|
||||
DateFrom time.Time `validate:"required"`
|
||||
DateTo time.Time `validate:"required"`
|
||||
GroupBy string `validate:"omitempty,oneof=day hour week month"`
|
||||
}
|
||||
|
||||
// PaymentMethodAnalyticsResponse represents the response for payment method analytics
|
||||
type PaymentMethodAnalyticsResponse struct {
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
|
||||
DateFrom time.Time `json:"date_from"`
|
||||
DateTo time.Time `json:"date_to"`
|
||||
GroupBy string `json:"group_by"`
|
||||
Summary PaymentMethodSummary `json:"summary"`
|
||||
Data []PaymentMethodAnalyticsData `json:"data"`
|
||||
}
|
||||
|
||||
// PaymentMethodSummary represents the summary of payment method analytics
|
||||
type PaymentMethodSummary struct {
|
||||
TotalAmount float64 `json:"total_amount"`
|
||||
TotalOrders int64 `json:"total_orders"`
|
||||
TotalPayments int64 `json:"total_payments"`
|
||||
AverageOrderValue float64 `json:"average_order_value"`
|
||||
}
|
||||
|
||||
// PaymentMethodAnalyticsData represents individual payment method analytics data
|
||||
type PaymentMethodAnalyticsData 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"`
|
||||
Percentage float64 `json:"percentage"`
|
||||
}
|
||||
|
||||
// SalesAnalyticsRequest represents the request for sales analytics
|
||||
type SalesAnalyticsRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
OutletID *uuid.UUID `validate:"omitempty"`
|
||||
DateFrom time.Time `validate:"required"`
|
||||
DateTo time.Time `validate:"required"`
|
||||
GroupBy string `validate:"omitempty,oneof=day hour week month"`
|
||||
}
|
||||
|
||||
// SalesAnalyticsResponse represents the response for sales analytics
|
||||
type SalesAnalyticsResponse struct {
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
|
||||
DateFrom time.Time `json:"date_from"`
|
||||
DateTo time.Time `json:"date_to"`
|
||||
GroupBy string `json:"group_by"`
|
||||
Summary SalesSummary `json:"summary"`
|
||||
Data []SalesAnalyticsData `json:"data"`
|
||||
}
|
||||
|
||||
// SalesSummary represents the summary of sales analytics
|
||||
type SalesSummary struct {
|
||||
TotalSales float64 `json:"total_sales"`
|
||||
TotalOrders int64 `json:"total_orders"`
|
||||
TotalItems int64 `json:"total_items"`
|
||||
AverageOrderValue float64 `json:"average_order_value"`
|
||||
TotalTax float64 `json:"total_tax"`
|
||||
TotalDiscount float64 `json:"total_discount"`
|
||||
NetSales float64 `json:"net_sales"`
|
||||
}
|
||||
|
||||
// SalesAnalyticsData represents individual sales analytics data point
|
||||
type SalesAnalyticsData 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"`
|
||||
}
|
||||
|
||||
// ProductAnalyticsRequest represents the request for product analytics
|
||||
type ProductAnalyticsRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
OutletID *uuid.UUID `validate:"omitempty"`
|
||||
DateFrom time.Time `validate:"required"`
|
||||
DateTo time.Time `validate:"required"`
|
||||
Limit int `validate:"min=1,max=100"`
|
||||
}
|
||||
|
||||
// ProductAnalyticsResponse represents the response for product analytics
|
||||
type ProductAnalyticsResponse struct {
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
|
||||
DateFrom time.Time `json:"date_from"`
|
||||
DateTo time.Time `json:"date_to"`
|
||||
Data []ProductAnalyticsData `json:"data"`
|
||||
}
|
||||
|
||||
// ProductAnalyticsData represents individual product analytics data
|
||||
type ProductAnalyticsData 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"`
|
||||
}
|
||||
|
||||
// DashboardAnalyticsRequest represents the request for dashboard analytics
|
||||
type DashboardAnalyticsRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
OutletID *uuid.UUID `validate:"omitempty"`
|
||||
DateFrom time.Time `validate:"required"`
|
||||
DateTo time.Time `validate:"required"`
|
||||
}
|
||||
|
||||
// DashboardAnalyticsResponse represents the response for dashboard analytics
|
||||
type DashboardAnalyticsResponse struct {
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
|
||||
DateFrom time.Time `json:"date_from"`
|
||||
DateTo time.Time `json:"date_to"`
|
||||
Overview DashboardOverview `json:"overview"`
|
||||
TopProducts []ProductAnalyticsData `json:"top_products"`
|
||||
PaymentMethods []PaymentMethodAnalyticsData `json:"payment_methods"`
|
||||
RecentSales []SalesAnalyticsData `json:"recent_sales"`
|
||||
}
|
||||
|
||||
// DashboardOverview represents the overview data for dashboard
|
||||
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"`
|
||||
}
|
||||
|
||||
// ProfitLossAnalyticsRequest represents the request for profit and loss analytics
|
||||
type ProfitLossAnalyticsRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
OutletID *uuid.UUID `validate:"omitempty"`
|
||||
DateFrom time.Time `validate:"required"`
|
||||
DateTo time.Time `validate:"required"`
|
||||
GroupBy string `validate:"omitempty,oneof=day hour week month"`
|
||||
}
|
||||
|
||||
// ProfitLossAnalyticsResponse represents the response for profit and loss analytics
|
||||
type ProfitLossAnalyticsResponse struct {
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
OutletID *uuid.UUID `json:"outlet_id,omitempty"`
|
||||
DateFrom time.Time `json:"date_from"`
|
||||
DateTo time.Time `json:"date_to"`
|
||||
GroupBy string `json:"group_by"`
|
||||
Summary ProfitLossSummary `json:"summary"`
|
||||
Data []ProfitLossData `json:"data"`
|
||||
ProductData []ProductProfitData `json:"product_data"`
|
||||
}
|
||||
|
||||
// ProfitLossSummary represents the summary of profit and loss analytics
|
||||
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 individual profit and loss data point 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,47 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Category struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
Name string
|
||||
Description *string
|
||||
ImageURL *string
|
||||
SortOrder int
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateCategoryRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
Name string `validate:"required,min=1,max=255"`
|
||||
Description *string `validate:"omitempty,max=1000"`
|
||||
ImageURL *string `validate:"omitempty,url"`
|
||||
SortOrder int `validate:"min=0"`
|
||||
}
|
||||
|
||||
type UpdateCategoryRequest struct {
|
||||
Name *string `validate:"omitempty,min=1,max=255"`
|
||||
Description *string `validate:"omitempty,max=1000"`
|
||||
ImageURL *string `validate:"omitempty,url"`
|
||||
SortOrder *int `validate:"omitempty,min=0"`
|
||||
IsActive *bool
|
||||
}
|
||||
|
||||
type CategoryResponse struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
Name string
|
||||
Description *string
|
||||
ImageURL *string
|
||||
SortOrder int
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/entities"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type CreateCustomerRequest struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Email *string `json:"email,omitempty" validate:"omitempty,email"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
Address *string `json:"address,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateCustomerRequest struct {
|
||||
Name *string `json:"name,omitempty" validate:"omitempty,required"`
|
||||
Email *string `json:"email,omitempty" validate:"omitempty,email"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
Address *string `json:"address,omitempty"`
|
||||
IsActive *bool `json:"is_active,omitempty"`
|
||||
}
|
||||
|
||||
type CustomerResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
Name string `json:"name"`
|
||||
Email *string `json:"email,omitempty"`
|
||||
Phone *string `json:"phone,omitempty"`
|
||||
Address *string `json:"address,omitempty"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Metadata entities.Metadata `json:"metadata"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ListCustomersQuery represents query parameters for listing customers
|
||||
type ListCustomersQuery struct {
|
||||
Page int `query:"page" validate:"min=1"`
|
||||
Limit int `query:"limit" validate:"min=1,max=100"`
|
||||
Search string `query:"search"`
|
||||
IsActive *bool `query:"is_active"`
|
||||
IsDefault *bool `query:"is_default"`
|
||||
SortBy string `query:"sort_by" validate:"omitempty,oneof=name email created_at updated_at"`
|
||||
SortOrder string `query:"sort_order" validate:"omitempty,oneof=asc desc"`
|
||||
}
|
||||
|
||||
// SetDefaultCustomerRequest represents the request to set a customer as default
|
||||
type SetDefaultCustomerRequest struct {
|
||||
CustomerID uuid.UUID `json:"customer_id" validate:"required"`
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
FileName string
|
||||
OriginalName string
|
||||
FileURL string
|
||||
FileSize int64
|
||||
MimeType string
|
||||
FileType string
|
||||
UploadPath string
|
||||
IsPublic bool
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// Request DTOs
|
||||
type UploadFileRequest struct {
|
||||
FileType constants.FileType `validate:"required"`
|
||||
IsPublic *bool `validate:"omitempty"`
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
type UpdateFileRequest struct {
|
||||
IsPublic *bool `validate:"omitempty"`
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
type ListFilesRequest struct {
|
||||
OrganizationID *uuid.UUID
|
||||
UserID *uuid.UUID
|
||||
FileType *constants.FileType
|
||||
IsPublic *bool
|
||||
DateFrom *time.Time
|
||||
DateTo *time.Time
|
||||
Search string
|
||||
Page int `validate:"required,min=1"`
|
||||
Limit int `validate:"required,min=1,max=100"`
|
||||
}
|
||||
|
||||
// Response DTOs
|
||||
type FileResponse struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
FileName string
|
||||
OriginalName string
|
||||
FileURL string
|
||||
FileSize int64
|
||||
MimeType string
|
||||
FileType string
|
||||
UploadPath string
|
||||
IsPublic bool
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type ListFilesResponse struct {
|
||||
Files []*FileResponse
|
||||
TotalCount int
|
||||
Page int
|
||||
Limit int
|
||||
TotalPages int
|
||||
}
|
||||
|
||||
type UploadFileResponse struct {
|
||||
File FileResponse
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
func (f *File) IsImage() bool {
|
||||
return f.FileType == string(constants.FileTypeImage)
|
||||
}
|
||||
|
||||
func (f *File) IsDocument() bool {
|
||||
return f.FileType == string(constants.FileTypeDocument)
|
||||
}
|
||||
|
||||
func (f *File) IsVideo() bool {
|
||||
return f.FileType == string(constants.FileTypeVideo)
|
||||
}
|
||||
|
||||
func (f *File) GetFileExtension() string {
|
||||
// Extract extension from original name
|
||||
for i := len(f.OriginalName) - 1; i >= 0; i-- {
|
||||
if f.OriginalName[i] == '.' {
|
||||
return f.OriginalName[i:]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Inventory struct {
|
||||
ID uuid.UUID
|
||||
OutletID uuid.UUID
|
||||
ProductID uuid.UUID
|
||||
Quantity int
|
||||
ReorderLevel int
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateInventoryRequest struct {
|
||||
OutletID uuid.UUID
|
||||
ProductID uuid.UUID
|
||||
Quantity int
|
||||
ReorderLevel int
|
||||
}
|
||||
|
||||
type UpdateInventoryRequest struct {
|
||||
Quantity *int
|
||||
ReorderLevel *int
|
||||
}
|
||||
|
||||
type InventoryAdjustmentRequest struct {
|
||||
Delta int
|
||||
Reason string
|
||||
}
|
||||
|
||||
type InventoryResponse struct {
|
||||
ID uuid.UUID
|
||||
OutletID uuid.UUID
|
||||
ProductID uuid.UUID
|
||||
Quantity int
|
||||
ReorderLevel int
|
||||
IsLowStock bool
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (i *Inventory) IsLowStock() bool {
|
||||
return i.Quantity <= i.ReorderLevel
|
||||
}
|
||||
|
||||
func (i *Inventory) CanFulfillOrder(requestedQuantity int) bool {
|
||||
return i.Quantity >= requestedQuantity
|
||||
}
|
||||
|
||||
func (i *Inventory) AdjustQuantity(delta int) int {
|
||||
newQuantity := i.Quantity + delta
|
||||
if newQuantity < 0 {
|
||||
newQuantity = 0
|
||||
}
|
||||
return newQuantity
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package models
|
||||
|
||||
// Pagination represents pagination information
|
||||
type Pagination struct {
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
Total int64 `json:"total"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
// PaginatedResponse represents a paginated response
|
||||
type PaginatedResponse[T any] struct {
|
||||
Data []T `json:"data"`
|
||||
Pagination Pagination `json:"pagination"`
|
||||
}
|
||||
|
||||
func GetAllModelNames() []string {
|
||||
return []string{
|
||||
"Organization",
|
||||
"Outlet",
|
||||
"User",
|
||||
"Category",
|
||||
"Product",
|
||||
"ProductVariant",
|
||||
"Inventory",
|
||||
"Order",
|
||||
"OrderItem",
|
||||
"PaymentMethod",
|
||||
"Payment",
|
||||
"Customer",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Order struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
OutletID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
CustomerID *uuid.UUID
|
||||
OrderNumber string
|
||||
TableNumber *string
|
||||
OrderType constants.OrderType
|
||||
Status constants.OrderStatus
|
||||
Subtotal float64
|
||||
TaxAmount float64
|
||||
DiscountAmount float64
|
||||
TotalAmount float64
|
||||
TotalCost float64
|
||||
PaymentStatus constants.PaymentStatus
|
||||
RefundAmount float64
|
||||
IsVoid bool
|
||||
IsRefund bool
|
||||
VoidReason *string
|
||||
VoidedAt *time.Time
|
||||
VoidedBy *uuid.UUID
|
||||
RefundReason *string
|
||||
RefundedAt *time.Time
|
||||
RefundedBy *uuid.UUID
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type OrderItem struct {
|
||||
ID uuid.UUID
|
||||
OrderID uuid.UUID
|
||||
ProductID uuid.UUID
|
||||
ProductVariantID *uuid.UUID
|
||||
Quantity int
|
||||
UnitPrice float64
|
||||
TotalPrice float64
|
||||
UnitCost float64
|
||||
TotalCost float64
|
||||
RefundAmount float64
|
||||
RefundQuantity int
|
||||
IsPartiallyRefunded bool
|
||||
IsFullyRefunded bool
|
||||
RefundReason *string
|
||||
RefundedAt *time.Time
|
||||
RefundedBy *uuid.UUID
|
||||
Modifiers []map[string]interface{}
|
||||
Status constants.OrderItemStatus
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type PaymentOrderItem struct {
|
||||
ID uuid.UUID
|
||||
PaymentID uuid.UUID
|
||||
OrderItemID uuid.UUID
|
||||
Amount float64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type OrderSequence struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
OutletID uuid.UUID
|
||||
Year int
|
||||
Month int
|
||||
SequenceNumber int
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateOrderRequest struct {
|
||||
OutletID uuid.UUID `validate:"required"`
|
||||
UserID uuid.UUID `validate:"required"`
|
||||
CustomerID *uuid.UUID `validate:"omitempty"`
|
||||
TableNumber *string `validate:"omitempty,max=50"`
|
||||
OrderType constants.OrderType `validate:"required"`
|
||||
OrderItems []CreateOrderItemRequest `validate:"required,min=1,dive"`
|
||||
Notes *string `validate:"omitempty,max=1000"`
|
||||
Metadata map[string]interface{}
|
||||
CustomerName *string `validate:"omitempty,max=255"`
|
||||
}
|
||||
|
||||
type CreateOrderItemRequest struct {
|
||||
ProductID uuid.UUID `validate:"required"`
|
||||
ProductVariantID *uuid.UUID `validate:"omitempty"`
|
||||
Quantity int `validate:"required,min=1"`
|
||||
UnitPrice *float64 `validate:"omitempty,min=0"` // Optional, will use database price if not provided
|
||||
Modifiers []map[string]interface{} `validate:"omitempty"`
|
||||
Notes *string `validate:"omitempty,max=500"`
|
||||
Metadata map[string]interface{} `validate:"omitempty"`
|
||||
}
|
||||
|
||||
type UpdateOrderRequest struct {
|
||||
TableNumber *string `validate:"omitempty,max=50"`
|
||||
Status *constants.OrderStatus `validate:"omitempty"`
|
||||
DiscountAmount *float64 `validate:"omitempty,min=0"`
|
||||
Notes *string `validate:"omitempty,max=1000"`
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
type CreatePaymentOrderItemRequest struct {
|
||||
OrderItemID uuid.UUID `validate:"required"`
|
||||
Amount float64 `validate:"required,min=0"`
|
||||
}
|
||||
|
||||
type VoidOrderRequest struct {
|
||||
OrderID uuid.UUID `validate:"required"`
|
||||
Reason string `validate:"required"`
|
||||
Type string `validate:"required,oneof=ALL ITEM"`
|
||||
Items []VoidItemRequest `validate:"required_if=Type ITEM,dive"`
|
||||
}
|
||||
|
||||
type VoidItemRequest struct {
|
||||
OrderItemID uuid.UUID `validate:"required"`
|
||||
Quantity int `validate:"required,min=1"`
|
||||
}
|
||||
|
||||
type RefundOrderRequest struct {
|
||||
Reason *string `validate:"omitempty,max=255"`
|
||||
RefundAmount *float64 `validate:"omitempty,min=0"`
|
||||
OrderItems []RefundOrderItemRequest `validate:"omitempty,dive"`
|
||||
}
|
||||
|
||||
type RefundOrderItemRequest struct {
|
||||
OrderItemID uuid.UUID `validate:"required"`
|
||||
RefundQuantity int `validate:"omitempty,min=1"`
|
||||
RefundAmount *float64 `validate:"omitempty,min=0"`
|
||||
Reason *string `validate:"omitempty,max=255"`
|
||||
}
|
||||
|
||||
// Response DTOs
|
||||
type OrderResponse struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
OutletID uuid.UUID
|
||||
UserID uuid.UUID
|
||||
CustomerID *uuid.UUID
|
||||
OrderNumber string
|
||||
TableNumber *string
|
||||
OrderType constants.OrderType
|
||||
Status constants.OrderStatus
|
||||
Subtotal float64
|
||||
TaxAmount float64
|
||||
DiscountAmount float64
|
||||
TotalAmount float64
|
||||
TotalCost float64
|
||||
PaymentStatus constants.PaymentStatus
|
||||
RefundAmount float64
|
||||
IsVoid bool
|
||||
IsRefund bool
|
||||
VoidReason *string
|
||||
VoidedAt *time.Time
|
||||
VoidedBy *uuid.UUID
|
||||
RefundReason *string
|
||||
RefundedAt *time.Time
|
||||
RefundedBy *uuid.UUID
|
||||
Notes *string
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
OrderItems []OrderItemResponse
|
||||
Payments []PaymentResponse
|
||||
}
|
||||
|
||||
type OrderItemResponse struct {
|
||||
ID uuid.UUID
|
||||
OrderID uuid.UUID
|
||||
ProductID uuid.UUID
|
||||
ProductName string
|
||||
ProductVariantID *uuid.UUID
|
||||
ProductVariantName *string
|
||||
Quantity int
|
||||
UnitPrice float64
|
||||
TotalPrice float64
|
||||
UnitCost float64
|
||||
TotalCost float64
|
||||
RefundAmount float64
|
||||
RefundQuantity int
|
||||
IsPartiallyRefunded bool
|
||||
IsFullyRefunded bool
|
||||
RefundReason *string
|
||||
RefundedAt *time.Time
|
||||
RefundedBy *uuid.UUID
|
||||
Modifiers []map[string]interface{}
|
||||
Notes *string
|
||||
Metadata map[string]interface{}
|
||||
Status constants.OrderItemStatus
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type PaymentOrderItemResponse struct {
|
||||
ID uuid.UUID
|
||||
PaymentID uuid.UUID
|
||||
OrderItemID uuid.UUID
|
||||
Amount float64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type ListOrdersRequest struct {
|
||||
OrganizationID *uuid.UUID
|
||||
OutletID *uuid.UUID
|
||||
UserID *uuid.UUID
|
||||
CustomerID *uuid.UUID
|
||||
OrderType *constants.OrderType
|
||||
Status *constants.OrderStatus
|
||||
PaymentStatus *constants.PaymentStatus
|
||||
IsVoid *bool
|
||||
IsRefund *bool
|
||||
DateFrom *time.Time
|
||||
DateTo *time.Time
|
||||
Search string
|
||||
Page int `validate:"required,min=1"`
|
||||
Limit int `validate:"required,min=1,max=100"`
|
||||
}
|
||||
|
||||
type ListOrdersResponse struct {
|
||||
Orders []OrderResponse
|
||||
TotalCount int
|
||||
Page int
|
||||
Limit int
|
||||
TotalPages int
|
||||
}
|
||||
|
||||
func (o *Order) CanBeModified() bool {
|
||||
return o.Status == constants.OrderStatusPending || o.Status == constants.OrderStatusPreparing
|
||||
}
|
||||
|
||||
func (o *Order) CanBeCancelled() bool {
|
||||
return o.Status == constants.OrderStatusPending || o.Status == constants.OrderStatusPreparing
|
||||
}
|
||||
|
||||
func (o *Order) CalculateTotalAmount(taxRate float64) float64 {
|
||||
taxAmount := o.Subtotal * taxRate
|
||||
return o.Subtotal + taxAmount - o.DiscountAmount
|
||||
}
|
||||
|
||||
func (o *Order) IsComplete() bool {
|
||||
return o.Status == constants.OrderStatusCompleted
|
||||
}
|
||||
|
||||
func (o *Order) IsPaid() bool {
|
||||
return o.Status == constants.OrderStatusPaid
|
||||
}
|
||||
|
||||
func (r *CreateOrderRequest) Validate() bool {
|
||||
return r.OrderType.IsValidOrderType()
|
||||
}
|
||||
|
||||
// AddToOrderRequest represents the request to add items to an existing order
|
||||
type AddToOrderRequest struct {
|
||||
OrderItems []CreateOrderItemRequest `validate:"required,min=1,dive"`
|
||||
Notes *string `validate:"omitempty,max=1000"`
|
||||
Metadata map[string]interface{} `validate:"omitempty"`
|
||||
}
|
||||
|
||||
// AddToOrderResponse represents the response when adding items to an existing order
|
||||
type AddToOrderResponse struct {
|
||||
OrderID uuid.UUID
|
||||
OrderNumber string
|
||||
AddedItems []OrderItemResponse
|
||||
UpdatedOrder OrderResponse
|
||||
}
|
||||
|
||||
// SetOrderCustomerRequest represents the request to set customer for an order
|
||||
type SetOrderCustomerRequest struct {
|
||||
CustomerID uuid.UUID `validate:"required"`
|
||||
}
|
||||
|
||||
// SetOrderCustomerResponse represents the response when setting customer for an order
|
||||
type SetOrderCustomerResponse struct {
|
||||
OrderID uuid.UUID
|
||||
CustomerID uuid.UUID
|
||||
Message string
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Organization struct {
|
||||
ID uuid.UUID
|
||||
Name string
|
||||
Email *string
|
||||
PhoneNumber *string
|
||||
PlanType constants.PlanType
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// CreateOrganizationRequest contains both organization and admin user attributes
|
||||
type CreateOrganizationRequest struct {
|
||||
// Organization attributes
|
||||
OrganizationName string `validate:"required,min=1,max=255"`
|
||||
OrganizationEmail *string `validate:"omitempty,email"`
|
||||
OrganizationPhoneNumber *string `validate:"omitempty"`
|
||||
PlanType constants.PlanType `validate:"required"`
|
||||
|
||||
// Admin user attributes
|
||||
AdminName string `validate:"required,min=1,max=255"`
|
||||
AdminEmail string `validate:"required,email"`
|
||||
AdminPassword string `validate:"required,min=6"`
|
||||
|
||||
// Default outlet attributes
|
||||
OutletName string `validate:"required,min=1,max=255"`
|
||||
OutletAddress *string
|
||||
OutletTimezone *string
|
||||
OutletCurrency string `validate:"required,len=3"`
|
||||
}
|
||||
|
||||
type UpdateOrganizationRequest struct {
|
||||
Name *string `validate:"omitempty,min=1,max=255"`
|
||||
Email *string `validate:"omitempty,email"`
|
||||
PhoneNumber *string `validate:"omitempty"`
|
||||
PlanType *constants.PlanType
|
||||
}
|
||||
|
||||
type OrganizationResponse struct {
|
||||
ID uuid.UUID
|
||||
Name string
|
||||
Email *string
|
||||
PhoneNumber *string
|
||||
PlanType constants.PlanType
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// CreateOrganizationResponse contains the created organization, admin user, and default outlet
|
||||
type CreateOrganizationResponse struct {
|
||||
Organization *OrganizationResponse `json:"organization"`
|
||||
AdminUser *UserResponse `json:"admin_user"`
|
||||
DefaultOutlet *OutletResponse `json:"default_outlet"`
|
||||
}
|
||||
|
||||
// Outlet response for the creation response
|
||||
type OutletResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OrganizationID uuid.UUID `json:"organization_id"`
|
||||
Name string `json:"name"`
|
||||
Address *string `json:"address"`
|
||||
Timezone *string `json:"timezone"`
|
||||
Currency string `json:"currency"`
|
||||
TaxRate float64 `json:"tax_rate"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Outlet struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
Name string
|
||||
Address string
|
||||
PhoneNumber *string
|
||||
BusinessType constants.BusinessType
|
||||
Currency constants.Currency
|
||||
TaxRate float64
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateOutletRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
Name string `validate:"required,min=1,max=255"`
|
||||
Address string `validate:"required,min=1,max=500"`
|
||||
PhoneNumber *string `validate:"omitempty,e164"`
|
||||
BusinessType constants.BusinessType `validate:"required"`
|
||||
Currency constants.Currency `validate:"required"`
|
||||
TaxRate float64 `validate:"min=0,max=1"`
|
||||
}
|
||||
|
||||
type UpdateOutletRequest struct {
|
||||
OrganizationID uuid.UUID
|
||||
Name *string `validate:"omitempty,min=1,max=255"`
|
||||
Address *string `validate:"omitempty,min=1,max=500"`
|
||||
PhoneNumber *string `validate:"omitempty,e164"`
|
||||
TaxRate *float64 `validate:"omitempty,min=0,max=1"`
|
||||
IsActive *bool
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type OutletSetting struct {
|
||||
ID uuid.UUID
|
||||
OutletID uuid.UUID
|
||||
Key string
|
||||
Value string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateOutletSettingRequest struct {
|
||||
OutletID uuid.UUID `validate:"required"`
|
||||
Key string `validate:"required,min=1,max=255"`
|
||||
Value string `validate:"required"`
|
||||
}
|
||||
|
||||
type UpdateOutletSettingRequest struct {
|
||||
Value string `validate:"required"`
|
||||
}
|
||||
|
||||
type OutletSettingResponse struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OutletID uuid.UUID `json:"outlet_id"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type OutletPrinterSettings struct {
|
||||
OutletName string `json:"outlet_name"`
|
||||
Address string `json:"address"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
PaperSize string `json:"paper_size"`
|
||||
Footer string `json:"footer"`
|
||||
FooterHashtag string `json:"footer_hashtag"`
|
||||
}
|
||||
|
||||
type UpdateOutletPrinterSettingsRequest struct {
|
||||
OutletName *string `json:"outlet_name,omitempty" validate:"omitempty,min=1,max=255"`
|
||||
Address *string `json:"address,omitempty" validate:"omitempty,min=1,max=500"`
|
||||
PhoneNumber *string `json:"phone_number,omitempty" validate:"omitempty,e164"`
|
||||
PaperSize *string `json:"paper_size,omitempty" validate:"omitempty,oneof=58mm 80mm A4 A5 Letter"`
|
||||
Footer *string `json:"footer,omitempty" validate:"omitempty,min=1,max=500"`
|
||||
FooterHashtag *string `json:"footer_hashtag,omitempty" validate:"omitempty,min=1,max=100"`
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Payment struct {
|
||||
ID uuid.UUID
|
||||
OrderID uuid.UUID
|
||||
PaymentMethodID uuid.UUID
|
||||
Amount float64
|
||||
Status constants.PaymentTransactionStatus
|
||||
TransactionID *string
|
||||
SplitNumber int
|
||||
SplitTotal int
|
||||
SplitDescription *string
|
||||
RefundAmount float64
|
||||
RefundReason *string
|
||||
RefundedAt *time.Time
|
||||
RefundedBy *uuid.UUID
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreatePaymentRequest struct {
|
||||
OrderID uuid.UUID `validate:"required"`
|
||||
PaymentMethodID uuid.UUID `validate:"required"`
|
||||
Amount float64 `validate:"required,min=0"`
|
||||
TransactionID *string `validate:"omitempty"`
|
||||
SplitNumber int `validate:"omitempty,min=1"`
|
||||
SplitTotal int `validate:"omitempty,min=1"`
|
||||
SplitDescription *string `validate:"omitempty,max=255"`
|
||||
PaymentOrderItems []CreatePaymentOrderItemRequest `validate:"omitempty,dive"`
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
type UpdatePaymentRequest struct {
|
||||
Status *constants.PaymentTransactionStatus
|
||||
TransactionID *string
|
||||
Notes *string `validate:"omitempty,max=1000"`
|
||||
}
|
||||
|
||||
type PaymentResponse struct {
|
||||
ID uuid.UUID
|
||||
OrderID uuid.UUID
|
||||
PaymentMethodID uuid.UUID
|
||||
Amount float64
|
||||
Status constants.PaymentTransactionStatus
|
||||
TransactionID *string
|
||||
SplitNumber int
|
||||
SplitTotal int
|
||||
SplitDescription *string
|
||||
RefundAmount float64
|
||||
RefundReason *string
|
||||
RefundedAt *time.Time
|
||||
RefundedBy *uuid.UUID
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
PaymentOrderItems []PaymentOrderItemResponse
|
||||
}
|
||||
|
||||
func (p *Payment) IsSuccessful() bool {
|
||||
return p.Status == constants.PaymentTransactionStatusCompleted
|
||||
}
|
||||
|
||||
func (p *Payment) IsPending() bool {
|
||||
return p.Status == constants.PaymentTransactionStatusPending
|
||||
}
|
||||
|
||||
func (p *Payment) CanBeRefunded() bool {
|
||||
return p.Status == constants.PaymentTransactionStatusCompleted
|
||||
}
|
||||
|
||||
func (p *Payment) IsProcessed() bool {
|
||||
return p.Status == constants.PaymentTransactionStatusCompleted || p.Status == constants.PaymentTransactionStatusFailed
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type PaymentMethod struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
Name string
|
||||
Type constants.PaymentMethodType
|
||||
Processor *string
|
||||
Configuration map[string]interface{}
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreatePaymentMethodRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
Name string `validate:"required,min=1,max=100"`
|
||||
Type constants.PaymentMethodType `validate:"required"`
|
||||
Processor *string `validate:"omitempty,max=100"`
|
||||
Configuration map[string]interface{} `validate:"omitempty"`
|
||||
IsActive *bool `validate:"omitempty"`
|
||||
}
|
||||
|
||||
type UpdatePaymentMethodRequest struct {
|
||||
Name *string `validate:"omitempty,min=1,max=100"`
|
||||
Type *constants.PaymentMethodType `validate:"omitempty"`
|
||||
Processor *string `validate:"omitempty,max=100"`
|
||||
Configuration map[string]interface{} `validate:"omitempty"`
|
||||
IsActive *bool `validate:"omitempty"`
|
||||
}
|
||||
|
||||
type PaymentMethodResponse struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
Name string
|
||||
Type constants.PaymentMethodType
|
||||
Processor *string
|
||||
Configuration map[string]interface{}
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type ListPaymentMethodsRequest struct {
|
||||
OrganizationID *uuid.UUID
|
||||
Type *constants.PaymentMethodType
|
||||
IsActive *bool
|
||||
Search string
|
||||
Page int `validate:"min=1"`
|
||||
Limit int `validate:"min=1,max=100"`
|
||||
}
|
||||
|
||||
type ListPaymentMethodsResponse struct {
|
||||
PaymentMethods []PaymentMethodResponse
|
||||
TotalCount int
|
||||
Page int
|
||||
Limit int
|
||||
TotalPages int
|
||||
}
|
||||
|
||||
func (pm *PaymentMethod) IsDigital() bool {
|
||||
return pm.Type == constants.PaymentMethodTypeCard ||
|
||||
pm.Type == constants.PaymentMethodTypeDigitalWallet ||
|
||||
pm.Type == constants.PaymentMethodTypeEDC ||
|
||||
pm.Type == constants.PaymentMethodTypeQR
|
||||
}
|
||||
|
||||
func (pm *PaymentMethod) RequiresProcessor() bool {
|
||||
return pm.IsDigital() && pm.Processor != nil
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Product struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
CategoryID uuid.UUID
|
||||
SKU *string
|
||||
Name string
|
||||
Description *string
|
||||
Price float64
|
||||
Cost float64
|
||||
BusinessType constants.BusinessType
|
||||
Metadata map[string]interface{}
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type ProductVariant struct {
|
||||
ID uuid.UUID
|
||||
ProductID uuid.UUID
|
||||
Name string
|
||||
PriceModifier float64
|
||||
Cost float64
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateProductRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
CategoryID uuid.UUID `validate:"required"`
|
||||
SKU *string `validate:"omitempty,max=100"`
|
||||
Name string `validate:"required,min=1,max=255"`
|
||||
Description *string `validate:"omitempty,max=1000"`
|
||||
Price float64 `validate:"required,min=0"`
|
||||
Cost float64 `validate:"min=0"`
|
||||
BusinessType constants.BusinessType `validate:"required"`
|
||||
Metadata map[string]interface{}
|
||||
Variants []CreateProductVariantRequest `validate:"omitempty,dive"`
|
||||
// Stock management fields
|
||||
InitialStock *int `validate:"omitempty,min=0"` // Initial stock quantity for all outlets
|
||||
ReorderLevel *int `validate:"omitempty,min=0"` // Reorder level for all outlets
|
||||
CreateInventory bool `validate:"omitempty"` // Whether to create inventory records for all outlets
|
||||
}
|
||||
|
||||
type UpdateProductRequest struct {
|
||||
CategoryID *uuid.UUID
|
||||
SKU *string `validate:"omitempty,max=100"`
|
||||
Name *string `validate:"omitempty,min=1,max=255"`
|
||||
Description *string `validate:"omitempty,max=1000"`
|
||||
Price *float64 `validate:"omitempty,min=0"`
|
||||
Cost *float64 `validate:"omitempty,min=0"`
|
||||
Metadata map[string]interface{}
|
||||
IsActive *bool
|
||||
// Stock management fields
|
||||
ReorderLevel *int `validate:"omitempty,min=0"` // Update reorder level for all existing inventory records
|
||||
}
|
||||
|
||||
type CreateProductVariantRequest struct {
|
||||
ProductID uuid.UUID `validate:"required"`
|
||||
Name string `validate:"required,min=1,max=255"`
|
||||
PriceModifier float64 `validate:"required"`
|
||||
Cost float64 `validate:"min=0"`
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
type UpdateProductVariantRequest struct {
|
||||
Name *string `validate:"omitempty,min=1,max=255"`
|
||||
PriceModifier *float64
|
||||
Cost *float64 `validate:"omitempty,min=0"`
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
type ProductResponse struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
CategoryID uuid.UUID
|
||||
SKU *string
|
||||
Name string
|
||||
Description *string
|
||||
Price float64
|
||||
Cost float64
|
||||
BusinessType constants.BusinessType
|
||||
Metadata map[string]interface{}
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Variants []ProductVariantResponse
|
||||
}
|
||||
|
||||
type ProductVariantResponse struct {
|
||||
ID uuid.UUID
|
||||
ProductID uuid.UUID
|
||||
Name string
|
||||
PriceModifier float64
|
||||
Cost float64
|
||||
Metadata map[string]interface{}
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (p *Product) GetFinalPrice(variant *ProductVariant) float64 {
|
||||
if variant == nil {
|
||||
return p.Price
|
||||
}
|
||||
return p.Price + variant.PriceModifier
|
||||
}
|
||||
|
||||
func (p *Product) GetProfitMargin() float64 {
|
||||
if p.Price == 0 {
|
||||
return 0
|
||||
}
|
||||
return ((p.Price - p.Cost) / p.Price) * 100
|
||||
}
|
||||
|
||||
func (p *Product) IsValidPrice() bool {
|
||||
return p.Price > 0 && p.Cost >= 0 && p.Price > p.Cost
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"apskel-pos-be/internal/constants"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
OutletID *uuid.UUID
|
||||
Name string
|
||||
Email string
|
||||
Role constants.UserRole
|
||||
Permissions map[string]interface{}
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
type CreateUserRequest struct {
|
||||
OrganizationID uuid.UUID `validate:"required"`
|
||||
OutletID *uuid.UUID
|
||||
Name string `validate:"required,min=1,max=255"`
|
||||
Email string `validate:"required,email"`
|
||||
Password string `validate:"required,min=6"`
|
||||
Role constants.UserRole `validate:"required"`
|
||||
Permissions map[string]interface{} `validate:"omitempty"`
|
||||
}
|
||||
|
||||
type UpdateUserRequest struct {
|
||||
Name *string `validate:"omitempty,min=1,max=255"`
|
||||
Email *string `validate:"omitempty,email"`
|
||||
Role *constants.UserRole
|
||||
OutletID *uuid.UUID
|
||||
IsActive *bool
|
||||
Permissions *map[string]interface{}
|
||||
}
|
||||
|
||||
type ChangePasswordRequest struct {
|
||||
CurrentPassword string `validate:"required"`
|
||||
NewPassword string `validate:"required,min=6"`
|
||||
}
|
||||
|
||||
type UserResponse struct {
|
||||
ID uuid.UUID
|
||||
OrganizationID uuid.UUID
|
||||
OutletID *uuid.UUID
|
||||
Name string
|
||||
Email string
|
||||
Role constants.UserRole
|
||||
Permissions map[string]interface{}
|
||||
IsActive bool
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
func (u *User) HasPermission(requiredRole constants.UserRole) bool {
|
||||
roleHierarchy := map[constants.UserRole]int{
|
||||
constants.RoleWaiter: 1,
|
||||
constants.RoleCashier: 2,
|
||||
constants.RoleManager: 3,
|
||||
constants.RoleAdmin: 4,
|
||||
}
|
||||
|
||||
userLevel := roleHierarchy[u.Role]
|
||||
requiredLevel := roleHierarchy[requiredRole]
|
||||
|
||||
return userLevel >= requiredLevel
|
||||
}
|
||||
|
||||
func (u *User) CanAccessOutlet(outletID uuid.UUID) bool {
|
||||
if u.Role == constants.RoleAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
return u.OutletID != nil && *u.OutletID == outletID
|
||||
}
|
||||
|
||||
func (u *User) IsManager() bool {
|
||||
return u.HasPermission(constants.RoleManager)
|
||||
}
|
||||
|
||||
func (u *User) IsAdmin() bool {
|
||||
return u.Role == constants.RoleAdmin
|
||||
}
|
||||
Reference in New Issue
Block a user