镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Commit 60f6040

Browse files
fix(dashboard): Orders reads the customers and coupons its orders reference
The Orders endpoint read the first 500 customers by name and the first 500 coupons by id, and normalizeCommerceOrderRecord refuses an order whose customer or coupon is not among what it was given. So with more than 500 customers, one order from a customer past the 500th failed the whole page, and the same for coupons. The action now reads exactly the customers and coupons its orders reference, by id (`referencedIds`). The order dialog's customer choices keep their own read, which the next slice replaces with a search. The test shows both halves: an order whose customer is the 704th normalizes from the referenced read, and is refused without it. Live with every feature on (STACKS_CANONICAL_FEATURES=1, no config change), Orders answers 200 with all 20 dev orders and the same summary as before. Refs #3218
1 parent 7702642 commit 60f6040

3 files changed

Lines changed: 59 additions & 4 deletions

File tree

‎storage/framework/defaults/app/Actions/Dashboard/Commerce/CommerceOrdersAction.ts‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
normalizeOrderCouponId,
99
normalizeOrderCustomerContext,
1010
normalizeOrderCustomerOption,
11+
referencedIds,
1112
summarizeCommerceOrders,
1213
} from './commerce-order-records'
1314
import { commerceCurrency, commerceIdentifier, commerceValue } from './commerce-record'
@@ -25,12 +26,18 @@ export default new Action({
2526
commerceIdentifier(commerceValue(order, 'id', 'uuid'), 'Order'),
2627
))
2728
const numericOrderIds = [...orderIds].map(Number).filter(Number.isSafeInteger)
28-
const [customers, coupons, items] = await Promise.all([
29-
Customer.orderBy('name', 'asc').limit(500).get(),
30-
Coupon.orderBy('id', 'asc').limit(500).get(),
29+
// Exactly the customers and coupons these orders reference, so no order
30+
// points at one that was not read (commerce-order-records.ts).
31+
const customerIds = referencedIds(orders, 'customer_id', 'customerId')
32+
const couponReferences = referencedIds(orders, 'coupon_id', 'couponId')
33+
const [referencedCustomers, coupons, items, customers] = await Promise.all([
34+
customerIds.length > 0 ? Customer.whereIn('id', customerIds).get() : [],
35+
couponReferences.length > 0 ? Coupon.whereIn('id', couponReferences).get() : [],
3136
numericOrderIds.length > 0 ? OrderItem.whereIn('order_id', numericOrderIds).get() : [],
37+
// The order dialog's customer choices.
38+
Customer.orderBy('name', 'asc').limit(500).get(),
3239
])
33-
const customerContexts = customers.map(normalizeOrderCustomerContext)
40+
const customerContexts = referencedCustomers.map(normalizeOrderCustomerContext)
3441
const customerMap = new Map(customerContexts.map(customer => [customer.id, customer.context]))
3542
const couponIds = new Set(coupons.map(normalizeOrderCouponId))
3643
const itemCounts = new Map<string, number>()

‎storage/framework/defaults/app/Actions/Dashboard/Commerce/commerce-order-records.test.ts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
normalizeCommerceOrderRecord,
55
normalizeOrderCustomerContext,
66
normalizeOrderCustomerOption,
7+
referencedIds,
78
summarizeCommerceOrders,
89
} from './commerce-order-records'
910

@@ -134,3 +135,31 @@ describe('dashboard commerce order records', () => {
134135
})
135136
})
136137
})
138+
139+
describe('the customers and coupons an order page reads', () => {
140+
test('are exactly the ids the orders reference, once each, in order', () => {
141+
const orders = [
142+
order({ id: 1, customer_id: 704, coupon_id: null }),
143+
order({ id: 2, customer_id: 12, coupon_id: 9 }),
144+
order({ id: 3, customer_id: 704, coupon_id: '9' }),
145+
order({ id: 4, customer_id: null, coupon_id: 3 }),
146+
]
147+
expect(referencedIds(orders, 'customer_id', 'customerId')).toEqual([12, 704])
148+
expect(referencedIds(orders, 'coupon_id', 'couponId')).toEqual([3, 9])
149+
})
150+
151+
test('cover every order, so none is refused for a customer that was not read', () => {
152+
// The 704th customer by name is past any fixed first-500 read. The map
153+
// built from the referenced ids still holds it, so the order normalizes.
154+
const orders = [order({ id: 1, customer_id: 704 })]
155+
const ids = referencedIds(orders, 'customer_id')
156+
const customers = new Map(ids.map(id => [String(id), { name: `Customer ${id}`, email: `c${id}@example.com` }]))
157+
expect(normalizeCommerceOrderRecord(orders[0], customers, new Map()).customerName).toBe('Customer 704')
158+
// And the refusal it prevents is real: without that customer the order is rejected.
159+
expect(() => normalizeCommerceOrderRecord(orders[0], new Map(), new Map())).toThrow('references missing Customer 704')
160+
})
161+
162+
test('ignore empty, zero and non-numeric references', () => {
163+
expect(referencedIds([order({ customer_id: '' }), order({ customer_id: 0 }), order({ customer_id: 'abc' }), order({ customer_id: -4 })], 'customer_id')).toEqual([])
164+
})
165+
})

‎storage/framework/defaults/app/Actions/Dashboard/Commerce/commerce-order-records.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,25 @@ export function normalizeOrderCustomerContext(customer: any): { id: string, cont
169169
}
170170
}
171171

172+
/**
173+
* The ids a set of records points at through one field, as integers, once
174+
* each. Orders are read with exactly the customers and coupons they reference,
175+
* not the first 500 of each: `normalizeCommerceOrderRecord` refuses an order
176+
* whose customer or coupon is missing from what it was given, so with more
177+
* than 500 customers a single order from one past the 500th by name failed the
178+
* whole Orders page.
179+
*/
180+
export function referencedIds(records: unknown[], ...keys: string[]): number[] {
181+
const ids = new Set<number>()
182+
for (const record of records) {
183+
const value = commerceValue(record, ...keys)
184+
const id = Number(value)
185+
if (value !== undefined && value !== null && value !== '' && Number.isSafeInteger(id) && id > 0)
186+
ids.add(id)
187+
}
188+
return [...ids].sort((left, right) => left - right)
189+
}
190+
172191
export function normalizeOrderCouponId(coupon: any): string {
173192
return commerceIdentifier(commerceValue(coupon, 'id', 'uuid'), 'Coupon')
174193
}

0 commit comments

Comments
 (0)