Dev #27

Merged
aefril merged 4 commits from dev into main 2026-08-11 17:46:44 +02:00
Showing only changes of commit dbc143954c - Show all commits
+8 -67
View File
@@ -32,6 +32,9 @@ type PurchaseOrderProcessorImpl struct {
categoryRepo CategoryRepository categoryRepo CategoryRepository
unitRepo UnitRepository unitRepo UnitRepository
fileRepo FileRepository fileRepo FileRepository
// Kept wired but currently unused: purchase orders are a record of spending
// only, so nothing here moves stock or converts units. These stay so that
// tying purchases back to inventory is a change in one place.
inventoryMovementService InventoryMovementService inventoryMovementService InventoryMovementService
unitConverterRepo IngredientUnitConverterRepository unitConverterRepo IngredientUnitConverterRepository
} }
@@ -438,73 +441,11 @@ func (p *PurchaseOrderProcessorImpl) UpdatePurchaseOrderStatus(ctx context.Conte
fmt.Println("status:", po.Status) fmt.Println("status:", po.Status)
// Check if status is changing to "received" and current status is not "received" // A purchase order is a record of spending only. Receiving one does not move
if status == "received" && po.Status != "received" { // ingredient stock, does not recalculate ingredient cost, and never converts
// Get purchase order with items for inventory update // units: the quantity and unit on an item are kept exactly as the user
poWithItems, err := p.purchaseOrderRepo.GetByID(ctx, id) // entered them. Raw material items are therefore treated the same way expense
if err != nil { // items already were, and the ingredient on an item is just a reference.
return nil, fmt.Errorf("failed to get purchase order with items: %w", err)
}
// Update inventory for each item
for _, item := range poWithItems.Items {
if item.PurchaseCategory != nil && item.PurchaseCategory.Type == entities.PurchaseCategoryTypeExpense {
continue
}
if item.IngredientID == nil || item.UnitID == nil || item.Quantity == nil {
return nil, fmt.Errorf("purchase order item %s is missing raw material inventory fields", item.ID)
}
// Get ingredient to find its base unit
ingredient, err := p.ingredientRepo.GetByID(ctx, *item.IngredientID, organizationID)
if err != nil {
return nil, fmt.Errorf("failed to get ingredient %s: %w", *item.IngredientID, err)
}
// Convert quantity to ingredient's base unit if needed. An ingredient
// without a unit has no base unit to convert into, so the purchased
// quantity is taken as-is.
quantityToAdd := *item.Quantity
if ingredient.UnitID != nil && *item.UnitID != *ingredient.UnitID {
// Convert from purchase unit to ingredient's base unit
convertedQuantity, err := p.unitConverterRepo.ConvertQuantity(ctx, *item.IngredientID, *item.UnitID, *ingredient.UnitID, organizationID, *item.Quantity)
if err != nil {
return nil, fmt.Errorf("failed to convert quantity for ingredient %s from unit %s to %s: %w", *item.IngredientID, *item.UnitID, *ingredient.UnitID, err)
}
quantityToAdd = convertedQuantity
}
// Calculate unit cost in ingredient's base unit
unitCost := 0.0
if quantityToAdd > 0 {
unitCost = calculatePurchaseOrderItemTotal(item.Quantity, item.Amount) / quantityToAdd
}
// Create inventory movement for ingredient purchase
reason := fmt.Sprintf("Purchase order %s received", po.PONumber)
referenceType := entities.InventoryMovementReferenceTypePurchaseOrder
referenceID := &id
err = p.inventoryMovementService.CreateIngredientMovement(
ctx,
*item.IngredientID,
organizationID,
outletID,
userID,
entities.InventoryMovementTypePurchase,
quantityToAdd,
unitCost,
reason,
&referenceType,
referenceID,
&item.ID,
)
if err != nil {
return nil, fmt.Errorf("failed to create inventory movement for ingredient %s: %w", *item.IngredientID, err)
}
}
}
// Update the purchase order status // Update the purchase order status
statusOutletID := po.OutletID statusOutletID := po.OutletID