Documentation
¶
Overview ¶
Package service contains the business logic for okpos. Service methods depend only on data.*Repoer interfaces and domain types; they never import database-specific packages (pgx, sqlite, etc.).
Index ¶
- func AvailableQuantity(ctx context.Context, productRepo data.ProductRepoer, inv data.InventoryQuerier, ...) (float64, error)
- func ComputeDiscountAmount(d *model.Discount, basisCents int64) int64
- type AuthService
- func (s *AuthService) HasAnyOwner(ctx context.Context) (bool, error)
- func (s *AuthService) Login(ctx context.Context, email, password string) (*auth.JWTTokens, error)
- func (s *AuthService) Logout(ctx context.Context, userID uuid.UUID) error
- func (s *AuthService) RefreshTokens(ctx context.Context, refreshToken string) (*auth.JWTTokens, error)
- func (s *AuthService) Register(ctx context.Context, req *dto.RegisterDTO) (*model.User, *auth.JWTTokens, error)
- type BranchService
- func (s *BranchService) Create(ctx context.Context, req *dto.CreateBranchDTO) (*model.Branch, error)
- func (s *BranchService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *BranchService) GetByID(ctx context.Context, id uuid.UUID) (*model.Branch, error)
- func (s *BranchService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.Branch, error)
- func (s *BranchService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Branch], error)
- func (s *BranchService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdateBranchDTO) (*model.Branch, error)
- type CompanyService
- func (s *CompanyService) Create(ctx context.Context, ownerID uuid.UUID, req *dto.CreateCompanyDTO) (*model.Company, error)
- func (s *CompanyService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *CompanyService) GetByID(ctx context.Context, id uuid.UUID) (*model.Company, error)
- func (s *CompanyService) GetByIDForOwner(ctx context.Context, id, ownerID uuid.UUID) (*model.Company, error)
- func (s *CompanyService) ListByOwner(ctx context.Context, ownerID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Company], error)
- func (s *CompanyService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdateCompanyDTO) (*model.Company, error)
- type CustomerService
- func (s *CustomerService) Create(ctx context.Context, req *dto.CreateCustomerDTO) (*model.Customer, error)
- func (s *CustomerService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *CustomerService) FindOrCreate(ctx context.Context, companyID uuid.UUID, req *dto.FindOrCreateCustomerDTO) (*model.Customer, error)
- func (s *CustomerService) GetByID(ctx context.Context, id uuid.UUID) (*model.Customer, error)
- func (s *CustomerService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.Customer, error)
- func (s *CustomerService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Customer], error)
- func (s *CustomerService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdateCustomerDTO) (*model.Customer, error)
- type DiscountService
- func (s *DiscountService) Create(ctx context.Context, req *dto.CreateDiscountDTO) (*model.Discount, error)
- func (s *DiscountService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *DiscountService) GetByID(ctx context.Context, id uuid.UUID) (*model.Discount, error)
- func (s *DiscountService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.Discount, error)
- func (s *DiscountService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Discount], error)
- func (s *DiscountService) ListSelectableForOrder(ctx context.Context, companyID uuid.UUID) ([]model.Discount, error)
- func (s *DiscountService) RedemptionCount(ctx context.Context, discountID uuid.UUID) (int, error)
- func (s *DiscountService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdateDiscountDTO) (*model.Discount, error)
- type ExpenseService
- func (s *ExpenseService) GetByID(ctx context.Context, id uuid.UUID) (*model.Expense, error)
- func (s *ExpenseService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.Expense, error)
- func (s *ExpenseService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Expense], error)
- func (s *ExpenseService) Record(ctx context.Context, req *dto.CreateExpenseDTO) (*model.Expense, error)
- type FileService
- func (s *FileService) Delete(ctx context.Context, id, companyID uuid.UUID) error
- func (s *FileService) Open(ctx context.Context, id, companyID uuid.UUID) (*model.File, io.ReadCloser, error)
- func (s *FileService) PresignedURL(ctx context.Context, file *model.File) (string, error)
- func (s *FileService) Upload(ctx context.Context, companyID, uploaderID uuid.UUID, intent data.UploadIntent, ...) (*model.File, string, error)
- type InventoryService
- func (s *InventoryService) CorrectMovement(ctx context.Context, companyID, movementID uuid.UUID, ...) (*model.StockMovement, error)
- func (s *InventoryService) CurrentStock(ctx context.Context, productID uuid.UUID) (float64, error)
- func (s *InventoryService) GetByID(ctx context.Context, id uuid.UUID) (*model.StockMovement, error)
- func (s *InventoryService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.StockMovement, error)
- func (s *InventoryService) IsCorrectable(reason model.MovementReason) bool
- func (s *InventoryService) LPP(ctx context.Context, productID uuid.UUID) (int64, error)
- func (s *InventoryService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.StockMovement], error)
- func (s *InventoryService) ListByProduct(ctx context.Context, productID, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.StockMovement], error)
- func (s *InventoryService) RecordAdjustmentDown(ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO) (*model.StockMovement, error)
- func (s *InventoryService) RecordAdjustmentUp(ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO) (*model.StockMovement, error)
- func (s *InventoryService) RecordMovement(ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO) (*model.StockMovement, error)
- func (s *InventoryService) RecordOpeningBalance(ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO) (*model.StockMovement, error)
- func (s *InventoryService) RecordProcurement(ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO) (*model.StockMovement, error)
- func (s *InventoryService) RecordStockCount(ctx context.Context, companyID uuid.UUID, req *dto.RecordStockCountDTO) (*model.StockMovement, float64, error)
- func (s *InventoryService) RecordWaste(ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO) (*model.StockMovement, error)
- func (s *InventoryService) StockAt(ctx context.Context, productID uuid.UUID, at time.Time) (float64, error)
- type OrderService
- func (s *OrderService) AddItem(ctx context.Context, orderID uuid.UUID, req *dto.AddOrderItemDTO) (*model.OrderItem, error)
- func (s *OrderService) ApplyOrderDiscount(ctx context.Context, orderID uuid.UUID, req *dto.ApplyOrderDiscountDTO) (*model.Order, error)
- func (s *OrderService) AssignCustomer(ctx context.Context, orderID, customerID uuid.UUID) (*model.Order, error)
- func (s *OrderService) Checkout(ctx context.Context, orderID uuid.UUID, userID uuid.UUID, req *dto.CheckoutDTO) (*model.Order, error)
- func (s *OrderService) CreateOrder(ctx context.Context, companyID uuid.UUID, _ uuid.UUID, req *dto.CreateOrderDTO) (*model.Order, error)
- func (s *OrderService) DecrementItem(ctx context.Context, orderID, itemID uuid.UUID) error
- func (s *OrderService) GetOrderByID(ctx context.Context, id uuid.UUID) (*model.Order, error)
- func (s *OrderService) GetOrderByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.Order, error)
- func (s *OrderService) ListItemsByOrder(ctx context.Context, orderID uuid.UUID) ([]model.OrderItem, error)
- func (s *OrderService) ListOrdersByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Order], error)
- func (s *OrderService) ListOrdersByCompanyFiltered(ctx context.Context, companyID uuid.UUID, filter *data.OrderFilter, ...) (*dto.PaginationResults[model.Order], error)
- func (s *OrderService) ListOrdersBySession(ctx context.Context, sessionID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Order], error)
- func (s *OrderService) RemoveCustomer(ctx context.Context, orderID uuid.UUID) (*model.Order, error)
- func (s *OrderService) RemoveItem(ctx context.Context, orderID, itemID uuid.UUID) error
- func (s *OrderService) RemoveOrderDiscount(ctx context.Context, orderID uuid.UUID) (*model.Order, error)
- func (s *OrderService) Return(ctx context.Context, orderID uuid.UUID, returnedBy uuid.UUID, ...) (*model.OrderReturn, error)
- func (s *OrderService) Void(ctx context.Context, orderID uuid.UUID, voidedBy uuid.UUID, ...) (*model.Order, error)
- type POSStationService
- func (s *POSStationService) Create(ctx context.Context, req *dto.CreatePOSStationDTO) (*model.POSStation, error)
- func (s *POSStationService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *POSStationService) GetByID(ctx context.Context, id uuid.UUID) (*model.POSStation, error)
- func (s *POSStationService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.POSStation, error)
- func (s *POSStationService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.POSStation], error)
- func (s *POSStationService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdatePOSStationDTO) (*model.POSStation, error)
- type ProductService
- func (s *ProductService) AvailableUnits(ctx context.Context, recipeID uuid.UUID) (float64, error)
- func (s *ProductService) ComputeCOGS(ctx context.Context, recipeID uuid.UUID) (int64, error)
- func (s *ProductService) Create(ctx context.Context, req *dto.CreateProductDTO) (*model.Product, error)
- func (s *ProductService) CurrentStock(ctx context.Context, id uuid.UUID) (float64, error)
- func (s *ProductService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *ProductService) GetByID(ctx context.Context, id uuid.UUID) (*model.Product, error)
- func (s *ProductService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.Product, error)
- func (s *ProductService) GetBySKU(ctx context.Context, companyID uuid.UUID, sku string) (*model.Product, error)
- func (s *ProductService) GetProductCategories(ctx context.Context, productID uuid.UUID) ([]model.Category, error)
- func (s *ProductService) GetRecipe(ctx context.Context, recipeID uuid.UUID) (*dto.RecipeDTO, error)
- func (s *ProductService) LPP(ctx context.Context, id uuid.UUID) (int64, error)
- func (s *ProductService) ListByCompany(ctx context.Context, companyID uuid.UUID, productType string, ...) (*dto.PaginationResults[model.Product], error)
- func (s *ProductService) ListCategories(ctx context.Context, companyID uuid.UUID) ([]model.Category, error)
- func (s *ProductService) SetRecipe(ctx context.Context, recipeID uuid.UUID, req *dto.SetRecipeDTO) error
- func (s *ProductService) StockAt(ctx context.Context, id uuid.UUID, at time.Time) (float64, error)
- func (s *ProductService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdateProductDTO) (*model.Product, error)
- type ReportsService
- func (s *ReportsService) InventoryReport(ctx context.Context, q *dto.InventoryReportQuery) (*model.InventoryReport, error)
- func (s *ReportsService) PLReport(ctx context.Context, q *dto.PLReportQuery) (*model.PLReport, error)
- func (s *ReportsService) SalesReport(ctx context.Context, q *dto.SalesReportQuery) (*model.SalesReport, error)
- type SessionService
- func (s *SessionService) Close(ctx context.Context, sessionID uuid.UUID, closerID uuid.UUID, ...) (*model.SessionReconciliation, error)
- func (s *SessionService) ForceClose(ctx context.Context, sessionID, companyID, actorID uuid.UUID) (*model.SessionReconciliation, error)
- func (s *SessionService) GetByID(ctx context.Context, id uuid.UUID) (*model.POSSession, error)
- func (s *SessionService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.POSSession, error)
- func (s *SessionService) GetOpenByUser(ctx context.Context, companyID, userID uuid.UUID) (*model.POSSession, error)
- func (s *SessionService) GetReconciliation(ctx context.Context, sessionID uuid.UUID) (*model.SessionReconciliation, error)
- func (s *SessionService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.POSSession], error)
- func (s *SessionService) ListOpenByCompanyFiltered(ctx context.Context, companyID uuid.UUID, filter *data.SessionFilter, ...) (*dto.PaginationResults[model.POSSession], error)
- func (s *SessionService) Open(ctx context.Context, companyID uuid.UUID, userID uuid.UUID, ...) (*model.POSSession, error)
- type SupplierPaymentService
- func (s *SupplierPaymentService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.SupplierPayment], error)
- func (s *SupplierPaymentService) ListBySupplier(ctx context.Context, supplierID, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.SupplierPayment], error)
- func (s *SupplierPaymentService) RecordPayment(ctx context.Context, supplierID uuid.UUID, companyID uuid.UUID, ...) (*model.SupplierPayment, error)
- type SupplierService
- func (s *SupplierService) Create(ctx context.Context, req *dto.CreateSupplierDTO) (*model.Supplier, error)
- func (s *SupplierService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *SupplierService) GetByID(ctx context.Context, id uuid.UUID) (*model.Supplier, error)
- func (s *SupplierService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.Supplier, error)
- func (s *SupplierService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.Supplier], error)
- func (s *SupplierService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdateSupplierDTO) (*model.Supplier, error)
- type UserService
- func (s *UserService) Create(ctx context.Context, req *dto.CreateUserDTO) (*model.User, error)
- func (s *UserService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *UserService) GetByID(ctx context.Context, id uuid.UUID) (*model.User, error)
- func (s *UserService) GetByIDForCompany(ctx context.Context, id, companyID uuid.UUID) (*model.User, error)
- func (s *UserService) List(ctx context.Context, q *dto.PaginationDTO) (*dto.PaginationResults[model.User], error)
- func (s *UserService) ListByCompany(ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO) (*dto.PaginationResults[model.User], error)
- func (s *UserService) Update(ctx context.Context, id uuid.UUID, req *dto.UpdateUserDTO) (*model.User, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AvailableQuantity ¶ added in v0.1.2
func AvailableQuantity( ctx context.Context, productRepo data.ProductRepoer, inv data.InventoryQuerier, product *model.Product, ) (float64, error)
AvailableQuantity returns how many units of product can currently be sold. For simple products this is the direct current stock. For recipe products it's how many complete units can be made from current ingredient stock: floor(min(ingredient_stock / component_quantity)) across recipe components.
func ComputeDiscountAmount ¶
ComputeDiscountAmount returns the discount in cents for the given basis amount. For fixed discounts, returns d.Value directly. For percentage discounts, returns (basisCents * d.Value) / 100 (integer division).
Types ¶
type AuthService ¶
type AuthService struct {
// contains filtered or unexported fields
}
AuthService handles login and token refresh.
func NewAuthService ¶
func NewAuthService(userRepo data.UserRepoer, a *auth.Auth) *AuthService
NewAuthService creates an AuthService.
func (*AuthService) HasAnyOwner ¶ added in v0.1.7
func (s *AuthService) HasAnyOwner(ctx context.Context) (bool, error)
HasAnyOwner reports whether an owner account already exists — used to gate registration when config.SingleOwnerMode is on (cap the system to one owner total).
func (*AuthService) Logout ¶
Logout revokes every access and refresh token already issued to the user by advancing their token_valid_after cutoff (UserRepoer.InvalidateTokens), so a token that was merely discarded client-side (or stolen) can no longer be used to authenticate or refresh. There's no per-device session store, so this revokes all of the user's sessions, not just the caller's.
func (*AuthService) RefreshTokens ¶
func (s *AuthService) RefreshTokens( ctx context.Context, refreshToken string, ) (*auth.JWTTokens, error)
RefreshTokens validates a refresh token and issues a new access+refresh pair.
type BranchService ¶
type BranchService struct {
// contains filtered or unexported fields
}
BranchService handles branch-related business operations.
func NewBranchService ¶
func NewBranchService(repo data.BranchRepoer, companyRepo data.CompanyRepoer) *BranchService
func (*BranchService) Create ¶
func (s *BranchService) Create( ctx context.Context, req *dto.CreateBranchDTO, ) (*model.Branch, error)
Create registers a new branch. Validates that the company exists.
func (*BranchService) GetByIDForCompany ¶
func (s *BranchService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.Branch, error)
GetByIDForCompany fetches a branch and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*BranchService) ListByCompany ¶
func (s *BranchService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Branch], error)
type CompanyService ¶
type CompanyService struct {
// contains filtered or unexported fields
}
CompanyService handles company-related business operations.
func NewCompanyService ¶
func NewCompanyService(repo data.CompanyRepoer) *CompanyService
func (*CompanyService) Create ¶
func (s *CompanyService) Create( ctx context.Context, ownerID uuid.UUID, req *dto.CreateCompanyDTO, ) (*model.Company, error)
Create registers a new company. ownerID is taken from the caller's JWT, not the DTO.
func (*CompanyService) GetByIDForOwner ¶
func (s *CompanyService) GetByIDForOwner( ctx context.Context, id, ownerID uuid.UUID, ) (*model.Company, error)
GetByIDForOwner fetches a company and verifies ownerID actually owns it. Returns data.ErrForbidden if the company exists but belongs to someone else — companies have no CompanyID of their own to compare (they're the tenant root), so ownership is checked via OwnerID instead.
func (*CompanyService) ListByOwner ¶
func (s *CompanyService) ListByOwner( ctx context.Context, ownerID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Company], error)
type CustomerService ¶
type CustomerService struct {
// contains filtered or unexported fields
}
CustomerService handles customer-related business operations.
func NewCustomerService ¶
func NewCustomerService(repo data.CustomerRepoer) *CustomerService
func (*CustomerService) Create ¶
func (s *CustomerService) Create( ctx context.Context, req *dto.CreateCustomerDTO, ) (*model.Customer, error)
func (*CustomerService) FindOrCreate ¶ added in v0.1.2
func (s *CustomerService) FindOrCreate( ctx context.Context, companyID uuid.UUID, req *dto.FindOrCreateCustomerDTO, ) (*model.Customer, error)
FindOrCreate looks up a customer within companyID by email (if provided) then by phone (if provided); if no match is found, it creates a new customer from req.Name/Email/Phone. Name is only required on the create path — a matched lookup returns the existing record as-is.
func (*CustomerService) GetByIDForCompany ¶
func (s *CustomerService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.Customer, error)
GetByIDForCompany fetches a customer and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*CustomerService) ListByCompany ¶
func (s *CustomerService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Customer], error)
type DiscountService ¶
type DiscountService struct {
// contains filtered or unexported fields
}
DiscountService handles CRUD operations on the named discount catalog.
func NewDiscountService ¶
func NewDiscountService(repo data.DiscountRepoer) *DiscountService
NewDiscountService constructs a DiscountService.
func (*DiscountService) Create ¶
func (s *DiscountService) Create( ctx context.Context, req *dto.CreateDiscountDTO, ) (*model.Discount, error)
Create persists a new discount. Validates type and value range.
func (*DiscountService) GetByIDForCompany ¶
func (s *DiscountService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.Discount, error)
GetByIDForCompany fetches a discount and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*DiscountService) ListByCompany ¶
func (s *DiscountService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Discount], error)
ListByCompany returns paginated discounts for a company.
func (*DiscountService) ListSelectableForOrder ¶ added in v0.1.4
func (s *DiscountService) ListSelectableForOrder( ctx context.Context, companyID uuid.UUID, ) ([]model.Discount, error)
ListSelectableForOrder returns discounts a cashier may pick from a company's dropdown at checkout — active, uncoded, and currently within their validity window (if any).
func (*DiscountService) RedemptionCount ¶ added in v0.1.4
RedemptionCount returns how many orders have redeemed a discount.
type ExpenseService ¶
type ExpenseService struct {
// contains filtered or unexported fields
}
ExpenseService handles expense-related business operations.
func NewExpenseService ¶
func NewExpenseService(repo data.ExpenseRepoer, emitter data.EventEmitter) *ExpenseService
func (*ExpenseService) GetByIDForCompany ¶
func (s *ExpenseService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.Expense, error)
GetByIDForCompany fetches an expense and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*ExpenseService) ListByCompany ¶
func (s *ExpenseService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Expense], error)
func (*ExpenseService) Record ¶
func (s *ExpenseService) Record( ctx context.Context, req *dto.CreateExpenseDTO, ) (*model.Expense, error)
type FileService ¶
type FileService struct {
// contains filtered or unexported fields
}
FileService validates and persists uploaded files against the intent registry, delegating byte storage to a data.FileStorage backend and producing presigned download URLs for either backend transparently.
func NewFileService ¶
func NewFileService( repo data.FileRepoer, storage data.FileStorage, a *auth.Auth, fileAccessTTL time.Duration, ) *FileService
NewFileService constructs a FileService.
func (*FileService) Delete ¶
Delete removes both the storage object and the metadata row for a file, scoped to companyID. Storage is deleted first so a failure leaves the DB row (and thus the file) intact for a retry, rather than orphaning bytes with no record pointing at them.
func (*FileService) Open ¶
func (s *FileService) Open( ctx context.Context, id, companyID uuid.UUID, ) (*model.File, io.ReadCloser, error)
Open retrieves a file's metadata and a reader over its bytes, scoped to companyID. Used by the public download handler.
func (*FileService) PresignedURL ¶
PresignedURL returns a time-limited download URL for file, using the storage backend's native presign when available (S3), or a file-access JWT pointing back at this app's own download route otherwise (local FS).
func (*FileService) Upload ¶
func (s *FileService) Upload( ctx context.Context, companyID, uploaderID uuid.UUID, intent data.UploadIntent, declaredMIME string, size int64, r io.Reader, ) (*model.File, string, error)
Upload validates r against intent's registered constraints, stores it, records a files row, and returns the record plus a presigned download URL.
type InventoryService ¶
type InventoryService struct {
// contains filtered or unexported fields
}
InventoryService handles stock movement operations. It also implements data.InventoryQuerier (LPP + CurrentStock) so it can be passed directly to ProductService.
func NewInventoryService ¶
func NewInventoryService( repo data.InventoryRepoer, productRepo data.ProductRepoer, supplierRepo data.SupplierRepoer, emitter data.EventEmitter, ) *InventoryService
func (*InventoryService) CorrectMovement ¶ added in v0.1.2
func (s *InventoryService) CorrectMovement( ctx context.Context, companyID, movementID uuid.UUID, req *dto.RecordStockMovementDTO, ) (*model.StockMovement, error)
CorrectMovement fixes a mistaken entry (e.g. a typo'd quantity or unit cost) without ever mutating the ledger: it records a reversal that exactly cancels the original movement, then a fresh correct movement of the same reason — both in one transaction. req.ProductID and req.Reason are overwritten from the original movement; only Quantity/UnitCost/Notes/ SupplierID are taken from the caller.
Returns ErrForbidden if the movement doesn't belong to companyID, ErrNotCorrectable if the reason isn't user-entered or is itself a correction, and ErrAlreadyCorrected if it has already been reversed.
func (*InventoryService) CurrentStock ¶
CurrentStock returns the signed sum of stock deltas for the product. Returns ErrInsufficientStock if no movements have been recorded yet.
func (*InventoryService) GetByID ¶
func (s *InventoryService) GetByID( ctx context.Context, id uuid.UUID, ) (*model.StockMovement, error)
func (*InventoryService) GetByIDForCompany ¶
func (s *InventoryService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.StockMovement, error)
GetByIDForCompany fetches a stock movement and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*InventoryService) IsCorrectable ¶ added in v0.1.2
func (s *InventoryService) IsCorrectable(reason model.MovementReason) bool
IsCorrectable reports whether a movement of the given reason is eligible for CorrectMovement.
func (*InventoryService) ListByCompany ¶
func (s *InventoryService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.StockMovement], error)
func (*InventoryService) ListByProduct ¶
func (s *InventoryService) ListByProduct( ctx context.Context, productID, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.StockMovement], error)
func (*InventoryService) RecordAdjustmentDown ¶
func (s *InventoryService) RecordAdjustmentDown( ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO, ) (*model.StockMovement, error)
RecordAdjustmentDown records a negative stock adjustment. UnitCost must be nil.
func (*InventoryService) RecordAdjustmentUp ¶
func (s *InventoryService) RecordAdjustmentUp( ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO, ) (*model.StockMovement, error)
RecordAdjustmentUp records a positive stock adjustment. UnitCost must be nil.
func (*InventoryService) RecordMovement ¶
func (s *InventoryService) RecordMovement( ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO, ) (*model.StockMovement, error)
RecordMovement dispatches to the appropriate method based on Reason.
func (*InventoryService) RecordOpeningBalance ¶
func (s *InventoryService) RecordOpeningBalance( ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO, ) (*model.StockMovement, error)
RecordOpeningBalance records an opening balance movement. UnitCost is required.
func (*InventoryService) RecordProcurement ¶
func (s *InventoryService) RecordProcurement( ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO, ) (*model.StockMovement, error)
RecordProcurement records an incoming stock movement of type "procurement". UnitCost is required.
func (*InventoryService) RecordStockCount ¶ added in v0.1.4
func (s *InventoryService) RecordStockCount( ctx context.Context, companyID uuid.UUID, req *dto.RecordStockCountDTO, ) (*model.StockMovement, float64, error)
RecordStockCount reconciles a physical count against current system stock, recording an adjustment_up or adjustment_down for the difference. Returns a nil movement and delta 0 when the count matches current stock (within stockCountEpsilon) — no discrepancy, nothing recorded.
func (*InventoryService) RecordWaste ¶
func (s *InventoryService) RecordWaste( ctx context.Context, companyID uuid.UUID, req *dto.RecordStockMovementDTO, ) (*model.StockMovement, error)
RecordWaste records a waste movement (negative delta). UnitCost must be nil. Resolves LPP to compute valueLost and optionally emits an event.
type OrderService ¶
type OrderService struct {
// contains filtered or unexported fields
}
OrderService handles the order lifecycle: create, add items, checkout, void, return.
func NewOrderService ¶
func NewOrderService( repo data.OrderRepoer, productRepo data.ProductRepoer, sessionRepo data.SessionRepoer, discountRepo data.DiscountRepoer, inventoryRepo data.InventoryRepoer, paymentMethods []model.PaymentMethod, cashDrawerMethods []model.PaymentMethod, emitter data.EventEmitter, ) *OrderService
NewOrderService constructs an OrderService. Pass nil for paymentMethods to use model.DefaultPaymentMethods. Pass nil for cashDrawerMethods to use model.DefaultCashDrawerMethods. Pass nil for inventoryRepo to skip stock-availability checks on add-to-cart and checkout (e.g. in tests that don't exercise inventory).
func (*OrderService) AddItem ¶
func (s *OrderService) AddItem( ctx context.Context, orderID uuid.UUID, req *dto.AddOrderItemDTO, ) (*model.OrderItem, error)
AddItem adds a product to an open order. Returns ErrOrderNotOpen if the order is not open. Returns ErrInvalidProductType if the product is an ingredient.
func (*OrderService) ApplyOrderDiscount ¶
func (s *OrderService) ApplyOrderDiscount( ctx context.Context, orderID uuid.UUID, req *dto.ApplyOrderDiscountDTO, ) (*model.Order, error)
ApplyOrderDiscount applies a named or ad-hoc discount to the order total. Replaces any previously applied order-level discount. Returns ErrOrderNotOpen if the order is not open. Returns ErrDiscountExceedsTotal if the discount would wipe out the order total.
func (*OrderService) AssignCustomer ¶ added in v0.1.2
func (s *OrderService) AssignCustomer( ctx context.Context, orderID, customerID uuid.UUID, ) (*model.Order, error)
AssignCustomer attaches a customer to an open order, replacing any previously assigned customer. The caller is responsible for verifying customerID belongs to the order's company before calling this. Returns ErrOrderNotOpen if the order is not open.
func (*OrderService) Checkout ¶
func (s *OrderService) Checkout( ctx context.Context, orderID uuid.UUID, userID uuid.UUID, req *dto.CheckoutDTO, ) (*model.Order, error)
Checkout finalizes a paid order in a single transaction: validates payment total and methods, deducts stock, records payments, generates a receipt number, and marks the order paid.
func (*OrderService) CreateOrder ¶
func (s *OrderService) CreateOrder( ctx context.Context, companyID uuid.UUID, _ uuid.UUID, req *dto.CreateOrderDTO, ) (*model.Order, error)
CreateOrder creates a new open order within the given session. Returns ErrNotFound if the session does not exist. Returns ErrOrderNotOpen if the session is already closed.
func (*OrderService) DecrementItem ¶
DecrementItem decreases an item's quantity by 1. If the quantity reaches 0, the item is removed entirely.
func (*OrderService) GetOrderByID ¶
GetOrderByID retrieves an order by ID.
func (*OrderService) GetOrderByIDForCompany ¶
func (s *OrderService) GetOrderByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.Order, error)
GetOrderByIDForCompany fetches an order and verifies it belongs to companyID, returning data.ErrForbidden otherwise. Order items and payments have no CompanyID of their own — verifying the parent order here is what scopes them too, since every item/payment action is keyed on an orderID first.
func (*OrderService) ListItemsByOrder ¶
func (s *OrderService) ListItemsByOrder( ctx context.Context, orderID uuid.UUID, ) ([]model.OrderItem, error)
ListItemsByOrder returns all items on an order.
func (*OrderService) ListOrdersByCompany ¶
func (s *OrderService) ListOrdersByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Order], error)
ListOrdersByCompany returns paginated orders for a company.
func (*OrderService) ListOrdersByCompanyFiltered ¶
func (s *OrderService) ListOrdersByCompanyFiltered( ctx context.Context, companyID uuid.UUID, filter *data.OrderFilter, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Order], error)
ListOrdersByCompanyFiltered returns paginated orders for a company, narrowed by filter.
func (*OrderService) ListOrdersBySession ¶
func (s *OrderService) ListOrdersBySession( ctx context.Context, sessionID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Order], error)
ListOrdersBySession returns paginated orders for a session.
func (*OrderService) RemoveCustomer ¶ added in v0.1.2
func (s *OrderService) RemoveCustomer( ctx context.Context, orderID uuid.UUID, ) (*model.Order, error)
RemoveCustomer detaches any customer from an open order. Returns ErrOrderNotOpen if the order is not open.
func (*OrderService) RemoveItem ¶
RemoveItem removes an item from an open order. Returns ErrOrderNotOpen if the order is not open. Returns ErrNotFound if the item does not belong to this order.
func (*OrderService) RemoveOrderDiscount ¶
func (s *OrderService) RemoveOrderDiscount( ctx context.Context, orderID uuid.UUID, ) (*model.Order, error)
RemoveOrderDiscount removes an order-level discount, restoring the full items total. Returns ErrOrderNotOpen if the order is not open.
func (*OrderService) Return ¶
func (s *OrderService) Return( ctx context.Context, orderID uuid.UUID, returnedBy uuid.UUID, req *dto.ReturnOrderDTO, ) (*model.OrderReturn, error)
Return processes a partial or full return against a paid order. Manager/owner only. Returns ErrOrderNotPaid if the order is not paid.
func (*OrderService) Void ¶
func (s *OrderService) Void( ctx context.Context, orderID uuid.UUID, voidedBy uuid.UUID, req *dto.VoidOrderDTO, ) (*model.Order, error)
Void voids a paid order, restoring stock. Manager/owner only. Returns ErrOrderNotPaid if the order is not in paid status. Returns ErrOrderAlreadyVoided if already voided.
type POSStationService ¶
type POSStationService struct {
// contains filtered or unexported fields
}
POSStationService handles POS station-related business operations.
func NewPOSStationService ¶
func NewPOSStationService( repo data.POSStationRepoer, branchRepo data.BranchRepoer, ) *POSStationService
func (*POSStationService) Create ¶
func (s *POSStationService) Create( ctx context.Context, req *dto.CreatePOSStationDTO, ) (*model.POSStation, error)
Create registers a new POS station. Validates that the branch belongs to the given company.
func (*POSStationService) GetByID ¶
func (s *POSStationService) GetByID(ctx context.Context, id uuid.UUID) (*model.POSStation, error)
func (*POSStationService) GetByIDForCompany ¶
func (s *POSStationService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.POSStation, error)
GetByIDForCompany fetches a station and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*POSStationService) ListByCompany ¶
func (s *POSStationService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.POSStation], error)
func (*POSStationService) Update ¶
func (s *POSStationService) Update( ctx context.Context, id uuid.UUID, req *dto.UpdatePOSStationDTO, ) (*model.POSStation, error)
type ProductService ¶
type ProductService struct {
// contains filtered or unexported fields
}
ProductService handles product-related business operations including recipes, COGS computation, and available units calculation.
func NewProductService ¶
func NewProductService(repo data.ProductRepoer, inventory data.InventoryQuerier) *ProductService
func (*ProductService) AvailableUnits ¶
AvailableUnits returns how many complete units of a recipe product can be made given current ingredient stock levels. Returns ErrInsufficientStock if the inventory querier is nil or any ingredient has no stock record yet.
func (*ProductService) ComputeCOGS ¶
ComputeCOGS returns the cost of goods sold for a recipe product. Returns ErrNoProcurementPrice if the inventory querier is nil or any ingredient has no procurement price recorded.
func (*ProductService) Create ¶
func (s *ProductService) Create( ctx context.Context, req *dto.CreateProductDTO, ) (*model.Product, error)
func (*ProductService) CurrentStock ¶
CurrentStock returns the current stock level for a product. Returns ErrInsufficientStock if inventory is nil or no movements have been recorded.
func (*ProductService) GetByIDForCompany ¶
func (s *ProductService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.Product, error)
GetByIDForCompany fetches a product and verifies it belongs to companyID, returning data.ErrForbidden otherwise. Recipe components/overheads have no CompanyID of their own — verifying their parent product here is what scopes them too, since they're never reached except via their product ID.
func (*ProductService) GetBySKU ¶
func (s *ProductService) GetBySKU( ctx context.Context, companyID uuid.UUID, sku string, ) (*model.Product, error)
GetBySKU retrieves a product by its SKU, scoped to companyID. Used by the POS barcode-scan-to-cart flow.
func (*ProductService) GetProductCategories ¶ added in v0.1.7
func (s *ProductService) GetProductCategories( ctx context.Context, productID uuid.UUID, ) ([]model.Category, error)
GetProductCategories returns the categories currently assigned to a product — used to pre-fill the edit form's tagsinput.
func (*ProductService) GetRecipe ¶
func (s *ProductService) GetRecipe( ctx context.Context, recipeID uuid.UUID, ) (*dto.RecipeDTO, error)
GetRecipe returns the recipe components and overheads for a recipe product.
func (*ProductService) LPP ¶
LPP returns the last procurement price for a product. Returns ErrNoProcurementPrice if inventory is nil or no procurement exists.
func (*ProductService) ListByCompany ¶
func (s *ProductService) ListByCompany( ctx context.Context, companyID uuid.UUID, productType string, categoryID *uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Product], error)
func (*ProductService) ListCategories ¶ added in v0.1.7
func (s *ProductService) ListCategories( ctx context.Context, companyID uuid.UUID, ) ([]model.Category, error)
ListCategories returns every category defined for a company — used to populate the tagsinput's Suggestions on the product form and the category filter dropdown on the product list page.
func (*ProductService) SetRecipe ¶
func (s *ProductService) SetRecipe( ctx context.Context, recipeID uuid.UUID, req *dto.SetRecipeDTO, ) error
SetRecipe atomically replaces all recipe components and overheads for a recipe product.
type ReportsService ¶
type ReportsService struct {
// contains filtered or unexported fields
}
ReportsService assembles management reports from aggregate repo queries.
func NewReportsService ¶
func NewReportsService(repo data.ReportsQuerier) *ReportsService
NewReportsService returns a ReportsService backed by the given querier.
func (*ReportsService) InventoryReport ¶
func (s *ReportsService) InventoryReport( ctx context.Context, q *dto.InventoryReportQuery, ) (*model.InventoryReport, error)
InventoryReport returns a stock snapshot with last-purchase-price and low-stock flags.
func (*ReportsService) PLReport ¶
func (s *ReportsService) PLReport( ctx context.Context, q *dto.PLReportQuery, ) (*model.PLReport, error)
PLReport returns a profit-and-loss summary for the requested period.
func (*ReportsService) SalesReport ¶
func (s *ReportsService) SalesReport( ctx context.Context, q *dto.SalesReportQuery, ) (*model.SalesReport, error)
SalesReport returns a full sales analytics report for the requested period.
type SessionService ¶
type SessionService struct {
// contains filtered or unexported fields
}
SessionService handles POS session lifecycle operations.
func NewSessionService ¶
func NewSessionService( repo data.SessionRepoer, stationRepo data.POSStationRepoer, emitter data.EventEmitter, ) *SessionService
NewSessionService constructs a SessionService.
func (*SessionService) Close ¶
func (s *SessionService) Close( ctx context.Context, sessionID uuid.UUID, closerID uuid.UUID, req *dto.CloseSessionDTO, ) (*model.SessionReconciliation, error)
Close closes a POS session and writes a SessionReconciliation record. Returns ErrNotFound if the session does not exist, ErrSessionAlreadyClosed if it is already closed, and ErrOpenOrdersExist if there are open orders (Phase 6 stub: never returns this error until the orders table exists).
func (*SessionService) ForceClose ¶
func (s *SessionService) ForceClose( ctx context.Context, sessionID, companyID, actorID uuid.UUID, ) (*model.SessionReconciliation, error)
ForceClose forcibly closes a session on an admin's behalf: any open (unpaid) order on the session is hard-deleted first — safe because stock is only decremented at Checkout, never when an item is merely added to an open order — and then the session is closed exactly like a normal Close, with DeclaredCash set to the computed expected cash (there is no cashier present to physically count the drawer, so the resulting reconciliation always has a zero Difference). Returns ErrNotFound if the session does not exist, ErrForbidden if it belongs to a different company, and ErrSessionAlreadyClosed if it is already closed.
func (*SessionService) GetByID ¶
func (s *SessionService) GetByID(ctx context.Context, id uuid.UUID) (*model.POSSession, error)
GetByID retrieves a session by ID.
func (*SessionService) GetByIDForCompany ¶
func (s *SessionService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.POSSession, error)
GetByIDForCompany fetches a session and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*SessionService) GetOpenByUser ¶
func (s *SessionService) GetOpenByUser( ctx context.Context, companyID, userID uuid.UUID, ) (*model.POSSession, error)
GetOpenByUser retrieves the open session opened by userID within companyID, if any.
func (*SessionService) GetReconciliation ¶
func (s *SessionService) GetReconciliation( ctx context.Context, sessionID uuid.UUID, ) (*model.SessionReconciliation, error)
GetReconciliation retrieves the reconciliation record for a session.
func (*SessionService) ListByCompany ¶
func (s *SessionService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.POSSession], error)
ListByCompany returns paginated sessions for a company.
func (*SessionService) ListOpenByCompanyFiltered ¶
func (s *SessionService) ListOpenByCompanyFiltered( ctx context.Context, companyID uuid.UUID, filter *data.SessionFilter, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.POSSession], error)
ListOpenByCompanyFiltered returns paginated open sessions for a company, optionally narrowed by branch.
func (*SessionService) Open ¶
func (s *SessionService) Open( ctx context.Context, companyID uuid.UUID, userID uuid.UUID, req *dto.OpenSessionDTO, ) (*model.POSSession, error)
Open creates a new POS session for the given station. Only cashier and owner roles may call this — enforced at the handler layer. Returns ErrNotFound if the station does not exist, ErrStationBusy if an open session already exists for that station.
type SupplierPaymentService ¶
type SupplierPaymentService struct {
// contains filtered or unexported fields
}
SupplierPaymentService handles payments made to suppliers.
func NewSupplierPaymentService ¶
func NewSupplierPaymentService( repo data.SupplierPaymentRepoer, supplierRepo data.SupplierRepoer, emitter data.EventEmitter, ) *SupplierPaymentService
func (*SupplierPaymentService) ListByCompany ¶
func (s *SupplierPaymentService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.SupplierPayment], error)
func (*SupplierPaymentService) ListBySupplier ¶
func (s *SupplierPaymentService) ListBySupplier( ctx context.Context, supplierID, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.SupplierPayment], error)
ListBySupplier returns a supplier's payment history, scoped to companyID — verifies the supplier actually belongs to the caller before listing its (potentially sensitive) payment history.
func (*SupplierPaymentService) RecordPayment ¶
func (s *SupplierPaymentService) RecordPayment( ctx context.Context, supplierID uuid.UUID, companyID uuid.UUID, req *dto.RecordSupplierPaymentDTO, ) (*model.SupplierPayment, error)
type SupplierService ¶
type SupplierService struct {
// contains filtered or unexported fields
}
SupplierService handles supplier-related business operations.
func NewSupplierService ¶
func NewSupplierService(repo data.SupplierRepoer) *SupplierService
func (*SupplierService) Create ¶
func (s *SupplierService) Create( ctx context.Context, req *dto.CreateSupplierDTO, ) (*model.Supplier, error)
func (*SupplierService) GetByIDForCompany ¶
func (s *SupplierService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.Supplier, error)
GetByIDForCompany fetches a supplier and verifies it belongs to companyID, returning data.ErrForbidden otherwise.
func (*SupplierService) ListByCompany ¶
func (s *SupplierService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.Supplier], error)
type UserService ¶
type UserService struct {
// contains filtered or unexported fields
}
UserService implements all user-related business operations.
func NewUserService ¶
func NewUserService(repo data.UserRepoer) *UserService
NewUserService creates a new UserService backed by the given repo.
func (*UserService) Create ¶
func (s *UserService) Create(ctx context.Context, req *dto.CreateUserDTO) (*model.User, error)
Create registers a new user. The entity's ID and timestamps are set by the repo layer. CompanyID is required when Role is manager or cashier.
func (*UserService) Delete ¶
Delete removes a user by ID. Returns data.ErrNotFound if the user does not exist.
func (*UserService) GetByID ¶
GetByID fetches a single user by its UUID. Returns data.ErrNotFound if absent.
func (*UserService) GetByIDForCompany ¶
func (s *UserService) GetByIDForCompany( ctx context.Context, id, companyID uuid.UUID, ) (*model.User, error)
GetByIDForCompany fetches a user and verifies it belongs to companyID, returning data.ErrForbidden otherwise. Owner-role users have a nil CompanyID and never match here — this is only for company team members.
func (*UserService) List ¶
func (s *UserService) List( ctx context.Context, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.User], error)
List returns a paginated list of users.
func (*UserService) ListByCompany ¶
func (s *UserService) ListByCompany( ctx context.Context, companyID uuid.UUID, q *dto.PaginationDTO, ) (*dto.PaginationResults[model.User], error)
ListByCompany returns a paginated list of users belonging to companyID.