Skip to content

Commit 62679c6

Browse files
committed
feat: Enhance checkout update logic to remove items no longer in the checkout
1 parent 9b6cb70 commit 62679c6

2 files changed

Lines changed: 246 additions & 1 deletion

File tree

internal/infrastructure/repository/gorm/checkout_repository.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,33 @@ func (c *CheckoutRepository) HasActiveCheckoutsWithProduct(productID uint) (bool
220220

221221
// Update implements repository.CheckoutRepository.
222222
func (c *CheckoutRepository) Update(checkout *entity.Checkout) error {
223-
return c.db.Session(&gorm.Session{FullSaveAssociations: true}).Save(checkout).Error
223+
return c.db.Transaction(func(tx *gorm.DB) error {
224+
// First, get the current items in the database
225+
var currentItems []entity.CheckoutItem
226+
if err := tx.Where("checkout_id = ?", checkout.ID).Find(&currentItems).Error; err != nil {
227+
return fmt.Errorf("failed to fetch current checkout items: %w", err)
228+
}
229+
230+
// Create a map of current item IDs in the checkout entity for efficient lookup
231+
currentItemIDs := make(map[uint]bool)
232+
for _, item := range checkout.Items {
233+
if item.ID != 0 {
234+
currentItemIDs[item.ID] = true
235+
}
236+
}
237+
238+
// Delete items that are no longer in the checkout
239+
for _, dbItem := range currentItems {
240+
if !currentItemIDs[dbItem.ID] {
241+
if err := tx.Unscoped().Delete(&dbItem).Error; err != nil {
242+
return fmt.Errorf("failed to delete checkout item %d: %w", dbItem.ID, err)
243+
}
244+
}
245+
}
246+
247+
// Save the checkout with remaining items
248+
return tx.Session(&gorm.Session{FullSaveAssociations: true}).Save(checkout).Error
249+
})
224250
}
225251

226252
// NewCheckoutRepository creates a new GORM-based CheckoutRepository
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
package gorm
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/zenfulcode/commercify/internal/domain/entity"
10+
"github.com/zenfulcode/commercify/testutil"
11+
)
12+
13+
func TestCheckoutRepository_Update_RemoveItems(t *testing.T) {
14+
db := testutil.SetupTestDB(t)
15+
checkoutRepo := NewCheckoutRepository(db)
16+
17+
t.Run("Remove item should delete from database", func(t *testing.T) {
18+
// Create a checkout with items
19+
checkout, err := entity.NewCheckout("session123", "USD")
20+
require.NoError(t, err)
21+
22+
// Add multiple items
23+
err = checkout.AddItem(1, 1, 2, 1000, 1.5, "Product 1", "Variant 1", "SKU-001")
24+
require.NoError(t, err)
25+
err = checkout.AddItem(2, 2, 1, 2000, 2.0, "Product 2", "Variant 2", "SKU-002")
26+
require.NoError(t, err)
27+
err = checkout.AddItem(3, 3, 3, 3000, 3.0, "Product 3", "Variant 3", "SKU-003")
28+
require.NoError(t, err)
29+
30+
// Create checkout in database
31+
err = checkoutRepo.Create(checkout)
32+
require.NoError(t, err)
33+
require.NotZero(t, checkout.ID)
34+
35+
// Verify all items were created
36+
retrievedCheckout, err := checkoutRepo.GetByID(checkout.ID)
37+
require.NoError(t, err)
38+
require.Len(t, retrievedCheckout.Items, 3)
39+
40+
// Store item IDs for verification
41+
var itemIDs []uint
42+
for _, item := range retrievedCheckout.Items {
43+
itemIDs = append(itemIDs, item.ID)
44+
}
45+
46+
// Remove one item from the checkout entity
47+
err = retrievedCheckout.RemoveItem(2, 2) // Remove Product 2
48+
require.NoError(t, err)
49+
require.Len(t, retrievedCheckout.Items, 2)
50+
51+
// Update checkout in database
52+
err = checkoutRepo.Update(retrievedCheckout)
53+
require.NoError(t, err)
54+
55+
// Verify the checkout now has only 2 items
56+
updatedCheckout, err := checkoutRepo.GetByID(checkout.ID)
57+
require.NoError(t, err)
58+
require.Len(t, updatedCheckout.Items, 2)
59+
60+
// Verify the correct item was removed (Product 2 should be gone)
61+
productIDs := make(map[uint]bool)
62+
for _, item := range updatedCheckout.Items {
63+
productIDs[item.ProductID] = true
64+
}
65+
assert.True(t, productIDs[1], "Product 1 should still exist")
66+
assert.False(t, productIDs[2], "Product 2 should be removed")
67+
assert.True(t, productIDs[3], "Product 3 should still exist")
68+
69+
// Verify the item was actually deleted from the database
70+
var itemCount int64
71+
err = db.Model(&entity.CheckoutItem{}).Where("checkout_id = ?", checkout.ID).Count(&itemCount).Error
72+
require.NoError(t, err)
73+
assert.Equal(t, int64(2), itemCount, "Should have exactly 2 items in database")
74+
75+
// Verify the specific item with Product ID 2 is deleted
76+
var deletedItemCount int64
77+
err = db.Model(&entity.CheckoutItem{}).Where("checkout_id = ? AND product_id = ?", checkout.ID, 2).Count(&deletedItemCount).Error
78+
require.NoError(t, err)
79+
assert.Equal(t, int64(0), deletedItemCount, "Product 2 item should be deleted from database")
80+
})
81+
82+
t.Run("Remove multiple items should delete all from database", func(t *testing.T) {
83+
// Create a checkout with items
84+
checkout, err := entity.NewCheckout("session456", "USD")
85+
require.NoError(t, err)
86+
87+
// Add multiple items
88+
err = checkout.AddItem(1, 1, 2, 1000, 1.5, "Product 1", "Variant 1", "SKU-001")
89+
require.NoError(t, err)
90+
err = checkout.AddItem(2, 2, 1, 2000, 2.0, "Product 2", "Variant 2", "SKU-002")
91+
require.NoError(t, err)
92+
err = checkout.AddItem(3, 3, 3, 3000, 3.0, "Product 3", "Variant 3", "SKU-003")
93+
require.NoError(t, err)
94+
95+
// Create checkout in database
96+
err = checkoutRepo.Create(checkout)
97+
require.NoError(t, err)
98+
require.NotZero(t, checkout.ID)
99+
100+
// Get the checkout and remove multiple items
101+
retrievedCheckout, err := checkoutRepo.GetByID(checkout.ID)
102+
require.NoError(t, err)
103+
require.Len(t, retrievedCheckout.Items, 3)
104+
105+
// Remove two items
106+
err = retrievedCheckout.RemoveItem(1, 1) // Remove Product 1
107+
require.NoError(t, err)
108+
err = retrievedCheckout.RemoveItem(3, 3) // Remove Product 3
109+
require.NoError(t, err)
110+
require.Len(t, retrievedCheckout.Items, 1)
111+
112+
// Update checkout in database
113+
err = checkoutRepo.Update(retrievedCheckout)
114+
require.NoError(t, err)
115+
116+
// Verify the checkout now has only 1 item
117+
updatedCheckout, err := checkoutRepo.GetByID(checkout.ID)
118+
require.NoError(t, err)
119+
require.Len(t, updatedCheckout.Items, 1)
120+
121+
// Verify only Product 2 remains
122+
assert.Equal(t, uint(2), updatedCheckout.Items[0].ProductID)
123+
124+
// Verify the correct count in database
125+
var itemCount int64
126+
err = db.Model(&entity.CheckoutItem{}).Where("checkout_id = ?", checkout.ID).Count(&itemCount).Error
127+
require.NoError(t, err)
128+
assert.Equal(t, int64(1), itemCount, "Should have exactly 1 item in database")
129+
})
130+
131+
t.Run("Remove all items should clear database", func(t *testing.T) {
132+
// Create a checkout with items
133+
checkout, err := entity.NewCheckout("session789", "USD")
134+
require.NoError(t, err)
135+
136+
// Add items
137+
err = checkout.AddItem(1, 1, 2, 1000, 1.5, "Product 1", "Variant 1", "SKU-001")
138+
require.NoError(t, err)
139+
err = checkout.AddItem(2, 2, 1, 2000, 2.0, "Product 2", "Variant 2", "SKU-002")
140+
require.NoError(t, err)
141+
142+
// Create checkout in database
143+
err = checkoutRepo.Create(checkout)
144+
require.NoError(t, err)
145+
146+
// Get the checkout and clear all items
147+
retrievedCheckout, err := checkoutRepo.GetByID(checkout.ID)
148+
require.NoError(t, err)
149+
150+
// Clear all items using the Clear method
151+
retrievedCheckout.Clear()
152+
require.Len(t, retrievedCheckout.Items, 0)
153+
154+
// Update checkout in database
155+
err = checkoutRepo.Update(retrievedCheckout)
156+
require.NoError(t, err)
157+
158+
// Verify the checkout has no items
159+
updatedCheckout, err := checkoutRepo.GetByID(checkout.ID)
160+
require.NoError(t, err)
161+
require.Len(t, updatedCheckout.Items, 0)
162+
163+
// Verify no items exist in database for this checkout
164+
var itemCount int64
165+
err = db.Model(&entity.CheckoutItem{}).Where("checkout_id = ?", checkout.ID).Count(&itemCount).Error
166+
require.NoError(t, err)
167+
assert.Equal(t, int64(0), itemCount, "Should have no items in database")
168+
})
169+
170+
t.Run("Update existing items without removal should work", func(t *testing.T) {
171+
// Create a checkout with items
172+
checkout, err := entity.NewCheckout("session999", "USD")
173+
require.NoError(t, err)
174+
175+
// Add items
176+
err = checkout.AddItem(1, 1, 2, 1000, 1.5, "Product 1", "Variant 1", "SKU-001")
177+
require.NoError(t, err)
178+
err = checkout.AddItem(2, 2, 1, 2000, 2.0, "Product 2", "Variant 2", "SKU-002")
179+
require.NoError(t, err)
180+
181+
// Create checkout in database
182+
err = checkoutRepo.Create(checkout)
183+
require.NoError(t, err)
184+
185+
// Get the checkout and update an item quantity
186+
retrievedCheckout, err := checkoutRepo.GetByID(checkout.ID)
187+
require.NoError(t, err)
188+
189+
// Update item quantity
190+
err = retrievedCheckout.UpdateItem(1, 1, 5) // Change quantity from 2 to 5
191+
require.NoError(t, err)
192+
193+
// Update checkout in database
194+
err = checkoutRepo.Update(retrievedCheckout)
195+
require.NoError(t, err)
196+
197+
// Verify the checkout still has 2 items with updated quantity
198+
updatedCheckout, err := checkoutRepo.GetByID(checkout.ID)
199+
require.NoError(t, err)
200+
require.Len(t, updatedCheckout.Items, 2)
201+
202+
// Find the updated item
203+
var updatedItem *entity.CheckoutItem
204+
for _, item := range updatedCheckout.Items {
205+
if item.ProductID == 1 {
206+
updatedItem = &item
207+
break
208+
}
209+
}
210+
require.NotNil(t, updatedItem)
211+
assert.Equal(t, 5, updatedItem.Quantity)
212+
213+
// Verify count in database remains the same
214+
var itemCount int64
215+
err = db.Model(&entity.CheckoutItem{}).Where("checkout_id = ?", checkout.ID).Count(&itemCount).Error
216+
require.NoError(t, err)
217+
assert.Equal(t, int64(2), itemCount, "Should still have 2 items in database")
218+
})
219+
}

0 commit comments

Comments
 (0)