Files
doc-manager/apps/api/prisma/migrations/3_orders/migration.sql
T
admin c2fcdec85d feat(orders): Site/Order/OrderItem + S2S incoming endpoint + Tochka webhook receiver
Schema:
- Site (organizationId, name, slug, domain, apiKey, defaultOfferTemplateId)
- Order (full customer fields, status enum, totalCents/vatCents, projectId link, rawPayload)
- OrderItem (orderId, position, name, serviceId, qty, unit, price, vat, eventDate)
- Migration 3_orders + OrderStatus enum

API:
- /api/sites — CRUD with apiKey shown only on create/regenerate
- /api/orders — list/get/convert-to-project (creates project + matches/creates client by INN)
- POST /api/incoming/orders — S2S, X-Site-Key header → resolves Site → creates Order
- POST /webhooks/tochka/<secret> — receives raw, dedupes, parses paymentId+purpose,
  matches by document number regex, creates Payment, updates Document status
  (paid/partially_paid), propagates Order.status=paid when fully covered

Web:
- /sites page: list + add site (paste-friendly modal with API key + curl example
  shown once after create/regenerate)
- /orders page: filterable list, link to project
- /orders/🆔 view with items + "Перевести в проект" button (creates project,
  upserts client by INN, links project<-order)
- Nav: «Заявки» and «Сайты» added

Manual demo flow:
1. /sites → add «Голосования» slug=voting → save the apiKey
2. curl POST /api/incoming/orders with X-Site-Key → order appears in /orders
3. Open order → «Перевести в проект» → project created with client+default
4. Create invoice document in project → «Выставить через Точку»
5. Webhook from sandbox/prod → document.status=paid → order.status=paid

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-16 15:00:24 +03:00

88 lines
3.5 KiB
SQL

-- CreateEnum
CREATE TYPE "OrderStatus" AS ENUM ('new', 'accepted', 'invoiced', 'paid', 'fulfilled', 'cancelled');
-- CreateTable Site
CREATE TABLE "Site" (
"id" UUID NOT NULL,
"organizationId" UUID NOT NULL,
"name" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"domain" TEXT,
"apiKey" TEXT NOT NULL,
"defaultOfferTemplateId" UUID,
"archivedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Site_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "Site_apiKey_key" ON "Site"("apiKey");
CREATE UNIQUE INDEX "Site_organizationId_slug_key" ON "Site"("organizationId", "slug");
CREATE INDEX "Site_organizationId_idx" ON "Site"("organizationId");
ALTER TABLE "Site" ADD CONSTRAINT "Site_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Site" ADD CONSTRAINT "Site_defaultOfferTemplateId_fkey"
FOREIGN KEY ("defaultOfferTemplateId") REFERENCES "DocumentTemplate"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- CreateTable Order
CREATE TABLE "Order" (
"id" UUID NOT NULL,
"organizationId" UUID NOT NULL,
"siteId" UUID,
"projectId" UUID,
"status" "OrderStatus" NOT NULL DEFAULT 'new',
"customerName" TEXT NOT NULL,
"customerInn" TEXT,
"customerKpp" TEXT,
"customerEmail" TEXT,
"customerPhone" TEXT,
"customerAddress" TEXT,
"customerKind" "ClientKind" NOT NULL DEFAULT 'ul',
"totalCents" BIGINT NOT NULL DEFAULT 0,
"vatCents" BIGINT NOT NULL DEFAULT 0,
"currency" TEXT NOT NULL DEFAULT 'RUB',
"acceptedOfferAt" TIMESTAMP(3),
"notes" TEXT,
"rawPayload" JSONB,
"archivedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Order_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "Order_organizationId_status_idx" ON "Order"("organizationId", "status");
CREATE INDEX "Order_organizationId_siteId_idx" ON "Order"("organizationId", "siteId");
CREATE INDEX "Order_projectId_idx" ON "Order"("projectId");
ALTER TABLE "Order" ADD CONSTRAINT "Order_organizationId_fkey"
FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Order" ADD CONSTRAINT "Order_siteId_fkey"
FOREIGN KEY ("siteId") REFERENCES "Site"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "Order" ADD CONSTRAINT "Order_projectId_fkey"
FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- CreateTable OrderItem
CREATE TABLE "OrderItem" (
"id" UUID NOT NULL,
"orderId" UUID NOT NULL,
"position" INTEGER NOT NULL,
"name" TEXT NOT NULL,
"serviceId" UUID,
"qtyMilli" BIGINT NOT NULL DEFAULT 1000,
"unit" TEXT NOT NULL DEFAULT 'шт',
"priceCents" BIGINT NOT NULL,
"vat" "VatRate" NOT NULL DEFAULT 'none',
"sumCents" BIGINT NOT NULL,
"eventDate" TIMESTAMP(3),
CONSTRAINT "OrderItem_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "OrderItem_orderId_idx" ON "OrderItem"("orderId");
CREATE INDEX "OrderItem_serviceId_idx" ON "OrderItem"("serviceId");
ALTER TABLE "OrderItem" ADD CONSTRAINT "OrderItem_orderId_fkey"
FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "OrderItem" ADD CONSTRAINT "OrderItem_serviceId_fkey"
FOREIGN KEY ("serviceId") REFERENCES "ServiceCatalog"("id") ON DELETE SET NULL ON UPDATE CASCADE;