Files
ai-wave-02/apps/api/src/orders/order-repository.ts
T
Jeighdy Tanamal bbf6a90e18
Runner Test / test (pull_request) Successful in 15s
Runner Test / test (push) Successful in 15s
Feat added internal notes
2026-09-24 15:54:28 -07:00

214 lines
6.6 KiB
TypeScript

import type { Address, AuditEvent, OrderDetail, OrderItemDetail, OrderSummary } from "@orderops/shared";
import { and, asc, desc, eq, inArray, or, sql } from "drizzle-orm";
import type { DatabaseContext } from "../db/database";
import { addresses, auditEvents, inventory, orderItems, orders, products, users } from "../db/schema";
function toAddress(row: typeof addresses.$inferSelect): Address {
return {
fullName: row.fullName,
line1: row.line1,
line2: row.line2,
city: row.city,
state: row.state,
postalCode: row.postalCode,
country: row.country
};
}
function toOrderItemDetail(row: {
item: typeof orderItems.$inferSelect;
product: typeof products.$inferSelect;
inventory: typeof inventory.$inferSelect | null;
}): OrderItemDetail {
return {
id: row.item.id,
productId: row.product.id,
sku: row.product.sku,
productName: row.product.name,
quantity: row.item.quantity,
unitPrice: {
amountCents: row.item.unitPriceCents,
currency: "USD"
},
availableQuantity: row.inventory?.availableQuantity ?? 0
};
}
export async function listOrderSummaries(context: DatabaseContext, query?: string) {
const search = query?.trim().toLowerCase();
const filters = search
? or(
sql`instr(lower(${orders.orderNumber}), ${search}) > 0`,
sql`instr(lower(${users.name}), ${search}) > 0`,
sql`instr(lower(${users.email}), ${search}) > 0`
)
: undefined;
const rows = await context.db
.select({
order: orders,
customer: users,
itemCount: sql<number>`count(${orderItems.id})`
})
.from(orders)
.innerJoin(users, eq(orders.customerId, users.id))
.leftJoin(orderItems, eq(orderItems.orderId, orders.id))
.where(filters)
.groupBy(orders.id, users.id)
.orderBy(desc(orders.createdAt));
return rows.map(({ order, customer, itemCount }) => ({
id: order.id,
orderNumber: order.orderNumber,
status: order.status as OrderSummary["status"],
customer: {
id: customer.id,
name: customer.name,
email: customer.email
},
itemCount,
createdAt: order.createdAt
})) satisfies OrderSummary[];
}
export async function findOrderDetail(context: DatabaseContext, orderId: string) {
const [header] = await context.db
.select({ order: orders, customer: users, address: addresses })
.from(orders)
.innerJoin(users, eq(orders.customerId, users.id))
.innerJoin(addresses, eq(orders.shippingAddressId, addresses.id))
.where(eq(orders.id, orderId));
if (!header) {
return null;
}
const itemRows = await context.db
.select({ item: orderItems, product: products, inventory })
.from(orderItems)
.innerJoin(products, eq(orderItems.productId, products.id))
.leftJoin(inventory, eq(orderItems.productId, inventory.productId))
.where(eq(orderItems.orderId, orderId))
.orderBy(asc(orderItems.id));
return {
id: header.order.id,
orderNumber: header.order.orderNumber,
status: header.order.status as OrderDetail["status"],
notes: header.order.notes,
createdAt: header.order.createdAt,
updatedAt: header.order.updatedAt,
customer: {
id: header.customer.id,
name: header.customer.name,
email: header.customer.email
},
shippingAddress: toAddress(header.address),
items: itemRows.map(toOrderItemDetail)
} satisfies OrderDetail;
}
export async function findOrderItemRecord(context: DatabaseContext, orderId: string, itemId: string) {
const [row] = await context.db
.select({ order: orders, item: orderItems, inventory })
.from(orderItems)
.innerJoin(orders, eq(orderItems.orderId, orders.id))
.leftJoin(inventory, eq(orderItems.productId, inventory.productId))
.where(and(eq(orderItems.id, itemId), eq(orderItems.orderId, orderId)));
return row ?? null;
}
export async function updateOrderItemQuantity(context: DatabaseContext, itemId: string, quantity: number) {
await context.db.update(orderItems).set({ quantity }).where(eq(orderItems.id, itemId));
}
export async function updateOrderTimestamp(context: DatabaseContext, orderId: string, updatedAt: string) {
await context.db.update(orders).set({ updatedAt }).where(eq(orders.id, orderId));
}
export async function updateOrderNotes(context: DatabaseContext, orderId: string, notes: string, updatedAt: string) {
await context.db.update(orders).set({ notes, updatedAt }).where(eq(orders.id, orderId));
}
export async function updateOrderStatus(context: DatabaseContext, orderId: string, status: OrderDetail["status"], updatedAt: string) {
await context.db.update(orders).set({ status, updatedAt }).where(eq(orders.id, orderId));
}
export async function updateOrderAddress(context: DatabaseContext, orderId: string, address: Address) {
const [row] = await context.db.select({ shippingAddressId: orders.shippingAddressId }).from(orders).where(eq(orders.id, orderId));
if (!row) {
return false;
}
await context.db
.update(addresses)
.set({
fullName: address.fullName,
line1: address.line1,
line2: address.line2,
city: address.city,
state: address.state,
postalCode: address.postalCode,
country: address.country
})
.where(eq(addresses.id, row.shippingAddressId));
return true;
}
export async function appendAuditEvent(
context: DatabaseContext,
event: {
orderId: string;
actor: string;
type: string;
message: string;
createdAt: string;
}
) {
await context.db.insert(auditEvents).values({
id: crypto.randomUUID(),
orderId: event.orderId,
actor: event.actor,
type: event.type,
message: event.message,
createdAt: event.createdAt
});
}
async function findCustomerSummaryByOrderId(context: DatabaseContext, orderId: string) {
const [row] = await context.db
.select({ customer: users })
.from(orders)
.innerJoin(users, eq(orders.customerId, users.id))
.where(eq(orders.id, orderId));
return row?.customer ?? null;
}
export async function listOrderAuditEvents(context: DatabaseContext, orderId: string) {
const events = await context.db.select().from(auditEvents).where(eq(auditEvents.orderId, orderId)).orderBy(desc(auditEvents.createdAt));
return Promise.all(
events.map(async (event) => {
await findCustomerSummaryByOrderId(context, event.orderId);
return {
id: event.id,
orderId: event.orderId,
type: event.type,
message: event.message,
createdAt: event.createdAt,
actor: event.actor
} satisfies AuditEvent;
})
);
}
export async function listOrdersByIds(context: DatabaseContext, orderIds: string[]) {
return context.db.select().from(orders).where(inArray(orders.id, orderIds));
}