package service import ( "crypto/sha256" "encoding/base64" "errors" "fmt" "golang.org/x/crypto/pbkdf2" "log" "rrbec_server/internal/models" "rrbec_server/internal/repository" "strings" "time" ) type Service struct { repo *repository.Repository } func NewService(repo *repository.Repository) *Service { return &Service{repo: repo} } // Comanda Logic func (s *Service) CreateComanda(comanda *models.Comanda) error { comanda.DtOpen = time.Now() comanda.Status = "OPEN" return s.repo.CreateComanda(comanda) } // Item Addition and Stock Logic func (s *Service) AddItemToComanda(comandaID, productID uint, applicant string) (*models.ProductComanda, error) { comanda, err := s.repo.GetComandaByID(comandaID) if err != nil { return nil, err } if comanda.Status != "OPEN" { return nil, errors.New("cannot add items to a closed or fiado comanda") } product, err := s.repo.GetProductByID(productID) if err != nil { return nil, errors.New("product not found") } if product.Quantity <= 0 { return nil, errors.New("product out of stock") } item := &models.ProductComanda{ ComandaID: comandaID, ProductID: productID, ProductName: product.Name, ProductPrice: product.Price, DateTime: time.Now(), Applicant: applicant, } err = s.repo.AddItemToComanda(item) if err != nil { return nil, err } if err := s.deductProductStock(productID, 1); err != nil { log.Printf("Warning: failed to deduct stock for product %d: %v", productID, err) } if product.Cuisine { order := &models.Order{ ProductComandaID: &item.ID, ProductID: productID, ComandaID: comandaID, Queue: time.Now(), } s.repo.CreateOrder(order) } return item, nil } func (s *Service) deductProductStock(productID uint, quantity int) error { components, err := s.repo.GetProductComponents(productID) if err != nil { return err } if len(components) > 0 { for _, comp := range components { if err := s.repo.DeductStock(comp.ComponentProductID, int(comp.QuantityRequired)); err != nil { return err } } return nil } return s.repo.DeductStock(productID, quantity) } func (s *Service) AddItemToComandaRaw(item *models.ProductComanda) error { comanda, err := s.repo.GetComandaByID(item.ComandaID) if err != nil { return err } if comanda.Status != "OPEN" { return errors.New("cannot add items to a closed or fiado comanda") } product, err := s.repo.GetProductByID(item.ProductID) if err != nil { return errors.New("product not found") } item.ProductName = product.Name item.ProductPrice = product.Price item.DateTime = time.Now() return s.repo.AddItemToComanda(item) } // Sync Logic Placeholder func (s *Service) SyncWithCloud() error { // Task for the background worker return nil } func (s *Service) GetProducts() ([]models.Product, error) { return s.repo.GetProducts() } func (s *Service) CreateProduct(product *models.Product) error { return s.repo.CreateProduct(product) } func (s *Service) UpdateProduct(id uint, updates map[string]interface{}) error { return s.repo.UpdateProductFields(id, updates) } func (s *Service) GetMesas() ([]models.Mesa, error) { return s.repo.GetMesas() } func (s *Service) GetComandas() ([]models.Comanda, error) { return s.repo.GetComandas() } func (s *Service) GetComandaByID(id uint) (*models.Comanda, error) { return s.repo.GetComandaByID(id) } func (s *Service) GetCategories() ([]models.Category, error) { return s.repo.GetCategories() } func (s *Service) CreateCategory(cat *models.Category) error { return s.repo.CreateCategory(cat) } func (s *Service) UpdateCategory(id uint, updates map[string]interface{}) error { return s.repo.UpdateCategoryFields(id, updates) } func (s *Service) GetTypePayments() ([]models.TypePay, error) { return s.repo.GetTypePayments() } func (s *Service) GetClients() ([]models.Client, error) { return s.repo.GetClients() } func (s *Service) GetOrders() ([]models.Order, error) { return s.repo.GetOrders() } func (s *Service) CreateOrder(order *models.Order) error { order.Queue = time.Now() return s.repo.CreateOrder(order) } func (s *Service) UpdateOrder(id uint, updates map[string]interface{}) error { return s.repo.UpdateOrderFields(id, updates) } func (s *Service) SetOrderPreparing(id uint) error { now := time.Now() return s.repo.UpdateOrderFields(id, map[string]interface{}{"preparing": &now}) } func (s *Service) SetOrderFinished(id uint) error { now := time.Now() return s.repo.UpdateOrderFields(id, map[string]interface{}{"finished": &now}) } func (s *Service) SetOrderDelivered(id uint) error { now := time.Now() return s.repo.UpdateOrderFields(id, map[string]interface{}{"delivered": &now}) } func (s *Service) SetOrderCanceled(id uint) error { now := time.Now() return s.repo.UpdateOrderFields(id, map[string]interface{}{"canceled": &now}) } func (s *Service) GetPayments() ([]models.Payment, error) { return s.repo.GetPayments() } func (s *Service) DeleteItem(itemID uint) error { // 1. Check if there's an associated Order (Kitchen) order, err := s.repo.GetOrderByPC(itemID) if err == nil && order != nil { // 2. Mark as Canceled now := time.Now() order.Canceled = &now s.repo.UpdateOrder(order) } // 3. Delete the item return s.repo.DeleteItem(itemID) } func (s *Service) ClearComanda(id uint) error { if err := s.repo.ClearComandaItems(id); err != nil { return err } return s.repo.UpdateComandaStatus(id, "CLOSED") } func (s *Service) PagarComanda(id uint, payment *models.Payment, status string) error { payment.ComandaID = id payment.DateTime = time.Now() if err := s.repo.CreatePayment(payment); err != nil { return err } if status == "" { status = "CLOSED" } return s.repo.UpdateComandaStatus(id, status) } func (s *Service) UpdateComanda(id uint, updates map[string]interface{}) error { return s.repo.UpdateComandaFields(id, updates) } // User Auth func (s *Service) Login(username, password string) (*models.User, error) { user, err := s.repo.GetUserByUsername(username) if err != nil { return nil, errors.New("invalid credentials") } if s.CheckDjangoPassword(password, user.Password) { return user, nil } return nil, errors.New("invalid credentials") } func (s *Service) CheckDjangoPassword(password, hash string) bool { parts := strings.Split(hash, "$") if len(parts) != 4 { return false } algorithm := parts[0] if algorithm != "pbkdf2_sha256" { return false } var iterations int fmt.Sscanf(parts[1], "%d", &iterations) salt := parts[2] djangoHash := parts[3] // PBKDF2 with SHA256 dk := pbkdf2.Key([]byte(password), []byte(salt), iterations, 32, sha256.New) encoded := base64.StdEncoding.EncodeToString(dk) return encoded == djangoHash }