Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 3m40s
ApiCore now owns the remaining stock per batch; the shop sells from it and reports every paid sale and every return back. - Outbox apicore_stock_events, written in the same transaction as the status change: paid -> SALE, cancelled-after-payment / refunded return -> RETURN onto the batches of the sale, reopened paid order -> SALE. Whether a return is due is decided by the open sales in the outbox, so unpaid orders never book and nothing is booked twice. - Delivery right after the response (next/server after()) plus CronJob wabenhain-apicore-stock every 10 minutes with 1/5/15/60 min backoff; ApiCore deduplicates by event id. Rejected bookings stop and show up on the dashboard. - Reconcile: shop stock = ApiCore remaining - unconfirmed bookings - reservations of unpaid orders, in a serializable transaction like the checkout. Triggered after bookings, by the signed ApiCore webhook stock.changed (ids only, numbers are fetched) and by the sync button. - Manufactured goods (candles, propolis products, ...): one shop product per ApiCore article via Product.apicoreProductId, drafts created by the sync, existing products linkable in the product form; new category SONSTIGES. Stock field is read-only for linked products. - OrderItem.apicoreAllocations keeps which lots were sold (recalls). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
74 lines
2.4 KiB
JSON
74 lines
2.4 KiB
JSON
{
|
|
"name": "wabenhain",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"scripts": {
|
|
"dev": "node scripts/generate-prisma-clients.mjs && next dev",
|
|
"build": "node scripts/generate-prisma-clients.mjs && next build",
|
|
"start": "node scripts/generate-prisma-clients.mjs && next start",
|
|
"lint": "eslint",
|
|
"postinstall": "node scripts/generate-prisma-clients.mjs",
|
|
"db:push": "node scripts/prisma-provider.mjs && prisma db push",
|
|
"db:migrate": "node scripts/prisma-provider.mjs && prisma migrate dev",
|
|
"db:deploy": "node scripts/prisma-provider.mjs && prisma migrate deploy",
|
|
"db:seed": "prisma db seed",
|
|
"db:seed:demo": "tsx prisma/seed-demo.ts",
|
|
"db:studio": "node scripts/prisma-provider.mjs && prisma studio",
|
|
"test": "vitest run",
|
|
"test:watch": "vitest",
|
|
"test:e2e": "playwright test",
|
|
"preflight": "tsx scripts/preflight.ts",
|
|
"places:find": "tsx scripts/find-place-id.ts",
|
|
"orders:release-stale": "tsx scripts/release-stale-orders.ts",
|
|
"stock:apicore-sync": "tsx scripts/apicore-stock-sync.ts"
|
|
},
|
|
"dependencies": {
|
|
"@prisma/adapter-mariadb": "^7.10.0",
|
|
"@prisma/adapter-pg": "^7.10.0",
|
|
"@prisma/client": "^7.10.0",
|
|
"@stripe/react-stripe-js": "^6.12.0",
|
|
"@stripe/stripe-js": "^9.17.0",
|
|
"bcryptjs": "^3.0.3",
|
|
"ffmpeg-static": "^5.3.0",
|
|
"jose": "^6.2.8",
|
|
"mariadb": "^3.5.3",
|
|
"motion": "^13.0.0",
|
|
"next": "16.3.5",
|
|
"nodemailer": "^9.1.1",
|
|
"otpauth": "^9.5.1",
|
|
"pdfkit": "^0.19.1",
|
|
"prisma": "^7.10.0",
|
|
"qrcode": "^1.5.4",
|
|
"react": "19.2.8",
|
|
"react-dom": "19.2.8",
|
|
"sharp": "^0.35.4",
|
|
"stripe": "^22.4.0",
|
|
"zod": "^4.4.3",
|
|
"zustand": "^5.0.14"
|
|
},
|
|
"devDependencies": {
|
|
"@playwright/test": "^1.62.1",
|
|
"@tailwindcss/postcss": "^4",
|
|
"@testing-library/jest-dom": "^7.0.1",
|
|
"@testing-library/react": "^16.3.2",
|
|
"@testing-library/user-event": "^14.6.4",
|
|
"@types/bcryptjs": "^3.0.0",
|
|
"@types/node": "^20.19.43",
|
|
"@types/nodemailer": "^8.0.1",
|
|
"@types/pdfkit": "^0.17.6",
|
|
"@types/qrcode": "^1.5.6",
|
|
"@types/react": "^19",
|
|
"@types/react-dom": "^19",
|
|
"@vitejs/plugin-react": "^6.0.5",
|
|
"dotenv": "^17.4.2",
|
|
"eslint": "^9",
|
|
"eslint-config-next": "16.3.5",
|
|
"jsdom": "^30.0.1",
|
|
"tailwindcss": "^4",
|
|
"tsx": "^4",
|
|
"typescript": "^5",
|
|
"vitest": "^4.1.10"
|
|
},
|
|
"packageManager": "[email protected]"
|
|
}
|