2025-07-18 20:10:29 +07:00
package repository
import (
"context"
"errors"
"fmt"
2025-08-13 23:36:31 +07:00
"time"
2025-07-18 20:10:29 +07:00
"apskel-pos-be/internal/entities"
2025-08-13 23:36:31 +07:00
"apskel-pos-be/internal/models"
2025-07-18 20:10:29 +07:00
"github.com/google/uuid"
"gorm.io/gorm"
)
2025-07-30 23:18:20 +07:00
type InventoryRepository interface {
Create ( ctx context . Context , inventory * entities . Inventory ) error
GetByID ( ctx context . Context , id uuid . UUID ) ( * entities . Inventory , error )
GetWithRelations ( ctx context . Context , id uuid . UUID ) ( * entities . Inventory , error )
GetByProductAndOutlet ( ctx context . Context , productID , outletID uuid . UUID ) ( * entities . Inventory , error )
GetByOutlet ( ctx context . Context , outletID uuid . UUID ) ( [ ] * entities . Inventory , error )
GetByProduct ( ctx context . Context , productID uuid . UUID ) ( [ ] * entities . Inventory , error )
GetLowStock ( ctx context . Context , outletID uuid . UUID ) ( [ ] * entities . Inventory , error )
GetZeroStock ( ctx context . Context , outletID uuid . UUID ) ( [ ] * entities . Inventory , error )
Update ( ctx context . Context , inventory * entities . Inventory ) error
Delete ( ctx context . Context , id uuid . UUID ) error
List ( ctx context . Context , filters map [ string ] interface { } , limit , offset int ) ( [ ] * entities . Inventory , int64 , error )
Count ( ctx context . Context , filters map [ string ] interface { } ) ( int64 , error )
AdjustQuantity ( ctx context . Context , productID , outletID uuid . UUID , delta int ) ( * entities . Inventory , error )
SetQuantity ( ctx context . Context , productID , outletID uuid . UUID , quantity int ) ( * entities . Inventory , error )
UpdateReorderLevel ( ctx context . Context , id uuid . UUID , reorderLevel int ) error
BulkCreate ( ctx context . Context , inventoryItems [ ] * entities . Inventory ) error
2025-08-13 23:14:26 +07:00
BulkUpdate ( ctx context . Context , inventoryItems [ ] * entities . Inventory ) error
2025-07-30 23:18:20 +07:00
BulkAdjustQuantity ( ctx context . Context , adjustments map [ uuid . UUID ] int , outletID uuid . UUID ) error
GetTotalValueByOutlet ( ctx context . Context , outletID uuid . UUID ) ( float64 , error )
2025-08-13 23:36:31 +07:00
GetInventoryReportSummary ( ctx context . Context , outletID uuid . UUID ) ( * models . InventoryReportSummary , error )
GetInventoryReportDetails ( ctx context . Context , filter * models . InventoryReportFilter ) ( * models . InventoryReportDetail , error )
2025-07-30 23:18:20 +07:00
}
2025-07-18 20:10:29 +07:00
type InventoryRepositoryImpl struct {
db * gorm . DB
}
func NewInventoryRepositoryImpl ( db * gorm . DB ) * InventoryRepositoryImpl {
return & InventoryRepositoryImpl {
db : db ,
}
}
func ( r * InventoryRepositoryImpl ) Create ( ctx context . Context , inventory * entities . Inventory ) error {
return r . db . WithContext ( ctx ) . Create ( inventory ) . Error
}
func ( r * InventoryRepositoryImpl ) GetByID ( ctx context . Context , id uuid . UUID ) ( * entities . Inventory , error ) {
var inventory entities . Inventory
err := r . db . WithContext ( ctx ) . First ( & inventory , "id = ?" , id ) . Error
if err != nil {
return nil , err
}
return & inventory , nil
}
func ( r * InventoryRepositoryImpl ) GetWithRelations ( ctx context . Context , id uuid . UUID ) ( * entities . Inventory , error ) {
var inventory entities . Inventory
err := r . db . WithContext ( ctx ) .
Preload ( "Product" ) .
Preload ( "Product.Category" ) .
Preload ( "Outlet" ) .
First ( & inventory , "id = ?" , id ) . Error
if err != nil {
return nil , err
}
return & inventory , nil
}
func ( r * InventoryRepositoryImpl ) GetByProductAndOutlet ( ctx context . Context , productID , outletID uuid . UUID ) ( * entities . Inventory , error ) {
var inventory entities . Inventory
err := r . db . WithContext ( ctx ) . Where ( "product_id = ? AND outlet_id = ?" , productID , outletID ) . First ( & inventory ) . Error
if err != nil {
return nil , err
}
return & inventory , nil
}
func ( r * InventoryRepositoryImpl ) GetByOutlet ( ctx context . Context , outletID uuid . UUID ) ( [ ] * entities . Inventory , error ) {
var inventory [ ] * entities . Inventory
err := r . db . WithContext ( ctx ) .
Preload ( "Product" ) .
Preload ( "Product.Category" ) .
Where ( "outlet_id = ?" , outletID ) .
Find ( & inventory ) . Error
return inventory , err
}
func ( r * InventoryRepositoryImpl ) GetByProduct ( ctx context . Context , productID uuid . UUID ) ( [ ] * entities . Inventory , error ) {
var inventory [ ] * entities . Inventory
err := r . db . WithContext ( ctx ) .
Preload ( "Outlet" ) .
Where ( "product_id = ?" , productID ) .
Find ( & inventory ) . Error
return inventory , err
}
func ( r * InventoryRepositoryImpl ) GetLowStock ( ctx context . Context , outletID uuid . UUID ) ( [ ] * entities . Inventory , error ) {
var inventory [ ] * entities . Inventory
err := r . db . WithContext ( ctx ) .
Preload ( "Product" ) .
Preload ( "Product.Category" ) .
Where ( "outlet_id = ? AND quantity <= reorder_level" , outletID ) .
Find ( & inventory ) . Error
return inventory , err
}
func ( r * InventoryRepositoryImpl ) GetZeroStock ( ctx context . Context , outletID uuid . UUID ) ( [ ] * entities . Inventory , error ) {
var inventory [ ] * entities . Inventory
err := r . db . WithContext ( ctx ) .
Preload ( "Product" ) .
Preload ( "Product.Category" ) .
Where ( "outlet_id = ? AND quantity = 0" , outletID ) .
Find ( & inventory ) . Error
return inventory , err
}
func ( r * InventoryRepositoryImpl ) Update ( ctx context . Context , inventory * entities . Inventory ) error {
return r . db . WithContext ( ctx ) . Save ( inventory ) . Error
}
func ( r * InventoryRepositoryImpl ) Delete ( ctx context . Context , id uuid . UUID ) error {
return r . db . WithContext ( ctx ) . Delete ( & entities . Inventory { } , "id = ?" , id ) . Error
}
func ( r * InventoryRepositoryImpl ) List ( ctx context . Context , filters map [ string ] interface { } , limit , offset int ) ( [ ] * entities . Inventory , int64 , error ) {
var inventory [ ] * entities . Inventory
var total int64
query := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Preload ( "Product" ) .
Preload ( "Product.Category" ) .
Preload ( "Outlet" )
for key , value := range filters {
switch key {
case "search" :
searchValue := "%" + value . ( string ) + "%"
query = query . Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "products.name ILIKE ? OR products.sku ILIKE ?" , searchValue , searchValue )
case "low_stock" :
if value . ( bool ) {
query = query . Where ( "quantity <= reorder_level" )
}
case "zero_stock" :
if value . ( bool ) {
query = query . Where ( "quantity = 0" )
}
case "category_id" :
query = query . Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "products.category_id = ?" , value )
default :
query = query . Where ( key + " = ?" , value )
}
}
if err := query . Count ( & total ) . Error ; err != nil {
return nil , 0 , err
}
err := query . Limit ( limit ) . Offset ( offset ) . Find ( & inventory ) . Error
return inventory , total , err
}
func ( r * InventoryRepositoryImpl ) Count ( ctx context . Context , filters map [ string ] interface { } ) ( int64 , error ) {
var count int64
query := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } )
for key , value := range filters {
switch key {
case "search" :
searchValue := "%" + value . ( string ) + "%"
query = query . Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "products.name ILIKE ? OR products.sku ILIKE ?" , searchValue , searchValue )
case "low_stock" :
if value . ( bool ) {
query = query . Where ( "quantity <= reorder_level" )
}
case "zero_stock" :
if value . ( bool ) {
query = query . Where ( "quantity = 0" )
}
case "category_id" :
query = query . Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "products.category_id = ?" , value )
default :
query = query . Where ( key + " = ?" , value )
}
}
err := query . Count ( & count ) . Error
return count , err
}
func ( r * InventoryRepositoryImpl ) AdjustQuantity ( ctx context . Context , productID , outletID uuid . UUID , delta int ) ( * entities . Inventory , error ) {
var inventory entities . Inventory
err := r . db . WithContext ( ctx ) . Transaction ( func ( tx * gorm . DB ) error {
// Try to find existing inventory
if err := tx . Where ( "product_id = ? AND outlet_id = ?" , productID , outletID ) . First ( & inventory ) . Error ; err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
// Inventory doesn't exist, create it with initial quantity
inventory = entities . Inventory {
ProductID : productID ,
OutletID : outletID ,
Quantity : 0 ,
ReorderLevel : 0 ,
}
if err := tx . Create ( & inventory ) . Error ; err != nil {
return fmt . Errorf ( "failed to create inventory record: %w" , err )
}
} else {
return err
}
}
inventory . UpdateQuantity ( delta )
return tx . Save ( & inventory ) . Error
} )
if err != nil {
return nil , err
}
return & inventory , nil
}
func ( r * InventoryRepositoryImpl ) SetQuantity ( ctx context . Context , productID , outletID uuid . UUID , quantity int ) ( * entities . Inventory , error ) {
var inventory entities . Inventory
err := r . db . WithContext ( ctx ) . Transaction ( func ( tx * gorm . DB ) error {
if err := tx . Where ( "product_id = ? AND outlet_id = ?" , productID , outletID ) . First ( & inventory ) . Error ; err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
inventory = entities . Inventory {
ProductID : productID ,
OutletID : outletID ,
Quantity : quantity ,
ReorderLevel : 0 ,
}
if inventory . Quantity < 0 {
inventory . Quantity = 0
}
if err := tx . Create ( & inventory ) . Error ; err != nil {
return fmt . Errorf ( "failed to create inventory record: %w" , err )
}
return nil
}
return err
}
// Set new quantity
inventory . Quantity = quantity
if inventory . Quantity < 0 {
inventory . Quantity = 0
}
// Save updated inventory
return tx . Save ( & inventory ) . Error
} )
if err != nil {
return nil , err
}
return & inventory , nil
}
func ( r * InventoryRepositoryImpl ) UpdateReorderLevel ( ctx context . Context , id uuid . UUID , reorderLevel int ) error {
return r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Where ( "id = ?" , id ) .
Update ( "reorder_level" , reorderLevel ) . Error
}
func ( r * InventoryRepositoryImpl ) BulkCreate ( ctx context . Context , inventoryItems [ ] * entities . Inventory ) error {
return r . db . WithContext ( ctx ) . CreateInBatches ( inventoryItems , 100 ) . Error
}
2025-08-13 23:14:26 +07:00
func ( r * InventoryRepositoryImpl ) BulkUpdate ( ctx context . Context , inventoryItems [ ] * entities . Inventory ) error {
if len ( inventoryItems ) == 0 {
return nil
}
2025-08-13 23:36:31 +07:00
2025-08-13 23:14:26 +07:00
// Use GORM's transaction for bulk updates
return r . db . WithContext ( ctx ) . Transaction ( func ( tx * gorm . DB ) error {
for _ , inventory := range inventoryItems {
if err := tx . Save ( inventory ) . Error ; err != nil {
return fmt . Errorf ( "failed to update inventory for product %s: %w" , inventory . ProductID , err )
}
}
return nil
} )
}
2025-07-18 20:10:29 +07:00
func ( r * InventoryRepositoryImpl ) BulkAdjustQuantity ( ctx context . Context , adjustments map [ uuid . UUID ] int , outletID uuid . UUID ) error {
return r . db . WithContext ( ctx ) . Transaction ( func ( tx * gorm . DB ) error {
for productID , delta := range adjustments {
var inventory entities . Inventory
if err := tx . Where ( "product_id = ? AND outlet_id = ?" , productID , outletID ) . First ( & inventory ) . Error ; err != nil {
if errors . Is ( err , gorm . ErrRecordNotFound ) {
// Inventory doesn't exist, create it with initial quantity
inventory = entities . Inventory {
ProductID : productID ,
OutletID : outletID ,
Quantity : 0 ,
ReorderLevel : 0 ,
}
if err := tx . Create ( & inventory ) . Error ; err != nil {
return fmt . Errorf ( "failed to create inventory record for product %s: %w" , productID , err )
}
} else {
return err
}
}
inventory . UpdateQuantity ( delta )
if err := tx . Save ( & inventory ) . Error ; err != nil {
return err
}
}
return nil
} )
}
func ( r * InventoryRepositoryImpl ) GetTotalValueByOutlet ( ctx context . Context , outletID uuid . UUID ) ( float64 , error ) {
var totalValue float64
2025-08-13 23:36:31 +07:00
if err := r . db . WithContext ( ctx ) .
2025-07-18 20:10:29 +07:00
Table ( "inventory" ) .
Select ( "SUM(inventory.quantity * products.cost)" ) .
Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "inventory.outlet_id = ?" , outletID ) .
2025-08-13 23:36:31 +07:00
Scan ( & totalValue ) . Error ; err != nil {
return 0 , fmt . Errorf ( "failed to get total value: %w" , err )
}
return totalValue , nil
}
// GetInventoryReportSummary returns summary statistics for inventory report
func ( r * InventoryRepositoryImpl ) GetInventoryReportSummary ( ctx context . Context , outletID uuid . UUID ) ( * models . InventoryReportSummary , error ) {
var summary models . InventoryReportSummary
summary . OutletID = outletID
summary . GeneratedAt = time . Now ( )
// Get outlet name
var outlet entities . Outlet
if err := r . db . WithContext ( ctx ) . Select ( "name" ) . First ( & outlet , "id = ?" , outletID ) . Error ; err != nil {
return nil , fmt . Errorf ( "failed to get outlet name: %w" , err )
}
summary . OutletName = outlet . Name
// Get total products count
var totalProducts int64
if err := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "inventory.outlet_id = ? AND products.has_ingredients = false" , outletID ) .
Count ( & totalProducts ) . Error ; err != nil {
return nil , fmt . Errorf ( "failed to count total products: %w" , err )
}
summary . TotalProducts = int ( totalProducts )
// Get total ingredients count
var totalIngredients int64
if err := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Joins ( "JOIN ingredients ON inventory.product_id = ingredients.id" ) .
Where ( "inventory.outlet_id = ?" , outletID ) .
Count ( & totalIngredients ) . Error ; err != nil {
return nil , fmt . Errorf ( "failed to count total ingredients: %w" , err )
}
summary . TotalIngredients = int ( totalIngredients )
// Get low stock products count
var lowStockProducts int64
if err := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "inventory.outlet_id = ? AND products.has_ingredients = false AND inventory.quantity <= inventory.reorder_level AND inventory.quantity > 0" , outletID ) .
Count ( & lowStockProducts ) . Error ; err != nil {
return nil , fmt . Errorf ( "failed to count low stock products: %w" , err )
}
summary . LowStockProducts = int ( lowStockProducts )
// Get low stock ingredients count
var lowStockIngredients int64
if err := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Joins ( "JOIN ingredients ON inventory.product_id = ingredients.id" ) .
Where ( "inventory.outlet_id = ? AND inventory.quantity <= inventory.reorder_level AND inventory.quantity > 0" , outletID ) .
Count ( & lowStockIngredients ) . Error ; err != nil {
return nil , fmt . Errorf ( "failed to count low stock ingredients: %w" , err )
}
summary . LowStockIngredients = int ( lowStockIngredients )
// Get zero stock products count
var zeroStockProducts int64
if err := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Where ( "inventory.outlet_id = ? AND products.has_ingredients = false AND inventory.quantity = 0" , outletID ) .
Count ( & zeroStockProducts ) . Error ; err != nil {
return nil , fmt . Errorf ( "failed to count zero stock products: %w" , err )
}
summary . ZeroStockProducts = int ( zeroStockProducts )
// Get zero stock ingredients count
var zeroStockIngredients int64
if err := r . db . WithContext ( ctx ) . Model ( & entities . Inventory { } ) .
Joins ( "JOIN ingredients ON inventory.product_id = ingredients.id" ) .
Where ( "inventory.outlet_id = ? AND inventory.quantity = 0" , outletID ) .
Count ( & zeroStockIngredients ) . Error ; err != nil {
return nil , fmt . Errorf ( "failed to count zero stock ingredients: %w" , err )
}
summary . ZeroStockIngredients = int ( zeroStockIngredients )
// Get total value
totalValue , err := r . GetTotalValueByOutlet ( ctx , outletID )
if err != nil {
return nil , fmt . Errorf ( "failed to get total value: %w" , err )
}
summary . TotalValue = totalValue
return & summary , nil
}
// GetInventoryReportDetails returns detailed inventory report with products and ingredients
func ( r * InventoryRepositoryImpl ) GetInventoryReportDetails ( ctx context . Context , filter * models . InventoryReportFilter ) ( * models . InventoryReportDetail , error ) {
report := & models . InventoryReportDetail { }
// Get summary
if filter . OutletID != nil {
summary , err := r . GetInventoryReportSummary ( ctx , * filter . OutletID )
if err != nil {
return nil , fmt . Errorf ( "failed to get report summary: %w" , err )
}
report . Summary = summary
}
// Get products details
products , err := r . getInventoryProductsDetails ( ctx , filter )
if err != nil {
return nil , fmt . Errorf ( "failed to get products details: %w" , err )
}
report . Products = products
// Get ingredients details
ingredients , err := r . getInventoryIngredientsDetails ( ctx , filter )
if err != nil {
return nil , fmt . Errorf ( "failed to get ingredients details: %w" , err )
}
report . Ingredients = ingredients
return report , nil
}
// getInventoryProductsDetails retrieves detailed product inventory information
func ( r * InventoryRepositoryImpl ) getInventoryProductsDetails ( ctx context . Context , filter * models . InventoryReportFilter ) ( [ ] * models . InventoryProductDetail , error ) {
query := r . db . WithContext ( ctx ) . Table ( "inventory" ) .
Select ( `
inventory . id ,
inventory . product_id ,
products . name as product_name ,
categories . name as category_name ,
inventory . quantity ,
inventory . reorder_level ,
COALESCE ( product_variants . cost , products . cost ) as unit_cost ,
( COALESCE ( product_variants . cost , products . cost ) * inventory . quantity ) as total_value ,
inventory . updated_at
` ) .
Joins ( "JOIN products ON inventory.product_id = products.id" ) .
Joins ( "LEFT JOIN categories ON products.category_id = categories.id" ) .
Joins ( "LEFT JOIN product_variants ON products.id = product_variants.product_id" ) .
Where ( "inventory.outlet_id = ? AND products.has_ingredients = false" , filter . OutletID )
// Apply filters
if filter . CategoryID != nil {
query = query . Where ( "products.category_id = ?" , * filter . CategoryID )
}
if filter . ShowLowStock != nil && * filter . ShowLowStock {
query = query . Where ( "inventory.quantity <= inventory.reorder_level AND inventory.quantity > 0" )
}
if filter . ShowZeroStock != nil && * filter . ShowZeroStock {
query = query . Where ( "inventory.quantity = 0" )
}
if filter . Search != nil && * filter . Search != "" {
searchTerm := "%" + * filter . Search + "%"
query = query . Where ( "products.name ILIKE ? OR categories.name ILIKE ?" , searchTerm , searchTerm )
}
// Apply pagination
if filter . Limit != nil {
query = query . Limit ( * filter . Limit )
}
if filter . Offset != nil {
query = query . Offset ( * filter . Offset )
}
query = query . Order ( "products.name ASC" )
var results [ ] struct {
ID uuid . UUID
ProductID uuid . UUID
ProductName string
CategoryName * string
Quantity int
ReorderLevel int
UnitCost float64
TotalValue float64
UpdatedAt time . Time
}
if err := query . Find ( & results ) . Error ; err != nil {
return nil , err
}
var products [ ] * models . InventoryProductDetail
for _ , result := range results {
categoryName := ""
if result . CategoryName != nil {
categoryName = * result . CategoryName
}
product := & models . InventoryProductDetail {
ID : result . ID ,
ProductID : result . ProductID ,
ProductName : result . ProductName ,
CategoryName : categoryName ,
Quantity : result . Quantity ,
ReorderLevel : result . ReorderLevel ,
UnitCost : result . UnitCost ,
TotalValue : result . TotalValue ,
IsLowStock : result . Quantity <= result . ReorderLevel && result . Quantity > 0 ,
IsZeroStock : result . Quantity == 0 ,
UpdatedAt : result . UpdatedAt ,
}
products = append ( products , product )
}
return products , nil
}
// getInventoryIngredientsDetails retrieves detailed ingredient inventory information
func ( r * InventoryRepositoryImpl ) getInventoryIngredientsDetails ( ctx context . Context , filter * models . InventoryReportFilter ) ( [ ] * models . InventoryIngredientDetail , error ) {
query := r . db . WithContext ( ctx ) . Table ( "inventory" ) .
Select ( `
inventory . id ,
inventory . product_id as ingredient_id ,
ingredients . name as ingredient_name ,
units . name as unit_name ,
inventory . quantity ,
inventory . reorder_level ,
ingredients . cost as unit_cost ,
( ingredients . cost * inventory . quantity ) as total_value ,
inventory . updated_at
` ) .
Joins ( "JOIN ingredients ON inventory.product_id = ingredients.id" ) .
Joins ( "LEFT JOIN units ON ingredients.unit_id = units.id" ) .
Where ( "inventory.outlet_id = ?" , filter . OutletID )
// Apply filters
if filter . ShowLowStock != nil && * filter . ShowLowStock {
query = query . Where ( "inventory.quantity <= inventory.reorder_level AND inventory.quantity > 0" )
}
if filter . ShowZeroStock != nil && * filter . ShowZeroStock {
query = query . Where ( "inventory.quantity = 0" )
}
if filter . Search != nil && * filter . Search != "" {
searchTerm := "%" + * filter . Search + "%"
query = query . Where ( "ingredients.name ILIKE ? OR units.name ILIKE ?" , searchTerm , searchTerm )
}
// Apply pagination
if filter . Limit != nil {
query = query . Limit ( * filter . Limit )
}
if filter . Offset != nil {
query = query . Offset ( * filter . Offset )
}
query = query . Order ( "ingredients.name ASC" )
var results [ ] struct {
ID uuid . UUID
IngredientID uuid . UUID
IngredientName string
UnitName * string
Quantity int
ReorderLevel int
UnitCost float64
TotalValue float64
UpdatedAt time . Time
}
if err := query . Find ( & results ) . Error ; err != nil {
return nil , err
}
var ingredients [ ] * models . InventoryIngredientDetail
for _ , result := range results {
unitName := ""
if result . UnitName != nil {
unitName = * result . UnitName
}
ingredient := & models . InventoryIngredientDetail {
ID : result . ID ,
IngredientID : result . IngredientID ,
IngredientName : result . IngredientName ,
UnitName : unitName ,
Quantity : result . Quantity ,
ReorderLevel : result . ReorderLevel ,
UnitCost : result . UnitCost ,
TotalValue : result . TotalValue ,
IsLowStock : result . Quantity <= result . ReorderLevel && result . Quantity > 0 ,
IsZeroStock : result . Quantity == 0 ,
UpdatedAt : result . UpdatedAt ,
}
ingredients = append ( ingredients , ingredient )
}
2025-07-18 20:10:29 +07:00
2025-08-13 23:36:31 +07:00
return ingredients , nil
2025-07-18 20:10:29 +07:00
}