Skip to content

feat: Add Warehouse#12

Open
jeninh wants to merge 151 commits intomainfrom
warehouse-dev
Open

feat: Add Warehouse#12
jeninh wants to merge 151 commits intomainfrom
warehouse-dev

Conversation

@jeninh
Copy link
Copy Markdown
Collaborator

@jeninh jeninh commented Feb 26, 2026

Summary
Adds a complete warehouse management system to Resolution, allowing ambassadors to place orders for physical inventory and admins to fulfill them with integrated multi-carrier shipping label generation.

Features
Warehouse Storefront (Ambassadors - /app/warehouse)
Browse inventory items grouped by category
Multi-step order wizard with address entry, item selection, and real-time shipping rate comparison
Order templates for quick reordering
Batch ordering via CSV upload with auto-field-mapping and a linked Google Sheets template
Tag input with chips and autocomplete for order filtering
Orders and batches scoped to the logged-in user

Warehouse Backend (Admins - /app/warehouse-backend)
Full CRUD for inventory categories and items (photo uploads via Hack Club CDN, dimensions, HS codes, sizing options)
Inventory tracking with automatic quantity subtraction on order placement (prevents negative stock)
Fulfillment panel with label generation, printing, and reprint support

Shipping Integration
Canada Post — contract and non-contract shipment support, state/province → 2-letter code resolution, country name → ISO code mapping, 8.5×11 → 4×6 label cropping
Chit Chats — rate quoting and shipment creation with HTS codes and manufacturer details; automatic fallback when Canada Post fails internationally
Theseus — lettermail label support
Zonos — landed cost integration for US-bound shipments
Cheapest-rate auto-selection across all carriers for batch shipping
Flat package type with envelope size snapping (4×6 / 6×9)

Printing
QZ Tray integration for direct thermal label printing (4×6)
Combined label + packing slip print button
Reprint support for previously generated labels

Billing
HCB (Hack Club Bank) billing integration for warehouse orders

Infrastructure
4 new Drizzle migrations (orders, templates/batches, label tracking, HS codes)
drizzle-kit push at container startup for auto-migrations
entrypoint.sh for container orchestration
removeAdmin.mjs CLI script
Staging mode to bypass OAuth for local/staging testing
Body size limit increased to 10MB for image uploads

UI
Phantom Sans font and clean white UI on warehouse, admin, and ambassador pages
Shipping cost estimate disclaimers on order and batch pages

Modified
Extended DB schema with warehouse orders, templates, batches, label/tracking fields
Updated admin page with warehouse backend link and inventory management
Updated validation schemas and tests for new order/item types
CI triggers on warehouse-dev branch

P.S. DO NOT MERGE YET, I NEED TO ADD ENV VARS.

This is to estimate prices for warehouse. Adds POST /api/shipping-rates endpoint that allows ambassadors to get Canada Post shipping rate quotes by providing destination address, package type (envelope/box), dimensions (inches), and weight (grams).
@vercel
Copy link
Copy Markdown

vercel bot commented Feb 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
resolution Ready Ready Preview, Comment Feb 26, 2026 3:37pm

- Warehouse: stripped admin controls from items/orders pages, now read-only for ambassadors and admins
- Warehouse-Backend: new admin-only route with full CRUD for items, orders, categories, and tags
jeninh added 10 commits March 24, 2026 16:26
9505.10.2500 -> 9505.10.25.00 (proper dotted format per Canada Post schema)
- address-line-1/2: 44 chars, with overflow from line-1 into line-2
- name: 44 chars, city: 40 chars, prov-state: 20 chars, phone: 25 chars
- sku: 15 chars, customs-description: 45 chars
…e versa)

INT.TP (Tracked Packet) isn't available for all countries (e.g. CZ).
Now automatically retries with Xpresspost International as fallback.
Adds resolveStateCode() for US states and CA provinces (e.g. California -> CA).
Applied when creating orders from CSV batches and calculating shipping rates.
- Resolve full state names (e.g. California -> CA) in createShipment XML
  so existing orders with full names in DB are handled
- Only fall back to INT.XP when CP_CONTRACT_ID is set (it requires a contract)
- Include error details in 502 response
…shipments

Countries like CZ and EG aren't supported by INT.TP. When Canada Post
fails for non-CA destinations, automatically try Chit Chats if configured.
- Add INT.SP.AIR, INT.IP, INT.IP.SURF as fallbacks for international shipments
- Stop forcing INT.SP.AIR/INT.SP.SURF to INT.TP when no contract
- Add International Parcel Air/Surface service code mappings
@jeninh
Copy link
Copy Markdown
Collaborator Author

jeninh commented Mar 25, 2026

P.S. DO NOT MERGE YET, I NEED TO ADD ENV VARS.

# Conflicts:
#	.github/workflows/ci.yml
#	resolution-frontend/Dockerfile
#	resolution-frontend/drizzle/meta/0002_snapshot.json
#	resolution-frontend/drizzle/meta/_journal.json
#	resolution-frontend/package-lock.json
#	resolution-frontend/package.json
#	resolution-frontend/src/lib/server/db/schema.ts
#	resolution-frontend/src/lib/server/validation/schemas.test.ts
#	resolution-frontend/src/lib/server/validation/schemas.ts
#	resolution-frontend/src/routes/app/+page.svelte
#	resolution-frontend/src/routes/app/admin/+page.svelte
@jeninh jeninh marked this pull request as ready for review March 25, 2026 12:55
- Add requireAuth() to /api/qz/sign and /api/qz/cert so only
  authenticated users can sign data or fetch the certificate.
- Add admin/ambassador role check to /api/fulfillment/get-label so
  arbitrary authenticated users cannot create shipments or generate
  shipping labels.
@jeninh jeninh requested a review from Charmunks March 26, 2026 12:44
@thesleepyniko thesleepyniko self-requested a review March 26, 2026 20:59
Copy link
Copy Markdown
Collaborator

@thesleepyniko thesleepyniko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good lord. changes requested, please also get a review from like amp using the code review tool for all files here, ty

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we including these files in the frontend? let's make sure this isn't going into prod

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey are we sure this needs to be merged? if you need to remove admin shouldn't you be going through admin dash?

Comment on lines +235 to +238
<form method="POST" action="?/deleteTemplate" use:enhance>
<input type="hidden" name="templateId" value={template.id} />
<button type="submit" class="delete-btn">Delete</button>
</form>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confirmation dialog here asw

Comment on lines +33 to +37
createOrder: async ({ request, locals }) => {
const user = locals.user;
if (!user) {
return fail(401, { error: 'Not logged in' });
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if user is ambassador or admin please

Comment on lines +90 to +127
const [order] = await db.insert(warehouseOrder).values({
createdById: user.id,
firstName,
lastName,
email,
phone: phone || null,
addressLine1,
addressLine2: addressLine2 || null,
city,
stateProvince,
postalCode: postalCode || null,
country,
notes: notes || null,
status: 'APPROVED',
estimatedShippingCents: estimatedShippingCents ? parseInt(estimatedShippingCents) : null,
estimatedServiceName: estimatedServiceName || null,
estimatedServiceCode: estimatedServiceCode || null
}).returning({ id: warehouseOrder.id });

await Promise.all(
items.map((item) =>
db.insert(warehouseOrderItem).values({
orderId: order.id,
warehouseItemId: item.warehouseItemId,
quantity: item.quantity,
sizingChoice: item.sizingChoice || null
})
)
);

for (const item of items) {
const result = await db.update(warehouseItem)
.set({ quantity: sql`${warehouseItem.quantity} - ${item.quantity}` })
.where(and(eq(warehouseItem.id, item.warehouseItemId), gte(warehouseItem.quantity, item.quantity)));
if (result.rowCount === 0) {
return fail(409, { error: `Insufficient stock (concurrent update). Please try again.` });
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all of this should be one transaction to prevent a case where an order is placed but stock wasn't deducted

Comment on lines +142 to +171
if (!user.isAdmin) {
const ambassadorPathways = await db
.select({ pathway: ambassadorPathway.pathway })
.from(ambassadorPathway)
.where(eq(ambassadorPathway.userId, user.id))
.limit(1);

if (ambassadorPathways.length > 0) {
const orgId = getOrgIdForPathway(ambassadorPathways[0].pathway);
if (orgId) {
let itemsTotalCents = 0;
for (const item of items) {
const stock = stockMap.get(item.warehouseItemId);
if (stock) {
itemsTotalCents += stock.costCents * item.quantity;
}
}
const shippingCents = estimatedShippingCents ? parseInt(estimatedShippingCents) : 0;
const totalCents = itemsTotalCents + shippingCents;

if (totalCents > 0) {
await createHcbTransfer(
orgId,
totalCents,
`Warehouse order #${order.id} by ${firstName} ${lastName}`
);
}
}
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

order may ship for free should the hcb transfer fail

Comment on lines +29 to +30
with: {
createdBy: true,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

full user objects being leaked here

Copy link
Copy Markdown
Collaborator

@thesleepyniko thesleepyniko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good lord. changes requested, please also get a review from like amp using the code review tool for all files here, ty

@thesleepyniko
Copy link
Copy Markdown
Collaborator

also

  1. fix your conflicts
  2. fix the failing security audit check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

warehouse

2 participants