This commit is contained in:
aditya.siregar
2025-07-18 20:10:29 +07:00
parent 1bceae010b
commit 4f5950543e
511 changed files with 24132 additions and 34137 deletions
+214
View File
@@ -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"`
}
+47
View File
@@ -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
}
+53
View File
@@ -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"`
}
+102
View File
@@ -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 ""
}
+59
View File
@@ -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
}
+32
View File
@@ -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",
}
}
+288
View File
@@ -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
}
+76
View File
@@ -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"`
}
+41
View File
@@ -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
}
+53
View File
@@ -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"`
}
+81
View File
@@ -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
}
+77
View File
@@ -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
}
+126
View File
@@ -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
}
+88
View File
@@ -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
}