Fix wipe inventory action to use correct onclose

This commit is contained in:
Phil
2025-12-28 17:29:14 +00:00
committed by GitHub
5 changed files with 375 additions and 37 deletions

View File

@@ -8,7 +8,7 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228052820-4557df86eddb h1:nRu1qr3gceoIIDJolCRnd/Eo5VLAMoH9CYnyKCCVBuA=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228052820-4557df86eddb/go.mod h1:9zHHw5TNttw5Kn4Wks+SxwXmJPz6PgGNbnB4BtF1Z4c=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228163253-2bd6ff580a7f h1:+5m3FRu/Ja3kH2XgFn0GYCWOKIe4O+7PbLawLXvb4gA=
github.com/sysadminsmedia/homebox/backend v0.0.0-20251228163253-2bd6ff580a7f/go.mod h1:9zHHw5TNttw5Kn4Wks+SxwXmJPz6PgGNbnB4BtF1Z4c=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -810,6 +810,22 @@ func (e *ItemsRepository) DeleteByGroup(ctx context.Context, gid, id uuid.UUID)
}
func (e *ItemsRepository) WipeInventory(ctx context.Context, gid uuid.UUID, wipeLabels bool, wipeLocations bool, wipeMaintenance bool) (int, error) {
deleted := 0
// Wipe maintenance records if requested
// IMPORTANT: Must delete maintenance records BEFORE items since they are linked to items
if wipeMaintenance {
maintenanceCount, err := e.db.MaintenanceEntry.Delete().
Where(maintenanceentry.HasItemWith(item.HasGroupWith(group.ID(gid)))).
Exec(ctx)
if err != nil {
log.Err(err).Msg("failed to delete maintenance entries during wipe inventory")
} else {
log.Info().Int("count", maintenanceCount).Msg("deleted maintenance entries during wipe inventory")
deleted += maintenanceCount
}
}
// Get all items for the group
items, err := e.db.Item.Query().
Where(item.HasGroupWith(group.ID(gid))).
@@ -819,7 +835,6 @@ func (e *ItemsRepository) WipeInventory(ctx context.Context, gid uuid.UUID, wipe
return 0, err
}
deleted := 0
// Delete each item with its attachments
// Note: We manually delete attachments and items instead of calling DeleteByGroup
// to continue processing remaining items even if some deletions fail
@@ -872,20 +887,6 @@ func (e *ItemsRepository) WipeInventory(ctx context.Context, gid uuid.UUID, wipe
}
}
// Wipe maintenance records if requested
if wipeMaintenance {
// Maintenance entries are linked to items, so we query by items in the group
maintenanceCount, err := e.db.MaintenanceEntry.Delete().
Where(maintenanceentry.HasItemWith(item.HasGroupWith(group.ID(gid)))).
Exec(ctx)
if err != nil {
log.Err(err).Msg("failed to delete maintenance entries during wipe inventory")
} else {
log.Info().Int("count", maintenanceCount).Msg("deleted maintenance entries during wipe inventory")
deleted += maintenanceCount
}
}
e.publishMutationEvent(gid)
return deleted, nil
}

View File

@@ -398,4 +398,161 @@ func TestItemsRepository_DeleteByGroupWithAttachments(t *testing.T) {
require.Error(t, err)
}
func TestItemsRepository_WipeInventory(t *testing.T) {
// Create test data: items, labels, locations, and maintenance entries
// Create locations
loc1, err := tRepos.Locations.Create(context.Background(), tGroup.ID, LocationCreate{
Name: "Test Location 1",
Description: "Test location for wipe test",
})
require.NoError(t, err)
loc2, err := tRepos.Locations.Create(context.Background(), tGroup.ID, LocationCreate{
Name: "Test Location 2",
Description: "Another test location",
})
require.NoError(t, err)
// Create labels
label1, err := tRepos.Labels.Create(context.Background(), tGroup.ID, LabelCreate{
Name: "Test Label 1",
Description: "Test label for wipe test",
})
require.NoError(t, err)
label2, err := tRepos.Labels.Create(context.Background(), tGroup.ID, LabelCreate{
Name: "Test Label 2",
Description: "Another test label",
})
require.NoError(t, err)
// Create items
item1, err := tRepos.Items.Create(context.Background(), tGroup.ID, ItemCreate{
Name: "Test Item 1",
Description: "Test item for wipe test",
LocationID: loc1.ID,
LabelIDs: []uuid.UUID{label1.ID},
})
require.NoError(t, err)
item2, err := tRepos.Items.Create(context.Background(), tGroup.ID, ItemCreate{
Name: "Test Item 2",
Description: "Another test item",
LocationID: loc2.ID,
LabelIDs: []uuid.UUID{label2.ID},
})
require.NoError(t, err)
// Create maintenance entries for items
_, err = tRepos.MaintEntry.Create(context.Background(), item1.ID, MaintenanceEntryCreate{
CompletedDate: types.DateFromTime(time.Now()),
Name: "Test Maintenance 1",
Description: "Test maintenance entry",
Cost: 100.0,
})
require.NoError(t, err)
_, err = tRepos.MaintEntry.Create(context.Background(), item2.ID, MaintenanceEntryCreate{
CompletedDate: types.DateFromTime(time.Now()),
Name: "Test Maintenance 2",
Description: "Another test maintenance entry",
Cost: 200.0,
})
require.NoError(t, err)
// Test 1: Wipe inventory with all options enabled
t.Run("wipe all including labels, locations, and maintenance", func(t *testing.T) {
deleted, err := tRepos.Items.WipeInventory(context.Background(), tGroup.ID, true, true, true)
require.NoError(t, err)
assert.Greater(t, deleted, 0, "Should have deleted at least some entities")
// Verify items are deleted
_, err = tRepos.Items.GetOneByGroup(context.Background(), tGroup.ID, item1.ID)
require.Error(t, err, "Item 1 should be deleted")
_, err = tRepos.Items.GetOneByGroup(context.Background(), tGroup.ID, item2.ID)
require.Error(t, err, "Item 2 should be deleted")
// Verify maintenance entries are deleted (query by item ID, should return empty)
maint1List, err := tRepos.MaintEntry.GetMaintenanceByItemID(context.Background(), tGroup.ID, item1.ID, MaintenanceFilters{})
require.NoError(t, err)
assert.Empty(t, maint1List, "Maintenance entry 1 should be deleted")
maint2List, err := tRepos.MaintEntry.GetMaintenanceByItemID(context.Background(), tGroup.ID, item2.ID, MaintenanceFilters{})
require.NoError(t, err)
assert.Empty(t, maint2List, "Maintenance entry 2 should be deleted")
// Verify labels are deleted
_, err = tRepos.Labels.GetOneByGroup(context.Background(), tGroup.ID, label1.ID)
require.Error(t, err, "Label 1 should be deleted")
_, err = tRepos.Labels.GetOneByGroup(context.Background(), tGroup.ID, label2.ID)
require.Error(t, err, "Label 2 should be deleted")
// Verify locations are deleted
_, err = tRepos.Locations.Get(context.Background(), loc1.ID)
require.Error(t, err, "Location 1 should be deleted")
_, err = tRepos.Locations.Get(context.Background(), loc2.ID)
require.Error(t, err, "Location 2 should be deleted")
})
}
func TestItemsRepository_WipeInventory_OnlyItems(t *testing.T) {
// Create test data
loc, err := tRepos.Locations.Create(context.Background(), tGroup.ID, LocationCreate{
Name: "Test Location",
Description: "Test location for wipe test",
})
require.NoError(t, err)
label, err := tRepos.Labels.Create(context.Background(), tGroup.ID, LabelCreate{
Name: "Test Label",
Description: "Test label for wipe test",
})
require.NoError(t, err)
item, err := tRepos.Items.Create(context.Background(), tGroup.ID, ItemCreate{
Name: "Test Item",
Description: "Test item for wipe test",
LocationID: loc.ID,
LabelIDs: []uuid.UUID{label.ID},
})
require.NoError(t, err)
_, err = tRepos.MaintEntry.Create(context.Background(), item.ID, MaintenanceEntryCreate{
CompletedDate: types.DateFromTime(time.Now()),
Name: "Test Maintenance",
Description: "Test maintenance entry",
Cost: 100.0,
})
require.NoError(t, err)
// Test: Wipe inventory with only items (no labels, locations, or maintenance)
deleted, err := tRepos.Items.WipeInventory(context.Background(), tGroup.ID, false, false, false)
require.NoError(t, err)
assert.Greater(t, deleted, 0, "Should have deleted at least the item")
// Verify item is deleted
_, err = tRepos.Items.GetOneByGroup(context.Background(), tGroup.ID, item.ID)
require.Error(t, err, "Item should be deleted")
// Verify maintenance entry is deleted due to cascade
maintList, err := tRepos.MaintEntry.GetMaintenanceByItemID(context.Background(), tGroup.ID, item.ID, MaintenanceFilters{})
require.NoError(t, err)
assert.Empty(t, maintList, "Maintenance entry should be cascade deleted with item")
// Verify label still exists
_, err = tRepos.Labels.GetOneByGroup(context.Background(), tGroup.ID, label.ID)
require.NoError(t, err, "Label should still exist")
// Verify location still exists
_, err = tRepos.Locations.Get(context.Background(), loc.ID)
require.NoError(t, err, "Location should still exist")
// Cleanup
_ = tRepos.Labels.DeleteByGroup(context.Background(), tGroup.ID, label.ID)
_ = tRepos.Locations.delete(context.Background(), loc.ID)
}

View File

@@ -0,0 +1,194 @@
package repo
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/sysadminsmedia/homebox/backend/internal/data/types"
)
// TestWipeInventory_Integration tests the complete wipe inventory flow
func TestWipeInventory_Integration(t *testing.T) {
// Create test data: locations, labels, items with maintenance
// 1. Create locations
loc1, err := tRepos.Locations.Create(context.Background(), tGroup.ID, LocationCreate{
Name: "Test Garage",
Description: "Garage location",
})
require.NoError(t, err)
loc2, err := tRepos.Locations.Create(context.Background(), tGroup.ID, LocationCreate{
Name: "Test Basement",
Description: "Basement location",
})
require.NoError(t, err)
// 2. Create labels
label1, err := tRepos.Labels.Create(context.Background(), tGroup.ID, LabelCreate{
Name: "Test Electronics",
Description: "Electronics label",
})
require.NoError(t, err)
label2, err := tRepos.Labels.Create(context.Background(), tGroup.ID, LabelCreate{
Name: "Test Tools",
Description: "Tools label",
})
require.NoError(t, err)
// 3. Create items
item1, err := tRepos.Items.Create(context.Background(), tGroup.ID, ItemCreate{
Name: "Test Laptop",
Description: "Work laptop",
LocationID: loc1.ID,
LabelIDs: []uuid.UUID{label1.ID},
})
require.NoError(t, err)
item2, err := tRepos.Items.Create(context.Background(), tGroup.ID, ItemCreate{
Name: "Test Drill",
Description: "Power drill",
LocationID: loc2.ID,
LabelIDs: []uuid.UUID{label2.ID},
})
require.NoError(t, err)
item3, err := tRepos.Items.Create(context.Background(), tGroup.ID, ItemCreate{
Name: "Test Monitor",
Description: "Computer monitor",
LocationID: loc1.ID,
LabelIDs: []uuid.UUID{label1.ID},
})
require.NoError(t, err)
// 4. Create maintenance entries
_, err = tRepos.MaintEntry.Create(context.Background(), item1.ID, MaintenanceEntryCreate{
CompletedDate: types.DateFromTime(time.Now()),
Name: "Laptop cleaning",
Description: "Cleaned keyboard and screen",
Cost: 0,
})
require.NoError(t, err)
_, err = tRepos.MaintEntry.Create(context.Background(), item2.ID, MaintenanceEntryCreate{
CompletedDate: types.DateFromTime(time.Now()),
Name: "Drill maintenance",
Description: "Oiled motor",
Cost: 5.00,
})
require.NoError(t, err)
_, err = tRepos.MaintEntry.Create(context.Background(), item3.ID, MaintenanceEntryCreate{
CompletedDate: types.DateFromTime(time.Now()),
Name: "Monitor calibration",
Description: "Color calibration",
Cost: 0,
})
require.NoError(t, err)
// 5. Verify items exist
allItems, err := tRepos.Items.GetAll(context.Background(), tGroup.ID)
require.NoError(t, err)
assert.GreaterOrEqual(t, len(allItems), 3, "Should have at least 3 items")
// 6. Verify maintenance entries exist
maint1List, err := tRepos.MaintEntry.GetMaintenanceByItemID(context.Background(), tGroup.ID, item1.ID, MaintenanceFilters{})
require.NoError(t, err)
assert.NotEmpty(t, maint1List, "Item 1 should have maintenance records")
maint2List, err := tRepos.MaintEntry.GetMaintenanceByItemID(context.Background(), tGroup.ID, item2.ID, MaintenanceFilters{})
require.NoError(t, err)
assert.NotEmpty(t, maint2List, "Item 2 should have maintenance records")
// 7. Test wipe inventory with all options enabled
deleted, err := tRepos.Items.WipeInventory(context.Background(), tGroup.ID, true, true, true)
require.NoError(t, err)
assert.Greater(t, deleted, 0, "Should have deleted entities")
// 8. Verify all items are deleted
allItemsAfter, err := tRepos.Items.GetAll(context.Background(), tGroup.ID)
require.NoError(t, err)
assert.Equal(t, 0, len(allItemsAfter), "All items should be deleted")
// 9. Verify maintenance entries are deleted
maint1After, err := tRepos.MaintEntry.GetMaintenanceByItemID(context.Background(), tGroup.ID, item1.ID, MaintenanceFilters{})
require.NoError(t, err)
assert.Empty(t, maint1After, "Item 1 maintenance records should be deleted")
// 10. Verify labels are deleted
_, err = tRepos.Labels.GetOneByGroup(context.Background(), tGroup.ID, label1.ID)
require.Error(t, err, "Label 1 should be deleted")
_, err = tRepos.Labels.GetOneByGroup(context.Background(), tGroup.ID, label2.ID)
require.Error(t, err, "Label 2 should be deleted")
// 11. Verify locations are deleted
_, err = tRepos.Locations.Get(context.Background(), loc1.ID)
require.Error(t, err, "Location 1 should be deleted")
_, err = tRepos.Locations.Get(context.Background(), loc2.ID)
require.Error(t, err, "Location 2 should be deleted")
}
// TestWipeInventory_SelectiveWipe tests wiping only certain entity types
func TestWipeInventory_SelectiveWipe(t *testing.T) {
// Create test data
loc, err := tRepos.Locations.Create(context.Background(), tGroup.ID, LocationCreate{
Name: "Test Office",
Description: "Office location",
})
require.NoError(t, err)
label, err := tRepos.Labels.Create(context.Background(), tGroup.ID, LabelCreate{
Name: "Test Important",
Description: "Important label",
})
require.NoError(t, err)
item, err := tRepos.Items.Create(context.Background(), tGroup.ID, ItemCreate{
Name: "Test Computer",
Description: "Desktop computer",
LocationID: loc.ID,
LabelIDs: []uuid.UUID{label.ID},
})
require.NoError(t, err)
_, err = tRepos.MaintEntry.Create(context.Background(), item.ID, MaintenanceEntryCreate{
CompletedDate: types.DateFromTime(time.Now()),
Name: "System update",
Description: "OS update",
Cost: 0,
})
require.NoError(t, err)
// Test: Wipe only items (keep labels and locations)
deleted, err := tRepos.Items.WipeInventory(context.Background(), tGroup.ID, false, false, false)
require.NoError(t, err)
assert.Greater(t, deleted, 0, "Should have deleted at least items")
// Verify item is deleted
_, err = tRepos.Items.GetOneByGroup(context.Background(), tGroup.ID, item.ID)
require.Error(t, err, "Item should be deleted")
// Verify maintenance is cascade deleted
maintList, err := tRepos.MaintEntry.GetMaintenanceByItemID(context.Background(), tGroup.ID, item.ID, MaintenanceFilters{})
require.NoError(t, err)
assert.Empty(t, maintList, "Maintenance should be cascade deleted")
// Verify label still exists
_, err = tRepos.Labels.GetOneByGroup(context.Background(), tGroup.ID, label.ID)
require.NoError(t, err, "Label should still exist")
// Verify location still exists
_, err = tRepos.Locations.Get(context.Background(), loc.ID)
require.NoError(t, err, "Location should still exist")
// Cleanup
_ = tRepos.Labels.DeleteByGroup(context.Background(), tGroup.ID, label.ID)
_ = tRepos.Locations.delete(context.Background(), loc.ID)
}

View File

@@ -83,24 +83,12 @@
const wipeLocations = ref(false);
const wipeMaintenance = ref(false);
let onCloseCallback:
| ((result?: { wipeLabels: boolean; wipeLocations: boolean; wipeMaintenance: boolean } | undefined) => void)
| undefined;
registerOpenDialogCallback(
DialogID.WipeInventory,
(params?: {
onClose?: (
result?: { wipeLabels: boolean; wipeLocations: boolean; wipeMaintenance: boolean } | undefined
) => void;
}) => {
dialog.value = true;
wipeLabels.value = false;
wipeLocations.value = false;
wipeMaintenance.value = false;
onCloseCallback = params?.onClose;
}
);
registerOpenDialogCallback(DialogID.WipeInventory, () => {
dialog.value = true;
wipeLabels.value = false;
wipeLocations.value = false;
wipeMaintenance.value = false;
});
watch(
dialog,
@@ -123,7 +111,6 @@
function close() {
dialog.value = false;
closeDialog(DialogID.WipeInventory, undefined);
onCloseCallback?.(undefined);
}
function confirm() {
@@ -134,6 +121,5 @@
wipeMaintenance: wipeMaintenance.value,
};
closeDialog(DialogID.WipeInventory, result);
onCloseCallback?.(result);
}
</script>