From 4b90472b098b17c97cd9bd8bcf420a77c9fe74be Mon Sep 17 00:00:00 2001 From: chanwoo7 Date: Wed, 19 Aug 2026 06:18:47 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=A6=B4=EB=A6=AC=EC=A6=88=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81=20(capacity=20=EC=86=8C=EC=A7=84?= =?UTF-8?q?=EC=9D=84=20=EC=A0=9C=EC=9E=91=20=EC=88=98=EB=9F=89=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EC=A7=91=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StoreDailyCapacity는 '일별 생산 가능 수량'(seller SDL 정의)이므로 소진 판정을 주문 distinct 수가 아닌 아이템 quantity 합으로 변경 (sumPickupQuantitiesInRange). CANCELED·soft-delete 제외는 동일. - 회귀 테스트 1건 추가(주문 1건 quantity=2 → capacity 2 소진). --- .../store/repositories/store.repository.ts | 13 ++++----- .../store-today-pickup.service.spec.ts | 27 +++++++++++++++++-- .../services/store-today-pickup.service.ts | 2 +- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/features/store/repositories/store.repository.ts b/src/features/store/repositories/store.repository.ts index 0fd213b..2831c35 100644 --- a/src/features/store/repositories/store.repository.ts +++ b/src/features/store/repositories/store.repository.ts @@ -133,19 +133,20 @@ export class StoreRepository { } /** - * 픽업 시각이 [rangeStart, rangeEnd)인 매장별 유효 주문 수(주문 단위 distinct). - * capacity 소진 판정용 — CANCELED·soft-delete 주문은 제외한다. + * 픽업 시각이 [rangeStart, rangeEnd)인 매장별 예약 제작 수량(아이템 quantity 합). + * capacity(일별 생산 가능 '수량') 소진 판정용 — CANCELED·soft-delete 주문은 제외한다. */ - async countPickupOrdersInRange( + async sumPickupQuantitiesInRange( storeIds: bigint[], rangeStart: Date, rangeEnd: Date, ): Promise> { if (storeIds.length === 0) return new Map(); const rows = await this.prisma.$queryRaw< - { store_id: bigint; order_count: bigint }[] + { store_id: bigint; booked_quantity: bigint }[] >(Prisma.sql` - SELECT oi.store_id AS store_id, COUNT(DISTINCT oi.order_id) AS order_count + SELECT oi.store_id AS store_id, + CAST(COALESCE(SUM(oi.quantity), 0) AS UNSIGNED) AS booked_quantity FROM order_item oi JOIN \`order\` o ON o.id = oi.order_id @@ -157,7 +158,7 @@ export class StoreRepository { AND oi.deleted_at IS NULL GROUP BY oi.store_id `); - return new Map(rows.map((r) => [r.store_id, Number(r.order_count)])); + return new Map(rows.map((r) => [r.store_id, Number(r.booked_quantity)])); } /** 활성 매장 존재 검증(찜 등). */ diff --git a/src/features/store/services/store-today-pickup.service.spec.ts b/src/features/store/services/store-today-pickup.service.spec.ts index d77bc39..adc5e98 100644 --- a/src/features/store/services/store-today-pickup.service.spec.ts +++ b/src/features/store/services/store-today-pickup.service.spec.ts @@ -73,8 +73,12 @@ describe('StoreTodayPickupService (real DB)', () => { }); } - /** 오늘 픽업 유효 주문 n건 생성. */ - async function bookToday(store: Store, count: number): Promise { + /** 오늘 픽업 유효 주문 n건 생성(주문당 케이크 quantity개). */ + async function bookToday( + store: Store, + count: number, + quantity = 1, + ): Promise { for (let i = 0; i < count; i += 1) { const order = await createOrder(prisma, { status: 'CONFIRMED', @@ -83,6 +87,7 @@ describe('StoreTodayPickupService (real DB)', () => { await createOrderItem(prisma, { order_id: order.id, store_id: store.id, + quantity, }); } } @@ -199,6 +204,24 @@ describe('StoreTodayPickupService (real DB)', () => { expect(result.items.map((i) => i.storeName)).toEqual(['여유매장']); }); + it('capacity는 주문 수가 아니라 제작 수량(quantity 합) 기준으로 소진된다', async () => { + // 주문 1건이라도 quantity=2면 capacity 2를 전부 소진한다 + const store = await createStore(prisma, { store_name: '수량마감매장' }); + await openToday(store, 10, 20); + await prisma.storeDailyCapacity.create({ + data: { + store_id: store.id, + capacity_date: TODAY_DATE_ONLY, + capacity: 2, + }, + }); + await bookToday(store, 1, 2); + + await expect(service.todayPickupStores()).resolves.toMatchObject({ + items: [], + }); + }); + it('capacity 레코드가 없으면 무제한으로 간주한다', async () => { const store = await createStore(prisma, { store_name: '무제한매장' }); await openToday(store, 10, 20); diff --git a/src/features/store/services/store-today-pickup.service.ts b/src/features/store/services/store-today-pickup.service.ts index f308e6a..89fb858 100644 --- a/src/features/store/services/store-today-pickup.service.ts +++ b/src/features/store/services/store-today-pickup.service.ts @@ -62,7 +62,7 @@ export class StoreTodayPickupService { this.repo.findBusinessHoursByWeekday(storeIds, weekday), this.repo.findSpecialClosureStoreIds(storeIds, dateOnlyUtc), this.repo.findDailyCapacities(storeIds, dateOnlyUtc), - this.repo.countPickupOrdersInRange(storeIds, dayStartUtc, dayEndUtc), + this.repo.sumPickupQuantitiesInRange(storeIds, dayStartUtc, dayEndUtc), ]); const hourByStore = new Map( businessHours.map((h) => [h.store_id.toString(), h]),