Montify stores integration: multi-store connections, customer/order/transaction sync, webhooks

This commit is contained in:
2026-10-10 15:59:12 +03:30
parent fa3fe0d364
commit e82bd2b2df
18 changed files with 3712 additions and 0 deletions
@@ -0,0 +1,162 @@
-- CreateTable
CREATE TABLE IF NOT EXISTS "MontifyConnection" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"label" TEXT NOT NULL,
"baseUrl" TEXT NOT NULL,
"storeId" TEXT NOT NULL DEFAULT '',
"tokenCiphertext" TEXT NOT NULL,
"tokenIv" TEXT NOT NULL,
"tokenTag" TEXT NOT NULL,
"tokenLast4" TEXT NOT NULL DEFAULT '',
"enabled" BOOLEAN NOT NULL DEFAULT true,
"webhookRemoteId" TEXT,
"webhookSecretCiphertext" TEXT,
"webhookSecretIv" TEXT,
"webhookSecretTag" TEXT,
"customersCursor" TIMESTAMP(3),
"syncStatus" TEXT NOT NULL DEFAULT 'idle',
"syncStartedAt" TIMESTAMP(3),
"lastSyncAt" TIMESTAMP(3),
"lastSyncResult" JSONB,
"lastHealthAt" TIMESTAMP(3),
"lastHealthOk" BOOLEAN,
"lastError" TEXT,
"lastWebhookAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "MontifyConnection_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE IF NOT EXISTS "MontifyLink" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"connectionId" TEXT NOT NULL,
"entityType" TEXT NOT NULL,
"externalId" TEXT NOT NULL,
"entityId" TEXT NOT NULL,
"externalUpdatedAt" TIMESTAMP(3),
"ordersSyncedAt" TIMESTAMP(3),
"data" JSONB,
"syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "MontifyLink_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE IF NOT EXISTS "MontifyOrder" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"connectionId" TEXT NOT NULL,
"externalId" TEXT NOT NULL,
"customerExternalId" TEXT,
"personId" TEXT,
"orderReference" TEXT,
"orderStatus" TEXT,
"paymentStatus" TEXT,
"paymentType" TEXT,
"totalPrice" DECIMAL(20,2),
"discountTotalPrice" DECIMAL(20,2),
"totalDiscounts" DECIMAL(20,2),
"totalTax" DECIMAL(20,2),
"totalQty" DECIMAL(14,3),
"profit" DECIMAL(20,2),
"percentProfit" DECIMAL(10,3),
"sumGold" DECIMAL(14,3),
"sumSilver" DECIMAL(14,3),
"address" JSONB,
"note" TEXT,
"cancelReason" TEXT,
"cancelledAt" TIMESTAMP(3),
"closedAt" TIMESTAMP(3),
"codeDiscount" TEXT,
"items" JSONB NOT NULL DEFAULT '[]',
"externalCreatedAt" TIMESTAMP(3),
"externalUpdatedAt" TIMESTAMP(3),
"deletedAt" TIMESTAMP(3),
"raw" JSONB,
"syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "MontifyOrder_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE IF NOT EXISTS "MontifyTransaction" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"connectionId" TEXT NOT NULL,
"externalId" TEXT NOT NULL,
"customerExternalId" TEXT,
"personId" TEXT,
"type" TEXT,
"amount" DECIMAL(24,4),
"currency" TEXT,
"status" TEXT,
"referenceCode" TEXT,
"externalCreatedAt" TIMESTAMP(3),
"raw" JSONB,
"syncedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "MontifyTransaction_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE IF NOT EXISTS "MontifyWebhookDelivery" (
"id" TEXT NOT NULL,
"connectionId" TEXT NOT NULL,
"deliveryId" TEXT NOT NULL,
"event" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'received',
"error" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "MontifyWebhookDelivery_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX IF NOT EXISTS "MontifyConnection_organizationId_enabled_idx" ON "MontifyConnection"("organizationId", "enabled");
CREATE UNIQUE INDEX IF NOT EXISTS "MontifyLink_connectionId_entityType_externalId_key" ON "MontifyLink"("connectionId", "entityType", "externalId");
CREATE INDEX IF NOT EXISTS "MontifyLink_organizationId_entityType_entityId_idx" ON "MontifyLink"("organizationId", "entityType", "entityId");
CREATE UNIQUE INDEX IF NOT EXISTS "MontifyOrder_connectionId_externalId_key" ON "MontifyOrder"("connectionId", "externalId");
CREATE INDEX IF NOT EXISTS "MontifyOrder_organizationId_personId_idx" ON "MontifyOrder"("organizationId", "personId");
CREATE INDEX IF NOT EXISTS "MontifyOrder_organizationId_externalCreatedAt_idx" ON "MontifyOrder"("organizationId", "externalCreatedAt");
CREATE UNIQUE INDEX IF NOT EXISTS "MontifyTransaction_connectionId_externalId_key" ON "MontifyTransaction"("connectionId", "externalId");
CREATE INDEX IF NOT EXISTS "MontifyTransaction_organizationId_personId_idx" ON "MontifyTransaction"("organizationId", "personId");
CREATE UNIQUE INDEX IF NOT EXISTS "MontifyWebhookDelivery_connectionId_deliveryId_key" ON "MontifyWebhookDelivery"("connectionId", "deliveryId");
CREATE INDEX IF NOT EXISTS "MontifyWebhookDelivery_connectionId_createdAt_idx" ON "MontifyWebhookDelivery"("connectionId", "createdAt");
-- AddForeignKey
DO $$ BEGIN
ALTER TABLE "MontifyConnection" ADD CONSTRAINT "MontifyConnection_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MontifyLink" ADD CONSTRAINT "MontifyLink_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "MontifyConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MontifyOrder" ADD CONSTRAINT "MontifyOrder_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "MontifyConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MontifyTransaction" ADD CONSTRAINT "MontifyTransaction_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "MontifyConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN
ALTER TABLE "MontifyWebhookDelivery" ADD CONSTRAINT "MontifyWebhookDelivery_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "MontifyConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- ماژول جدید برای محیط‌هایی که فهرست ماژول ذخیره‌شده دارند و ووکامرس فعال است
UPDATE "Organization"
SET "enabledModules" = array_append("enabledModules", 'montify')
WHERE cardinality("enabledModules") > 0
AND 'woocommerce' = ANY("enabledModules")
AND NOT ('montify' = ANY("enabledModules"));
+142
View File
@@ -201,6 +201,7 @@ model Organization {
webhookEndpoints WebhookEndpoint[]
smsCampaigns SmsCampaign[]
wooCommerceConnections WooCommerceConnection[]
montifyConnections MontifyConnection[]
voipConnections VoipConnection[]
reservationServices ReservationService[]
reservationAppointments ReservationAppointment[]
@@ -2080,6 +2081,147 @@ model WooCommerceOrderLink {
@@index([invoiceId])
}
/// اتصال فروشگاه Montify (هر فروشگاه: Base URL + توکن اختصاصی)
model MontifyConnection {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
label String
baseUrl String
storeId String @default("")
tokenCiphertext String
tokenIv String
tokenTag String
tokenLast4 String @default("")
enabled Boolean @default(true)
webhookRemoteId String?
webhookSecretCiphertext String?
webhookSecretIv String?
webhookSecretTag String?
/// بیشترین updated_at مشتریان دریافت‌شده (برای updated_after)
customersCursor DateTime?
/// idle | running | ok | error
syncStatus String @default("idle")
syncStartedAt DateTime?
lastSyncAt DateTime?
lastSyncResult Json?
lastHealthAt DateTime?
lastHealthOk Boolean?
lastError String?
lastWebhookAt DateTime?
links MontifyLink[]
orders MontifyOrder[]
transactions MontifyTransaction[]
deliveries MontifyWebhookDelivery[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([organizationId, enabled])
}
/// پیوند مشتری فروشگاه با شخص CRM (entityType = customer، entityId = Person.id)
model MontifyLink {
id String @id @default(cuid())
organizationId String
connectionId String
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
entityType String
externalId String
entityId String
externalUpdatedAt DateTime?
/// آخرین زمان دریافت سفارش‌های این مشتری (updated_after سفارش‌ها)
ordersSyncedAt DateTime?
/// پروفایل فروشگاهی مشتری: wallet_balance، gold_wallet_balance، summary، engagement، …
data Json?
syncedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([connectionId, entityType, externalId])
@@index([organizationId, entityType, entityId])
}
/// سفارش فروشگاه (فقط‌خواندنی، نگاشت مستند: code → order_reference، sts → order_status)
model MontifyOrder {
id String @id @default(cuid())
organizationId String
connectionId String
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
externalId String
customerExternalId String?
personId String?
orderReference String?
orderStatus String?
paymentStatus String?
paymentType String?
totalPrice Decimal? @db.Decimal(20, 2)
discountTotalPrice Decimal? @db.Decimal(20, 2)
totalDiscounts Decimal? @db.Decimal(20, 2)
totalTax Decimal? @db.Decimal(20, 2)
totalQty Decimal? @db.Decimal(14, 3)
profit Decimal? @db.Decimal(20, 2)
percentProfit Decimal? @db.Decimal(10, 3)
sumGold Decimal? @db.Decimal(14, 3)
sumSilver Decimal? @db.Decimal(14, 3)
address Json?
note String?
cancelReason String?
cancelledAt DateTime?
closedAt DateTime?
codeDiscount String?
items Json @default("[]")
externalCreatedAt DateTime?
externalUpdatedAt DateTime?
deletedAt DateTime?
raw Json?
syncedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([connectionId, externalId])
@@index([organizationId, personId])
@@index([organizationId, externalCreatedAt])
}
/// تراکنش مالی مشتری در فروشگاه (واریز/برداشت ریالی یا گرم طلا)
model MontifyTransaction {
id String @id @default(cuid())
organizationId String
connectionId String
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
externalId String
customerExternalId String?
personId String?
type String?
amount Decimal? @db.Decimal(24, 4)
currency String?
status String?
referenceCode String?
externalCreatedAt DateTime?
raw Json?
syncedAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([connectionId, externalId])
@@index([organizationId, personId])
}
model MontifyWebhookDelivery {
id String @id @default(cuid())
connectionId String
connection MontifyConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
deliveryId String
event String
/// received | processed | failed
status String @default("received")
error String?
createdAt DateTime @default(now())
@@unique([connectionId, deliveryId])
@@index([connectionId, createdAt])
}
/// اتصال تلفن سازمانی / سانترال (وب‌هوک عمومی)
model VoipConnection {
id String @id @default(cuid())