2025-07-18 20:10:29 +07:00
package service
import (
"context"
2025-08-13 23:36:31 +07:00
"time"
2025-07-18 20:10:29 +07:00
"apskel-pos-be/internal/appcontext"
"apskel-pos-be/internal/constants"
"apskel-pos-be/internal/contract"
2025-08-13 23:36:31 +07:00
"apskel-pos-be/internal/models"
2025-07-18 20:10:29 +07:00
"apskel-pos-be/internal/processor"
"apskel-pos-be/internal/transformer"
"github.com/google/uuid"
)
type InventoryService interface {
CreateInventory ( ctx context . Context , apctx * appcontext . ContextInfo , req * contract . CreateInventoryRequest ) * contract . Response
UpdateInventory ( ctx context . Context , id uuid . UUID , req * contract . UpdateInventoryRequest ) * contract . Response
DeleteInventory ( ctx context . Context , id uuid . UUID ) * contract . Response
GetInventoryByID ( ctx context . Context , id uuid . UUID ) * contract . Response
ListInventory ( ctx context . Context , req * contract . ListInventoryRequest ) * contract . Response
AdjustInventory ( ctx context . Context , req * contract . AdjustInventoryRequest ) * contract . Response
GetLowStockItems ( ctx context . Context , outletID uuid . UUID ) * contract . Response
GetZeroStockItems ( ctx context . Context , outletID uuid . UUID ) * contract . Response
2025-08-14 00:38:26 +07:00
GetInventoryReportSummary ( ctx context . Context , outletID , organizationID uuid . UUID , dateFrom , dateTo * time . Time ) ( * contract . InventoryReportSummaryResponse , error )
2025-08-13 23:36:31 +07:00
GetInventoryReportDetails ( ctx context . Context , filter * models . InventoryReportFilter , organizationID uuid . UUID ) ( * contract . InventoryReportDetailResponse , error )
2025-07-18 20:10:29 +07:00
}
type InventoryServiceImpl struct {
inventoryProcessor processor . InventoryProcessor
}
func NewInventoryService ( inventoryProcessor processor . InventoryProcessor ) * InventoryServiceImpl {
return & InventoryServiceImpl {
inventoryProcessor : inventoryProcessor ,
}
}
func ( s * InventoryServiceImpl ) CreateInventory ( ctx context . Context , apctx * appcontext . ContextInfo , req * contract . CreateInventoryRequest ) * contract . Response {
modelReq := transformer . CreateInventoryRequestToModel ( req )
2025-08-13 23:36:31 +07:00
inventoryResponse , err := s . inventoryProcessor . Create ( ctx , modelReq , apctx . OrganizationID )
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
contractResponse := transformer . InventoryModelResponseToResponse ( inventoryResponse )
return contract . BuildSuccessResponse ( contractResponse )
}
func ( s * InventoryServiceImpl ) UpdateInventory ( ctx context . Context , id uuid . UUID , req * contract . UpdateInventoryRequest ) * contract . Response {
modelReq := transformer . UpdateInventoryRequestToModel ( req )
2025-08-13 23:36:31 +07:00
inventoryResponse , err := s . inventoryProcessor . Update ( ctx , id , modelReq , uuid . Nil ) // TODO: Get organizationID from context
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
contractResponse := transformer . InventoryModelResponseToResponse ( inventoryResponse )
return contract . BuildSuccessResponse ( contractResponse )
}
func ( s * InventoryServiceImpl ) DeleteInventory ( ctx context . Context , id uuid . UUID ) * contract . Response {
2025-08-13 23:36:31 +07:00
err := s . inventoryProcessor . Delete ( ctx , id , uuid . Nil ) // TODO: Get organizationID from context
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
return contract . BuildSuccessResponse ( map [ string ] interface { } {
"message" : "Inventory deleted successfully" ,
} )
}
func ( s * InventoryServiceImpl ) GetInventoryByID ( ctx context . Context , id uuid . UUID ) * contract . Response {
2025-08-13 23:36:31 +07:00
inventoryResponse , err := s . inventoryProcessor . GetByID ( ctx , id , uuid . Nil ) // TODO: Get organizationID from context
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
contractResponse := transformer . InventoryModelResponseToResponse ( inventoryResponse )
return contract . BuildSuccessResponse ( contractResponse )
}
func ( s * InventoryServiceImpl ) ListInventory ( ctx context . Context , req * contract . ListInventoryRequest ) * contract . Response {
// Build filters
filters := make ( map [ string ] interface { } )
if req . OutletID != nil {
filters [ "outlet_id" ] = * req . OutletID
}
if req . ProductID != nil {
filters [ "product_id" ] = * req . ProductID
}
if req . CategoryID != nil {
filters [ "category_id" ] = * req . CategoryID
}
if req . LowStockOnly != nil && * req . LowStockOnly {
filters [ "low_stock" ] = true
}
if req . ZeroStockOnly != nil && * req . ZeroStockOnly {
filters [ "zero_stock" ] = true
}
if req . Search != "" {
filters [ "search" ] = req . Search
}
2025-08-13 23:36:31 +07:00
inventory , totalCount , err := s . inventoryProcessor . List ( ctx , filters , req . Limit , req . Page , uuid . Nil ) // TODO: Get organizationID from context
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
2025-08-13 23:36:31 +07:00
// Convert from []*models.InventoryResponse to []models.InventoryResponse
var inventoryResponses [ ] models . InventoryResponse
for _ , inv := range inventory {
inventoryResponses = append ( inventoryResponses , * inv )
}
2025-07-18 20:10:29 +07:00
// Convert to contract responses
2025-08-13 23:36:31 +07:00
contractResponses := transformer . InventoryToResponses ( inventoryResponses )
2025-07-18 20:10:29 +07:00
// Calculate total pages
2025-08-13 23:36:31 +07:00
totalPages := int ( totalCount ) / req . Limit
if int ( totalCount ) % req . Limit > 0 {
2025-07-18 20:10:29 +07:00
totalPages ++
}
listResponse := & contract . ListInventoryResponse {
Inventory : contractResponses ,
2025-08-13 23:36:31 +07:00
TotalCount : int ( totalCount ) ,
2025-07-18 20:10:29 +07:00
Page : req . Page ,
Limit : req . Limit ,
TotalPages : totalPages ,
}
return contract . BuildSuccessResponse ( listResponse )
}
func ( s * InventoryServiceImpl ) AdjustInventory ( ctx context . Context , req * contract . AdjustInventoryRequest ) * contract . Response {
modelReq := transformer . AdjustInventoryRequestToModel ( req )
2025-08-13 23:36:31 +07:00
inventoryResponse , err := s . inventoryProcessor . AdjustQuantity ( ctx , modelReq . ProductID , modelReq . OutletID , uuid . Nil , modelReq . Delta ) // TODO: Get organizationID from context
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
contractResponse := transformer . InventoryModelResponseToResponse ( inventoryResponse )
return contract . BuildSuccessResponse ( contractResponse )
}
func ( s * InventoryServiceImpl ) GetLowStockItems ( ctx context . Context , outletID uuid . UUID ) * contract . Response {
2025-08-13 23:36:31 +07:00
inventory , err := s . inventoryProcessor . GetLowStock ( ctx , outletID , uuid . Nil ) // TODO: Get organizationID from context
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
2025-08-13 23:36:31 +07:00
// Convert from []*models.InventoryResponse to []models.InventoryResponse
var inventoryResponses [ ] models . InventoryResponse
for _ , inv := range inventory {
inventoryResponses = append ( inventoryResponses , * inv )
}
contractResponses := transformer . InventoryToResponses ( inventoryResponses )
2025-07-18 20:10:29 +07:00
return contract . BuildSuccessResponse ( contractResponses )
}
func ( s * InventoryServiceImpl ) GetZeroStockItems ( ctx context . Context , outletID uuid . UUID ) * contract . Response {
2025-08-13 23:36:31 +07:00
inventory , err := s . inventoryProcessor . GetZeroStock ( ctx , outletID , uuid . Nil ) // TODO: Get organizationID from context
2025-07-18 20:10:29 +07:00
if err != nil {
errorResp := contract . NewResponseError ( constants . InternalServerErrorCode , constants . InventoryServiceEntity , err . Error ( ) )
return contract . BuildErrorResponse ( [ ] * contract . ResponseError { errorResp } )
}
2025-08-13 23:36:31 +07:00
// Convert from []*models.InventoryResponse to []models.InventoryResponse
var inventoryResponses [ ] models . InventoryResponse
for _ , inv := range inventory {
inventoryResponses = append ( inventoryResponses , * inv )
}
contractResponses := transformer . InventoryToResponses ( inventoryResponses )
2025-07-18 20:10:29 +07:00
return contract . BuildSuccessResponse ( contractResponses )
}
2025-08-13 23:36:31 +07:00
// GetInventoryReportSummary returns summary statistics for inventory report
2025-08-14 00:38:26 +07:00
func ( s * InventoryServiceImpl ) GetInventoryReportSummary ( ctx context . Context , outletID , organizationID uuid . UUID , dateFrom , dateTo * time . Time ) ( * contract . InventoryReportSummaryResponse , error ) {
summary , err := s . inventoryProcessor . GetInventoryReportSummary ( ctx , outletID , organizationID , dateFrom , dateTo )
2025-08-13 23:36:31 +07:00
if err != nil {
return nil , err
}
return & contract . InventoryReportSummaryResponse {
TotalProducts : summary . TotalProducts ,
TotalIngredients : summary . TotalIngredients ,
TotalValue : summary . TotalValue ,
LowStockProducts : summary . LowStockProducts ,
LowStockIngredients : summary . LowStockIngredients ,
ZeroStockProducts : summary . ZeroStockProducts ,
ZeroStockIngredients : summary . ZeroStockIngredients ,
2025-08-14 00:38:26 +07:00
TotalSoldProducts : summary . TotalSoldProducts ,
TotalSoldIngredients : summary . TotalSoldIngredients ,
2025-08-13 23:36:31 +07:00
OutletID : summary . OutletID . String ( ) ,
OutletName : summary . OutletName ,
GeneratedAt : summary . GeneratedAt . Format ( time . RFC3339 ) ,
} , nil
}
// GetInventoryReportDetails returns detailed inventory report with products and ingredients
func ( s * InventoryServiceImpl ) GetInventoryReportDetails ( ctx context . Context , filter * models . InventoryReportFilter , organizationID uuid . UUID ) ( * contract . InventoryReportDetailResponse , error ) {
report , err := s . inventoryProcessor . GetInventoryReportDetails ( ctx , filter , organizationID )
if err != nil {
return nil , err
}
response := & contract . InventoryReportDetailResponse { }
if report . Summary != nil {
response . Summary = & contract . InventoryReportSummaryResponse {
TotalProducts : report . Summary . TotalProducts ,
TotalIngredients : report . Summary . TotalIngredients ,
TotalValue : report . Summary . TotalValue ,
LowStockProducts : report . Summary . LowStockProducts ,
LowStockIngredients : report . Summary . LowStockIngredients ,
ZeroStockProducts : report . Summary . ZeroStockProducts ,
ZeroStockIngredients : report . Summary . ZeroStockIngredients ,
2025-08-14 00:38:26 +07:00
TotalSoldProducts : report . Summary . TotalSoldProducts ,
TotalSoldIngredients : report . Summary . TotalSoldIngredients ,
2025-08-13 23:36:31 +07:00
OutletID : report . Summary . OutletID . String ( ) ,
OutletName : report . Summary . OutletName ,
GeneratedAt : report . Summary . GeneratedAt . Format ( time . RFC3339 ) ,
}
}
response . Products = make ( [ ] * contract . InventoryProductDetailResponse , len ( report . Products ) )
for i , product := range report . Products {
response . Products [ i ] = & contract . InventoryProductDetailResponse {
ID : product . ID . String ( ) ,
ProductID : product . ProductID . String ( ) ,
ProductName : product . ProductName ,
CategoryName : product . CategoryName ,
Quantity : product . Quantity ,
ReorderLevel : product . ReorderLevel ,
UnitCost : product . UnitCost ,
TotalValue : product . TotalValue ,
2025-08-14 00:38:26 +07:00
TotalSold : product . TotalSold ,
2025-08-13 23:36:31 +07:00
IsLowStock : product . IsLowStock ,
IsZeroStock : product . IsZeroStock ,
UpdatedAt : product . UpdatedAt . Format ( time . RFC3339 ) ,
}
}
// Transform ingredients
response . Ingredients = make ( [ ] * contract . InventoryIngredientDetailResponse , len ( report . Ingredients ) )
for i , ingredient := range report . Ingredients {
response . Ingredients [ i ] = & contract . InventoryIngredientDetailResponse {
ID : ingredient . ID . String ( ) ,
IngredientID : ingredient . IngredientID . String ( ) ,
IngredientName : ingredient . IngredientName ,
UnitName : ingredient . UnitName ,
Quantity : ingredient . Quantity ,
ReorderLevel : ingredient . ReorderLevel ,
UnitCost : ingredient . UnitCost ,
TotalValue : ingredient . TotalValue ,
2025-08-14 00:38:26 +07:00
TotalSold : ingredient . TotalSold ,
2025-08-13 23:36:31 +07:00
IsLowStock : ingredient . IsLowStock ,
IsZeroStock : ingredient . IsZeroStock ,
UpdatedAt : ingredient . UpdatedAt . Format ( time . RFC3339 ) ,
}
}
return response , nil
}