@chatman-media/storage 1.20.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,10 +35,16 @@ __export(exports_schema, {
35
35
  skillOutcomes: () => skillOutcomes,
36
36
  shadowEvaluations: () => shadowEvaluations,
37
37
  sessions: () => sessions,
38
+ serviceOrders: () => serviceOrders,
39
+ serviceOrderPayments: () => serviceOrderPayments,
40
+ serviceOrderCommissions: () => serviceOrderCommissions,
38
41
  serviceCatalogItems: () => serviceCatalogItems,
39
42
  selfPlayMatches: () => selfPlayMatches,
40
43
  referralCodes: () => referralCodes,
41
44
  questionnaireTokens: () => questionnaireTokens,
45
+ providerServices: () => providerServices,
46
+ providerRequests: () => providerRequests,
47
+ providerProfiles: () => providerProfiles,
42
48
  passwordResets: () => passwordResets,
43
49
  partners: () => partners,
44
50
  partnerSettlements: () => partnerSettlements,
@@ -48,6 +54,7 @@ __export(exports_schema, {
48
54
  outreachCampaigns: () => outreachCampaigns,
49
55
  outreachCampaignLeads: () => outreachCampaignLeads,
50
56
  outboundQueue: () => outboundQueue,
57
+ orderEvents: () => orderEvents,
51
58
  operatorSettings: () => operatorSettings,
52
59
  notificationTemplates: () => notificationTemplates,
53
60
  notificationRules: () => notificationRules,
@@ -2602,10 +2609,20 @@ var kbDocuments = pgTable("kb_documents", {
2602
2609
  title: text("title").notNull(),
2603
2610
  contentHash: text("content_hash").notNull(),
2604
2611
  topic: text("topic"),
2612
+ scopeType: text("scope_type").notNull().default("global"),
2613
+ funnelId: integer("funnel_id"),
2614
+ stageSlug: text("stage_slug"),
2605
2615
  createdAt: integer("created_at").notNull().default(epochNow())
2606
2616
  }, (t) => [
2617
+ check("kb_documents_scope_type_check", sql`${t.scopeType} IN ('global','funnel','stage')`),
2618
+ check("kb_documents_scope_shape_check", sql`(
2619
+ (${t.scopeType} = 'global' AND ${t.funnelId} IS NULL AND ${t.stageSlug} IS NULL)
2620
+ OR (${t.scopeType} = 'funnel' AND ${t.funnelId} IS NOT NULL AND ${t.stageSlug} IS NULL)
2621
+ OR (${t.scopeType} = 'stage' AND ${t.funnelId} IS NOT NULL AND ${t.stageSlug} IS NOT NULL)
2622
+ )`),
2607
2623
  uniqueIndex("uniq_kb_source_hash").on(t.source, t.contentHash),
2608
- index("idx_kb_docs_topic").on(t.topic).where(sql`topic IS NOT NULL`)
2624
+ index("idx_kb_docs_topic").on(t.topic).where(sql`topic IS NOT NULL`),
2625
+ index("idx_kb_docs_scope").on(t.tenantId, t.scopeType, t.funnelId, t.stageSlug)
2609
2626
  ]);
2610
2627
  var kbChunks = pgTable("kb_chunks", {
2611
2628
  id: serial("id").primaryKey(),
@@ -2881,6 +2898,9 @@ var kbSuggestions = pgTable("kb_suggestions", {
2881
2898
  questionText: text("question_text").notNull(),
2882
2899
  answerDraft: text("answer_draft"),
2883
2900
  status: text("status").notNull().default("pending"),
2901
+ scopeType: text("scope_type").notNull().default("global"),
2902
+ funnelId: integer("funnel_id"),
2903
+ stageSlug: text("stage_slug"),
2884
2904
  sourceConversationId: integer("source_conversation_id").references(() => conversations.id, { onDelete: "set null" }),
2885
2905
  sourceMessageId: integer("source_message_id").references(() => messages.id, { onDelete: "set null" }),
2886
2906
  decidedByAdminId: integer("decided_by_admin_id").references(() => admins.id, { onDelete: "set null" }),
@@ -2891,8 +2911,15 @@ var kbSuggestions = pgTable("kb_suggestions", {
2891
2911
  updatedAt: integer("updated_at").notNull().default(epochNow())
2892
2912
  }, (t) => [
2893
2913
  check("kb_suggestions_status_check", sql`${t.status} IN ('pending','ingested','rejected')`),
2914
+ check("kb_suggestions_scope_type_check", sql`${t.scopeType} IN ('global','funnel','stage')`),
2915
+ check("kb_suggestions_scope_shape_check", sql`(
2916
+ (${t.scopeType} = 'global' AND ${t.funnelId} IS NULL AND ${t.stageSlug} IS NULL)
2917
+ OR (${t.scopeType} = 'funnel' AND ${t.funnelId} IS NOT NULL AND ${t.stageSlug} IS NULL)
2918
+ OR (${t.scopeType} = 'stage' AND ${t.funnelId} IS NOT NULL AND ${t.stageSlug} IS NOT NULL)
2919
+ )`),
2894
2920
  index("idx_kb_suggestions_status").on(t.status, sql`${t.createdAt} DESC`),
2895
- index("idx_kb_suggestions_conv").on(t.sourceConversationId)
2921
+ index("idx_kb_suggestions_conv").on(t.sourceConversationId),
2922
+ index("idx_kb_suggestions_scope").on(t.tenantId, t.scopeType, t.funnelId, t.stageSlug, sql`${t.createdAt} DESC`)
2896
2923
  ]);
2897
2924
  var skills = pgTable("skills", {
2898
2925
  id: serial("id").primaryKey(),
@@ -3634,6 +3661,181 @@ var partnerSettlements = pgTable("partner_settlements", {
3634
3661
  check("partner_settlements_status_check", sql`${t.status} IN ('draft','issued','paid','cancelled')`),
3635
3662
  index("idx_partner_settlements_partner").on(t.tenantId, t.partnerId, t.periodStart)
3636
3663
  ]);
3664
+ var providerProfiles = pgTable("provider_profiles", {
3665
+ id: serial("id").primaryKey(),
3666
+ tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
3667
+ contactId: integer("contact_id").notNull().references(() => contacts.id, { onDelete: "cascade" }),
3668
+ name: text("name").notNull(),
3669
+ category: text("category"),
3670
+ status: text("status").notNull().default("active"),
3671
+ serviceArea: text("service_area"),
3672
+ defaultCommissionPct: doublePrecision("default_commission_pct").notNull().default(0),
3673
+ notes: text("notes"),
3674
+ metadataJson: text("metadata_json").notNull().default("{}"),
3675
+ createdAt: integer("created_at").notNull().default(epochNow()),
3676
+ updatedAt: integer("updated_at").notNull().default(epochNow())
3677
+ }, (t) => [
3678
+ check("provider_profiles_status_check", sql`${t.status} IN ('active','paused','archived')`),
3679
+ uniqueIndex("uniq_provider_profiles_contact").on(t.tenantId, t.contactId),
3680
+ index("idx_provider_profiles_tenant_status").on(t.tenantId, t.status),
3681
+ index("idx_provider_profiles_category").on(t.tenantId, t.category)
3682
+ ]);
3683
+ var providerServices = pgTable("provider_services", {
3684
+ id: serial("id").primaryKey(),
3685
+ tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
3686
+ providerId: integer("provider_id").notNull().references(() => providerProfiles.id, { onDelete: "cascade" }),
3687
+ serviceType: text("service_type").notNull(),
3688
+ name: text("name").notNull(),
3689
+ serviceArea: text("service_area"),
3690
+ pricingPolicyJson: text("pricing_policy_json").notNull().default("{}"),
3691
+ commissionPct: doublePrecision("commission_pct"),
3692
+ isActive: boolean("is_active").notNull().default(true),
3693
+ metadataJson: text("metadata_json").notNull().default("{}"),
3694
+ createdAt: integer("created_at").notNull().default(epochNow()),
3695
+ updatedAt: integer("updated_at").notNull().default(epochNow())
3696
+ }, (t) => [
3697
+ uniqueIndex("uniq_provider_services_name").on(t.tenantId, t.providerId, t.serviceType, t.name),
3698
+ index("idx_provider_services_tenant_active").on(t.tenantId, t.isActive),
3699
+ index("idx_provider_services_type").on(t.tenantId, t.serviceType, t.isActive)
3700
+ ]);
3701
+ var serviceOrders = pgTable("service_orders", {
3702
+ id: serial("id").primaryKey(),
3703
+ tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
3704
+ customerContactId: integer("customer_contact_id").notNull().references(() => contacts.id, { onDelete: "cascade" }),
3705
+ customerConversationId: integer("customer_conversation_id").references(() => conversations.id, { onDelete: "set null" }),
3706
+ leadId: integer("lead_id").references(() => leads.id, { onDelete: "set null" }),
3707
+ assignedProviderId: integer("assigned_provider_id").references(() => providerProfiles.id, { onDelete: "set null" }),
3708
+ requestType: text("request_type").notNull(),
3709
+ status: text("status").notNull().default("intake"),
3710
+ summary: text("summary"),
3711
+ quotedAmount: doublePrecision("quoted_amount"),
3712
+ customerAmount: doublePrecision("customer_amount"),
3713
+ commissionPct: doublePrecision("commission_pct"),
3714
+ commissionAmount: doublePrecision("commission_amount"),
3715
+ currency: text("currency").notNull().default("THB"),
3716
+ paymentStatus: text("payment_status").notNull().default("unpaid"),
3717
+ paymentProvider: text("payment_provider"),
3718
+ paymentRef: text("payment_ref"),
3719
+ idempotencyKey: text("idempotency_key"),
3720
+ metadataJson: text("metadata_json").notNull().default("{}"),
3721
+ expiresAt: integer("expires_at"),
3722
+ confirmedAt: integer("confirmed_at"),
3723
+ completedAt: integer("completed_at"),
3724
+ cancelledAt: integer("cancelled_at"),
3725
+ createdAt: integer("created_at").notNull().default(epochNow()),
3726
+ updatedAt: integer("updated_at").notNull().default(epochNow())
3727
+ }, (t) => [
3728
+ check("service_orders_status_check", sql`${t.status} IN ('intake','matching','awaiting_provider','provider_declined','offer_ready','awaiting_customer_payment','paid','confirmed','fulfilled','cancelled','failed')`),
3729
+ check("service_orders_payment_status_check", sql`${t.paymentStatus} IN ('unpaid','pending','paid','refunded','failed')`),
3730
+ uniqueIndex("uniq_service_orders_idem").on(t.idempotencyKey).where(sql`idempotency_key IS NOT NULL`),
3731
+ index("idx_service_orders_tenant_status").on(t.tenantId, t.status),
3732
+ index("idx_service_orders_customer").on(t.tenantId, t.customerContactId),
3733
+ index("idx_service_orders_lead").on(t.tenantId, t.leadId),
3734
+ index("idx_service_orders_provider").on(t.tenantId, t.assignedProviderId),
3735
+ index("idx_service_orders_payment").on(t.tenantId, t.paymentStatus)
3736
+ ]);
3737
+ var providerRequests = pgTable("provider_requests", {
3738
+ id: serial("id").primaryKey(),
3739
+ tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
3740
+ orderId: integer("order_id").notNull().references(() => serviceOrders.id, { onDelete: "cascade" }),
3741
+ providerId: integer("provider_id").references(() => providerProfiles.id, { onDelete: "set null" }),
3742
+ providerConversationId: integer("provider_conversation_id").references(() => conversations.id, { onDelete: "set null" }),
3743
+ channelId: integer("channel_id").references(() => channels.id, { onDelete: "set null" }),
3744
+ outboundQueueId: integer("outbound_queue_id").references(() => outboundQueue.id, { onDelete: "set null" }),
3745
+ status: text("status").notNull().default("draft"),
3746
+ quotedAmount: doublePrecision("quoted_amount"),
3747
+ customerAmount: doublePrecision("customer_amount"),
3748
+ commissionAmount: doublePrecision("commission_amount"),
3749
+ currency: text("currency").notNull().default("THB"),
3750
+ availableAt: integer("available_at"),
3751
+ quoteExpiresAt: integer("quote_expires_at"),
3752
+ responseText: text("response_text"),
3753
+ idempotencyKey: text("idempotency_key"),
3754
+ metadataJson: text("metadata_json").notNull().default("{}"),
3755
+ sentAt: integer("sent_at"),
3756
+ respondedAt: integer("responded_at"),
3757
+ expiredAt: integer("expired_at"),
3758
+ cancelledAt: integer("cancelled_at"),
3759
+ createdAt: integer("created_at").notNull().default(epochNow()),
3760
+ updatedAt: integer("updated_at").notNull().default(epochNow())
3761
+ }, (t) => [
3762
+ check("provider_requests_status_check", sql`${t.status} IN ('draft','sent','seen','quoted','accepted','declined','expired','cancelled')`),
3763
+ uniqueIndex("uniq_provider_requests_idem").on(t.idempotencyKey).where(sql`idempotency_key IS NOT NULL`),
3764
+ index("idx_provider_requests_order").on(t.tenantId, t.orderId),
3765
+ index("idx_provider_requests_provider_status").on(t.tenantId, t.providerId, t.status),
3766
+ index("idx_provider_requests_tenant_status").on(t.tenantId, t.status),
3767
+ index("idx_provider_requests_quote_ttl").on(t.status, t.quoteExpiresAt).where(sql`status = 'quoted'`)
3768
+ ]);
3769
+ var orderEvents = pgTable("order_events", {
3770
+ id: serial("id").primaryKey(),
3771
+ tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
3772
+ orderId: integer("order_id").notNull().references(() => serviceOrders.id, { onDelete: "cascade" }),
3773
+ providerRequestId: integer("provider_request_id").references(() => providerRequests.id, { onDelete: "set null" }),
3774
+ conversationId: integer("conversation_id").references(() => conversations.id, { onDelete: "set null" }),
3775
+ messageId: integer("message_id").references(() => messages.id, { onDelete: "set null" }),
3776
+ actorType: text("actor_type").notNull().default("system"),
3777
+ eventType: text("event_type").notNull(),
3778
+ dataJson: text("data_json").notNull().default("{}"),
3779
+ createdAt: integer("created_at").notNull().default(epochNow())
3780
+ }, (t) => [
3781
+ check("order_events_actor_type_check", sql`${t.actorType} IN ('system','customer','provider','operator','payment')`),
3782
+ index("idx_order_events_order_created").on(t.tenantId, t.orderId, t.createdAt),
3783
+ index("idx_order_events_provider_request").on(t.tenantId, t.providerRequestId),
3784
+ index("idx_order_events_type").on(t.tenantId, t.eventType)
3785
+ ]);
3786
+ var serviceOrderPayments = pgTable("service_order_payments", {
3787
+ id: serial("id").primaryKey(),
3788
+ tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
3789
+ orderId: integer("order_id").notNull().references(() => serviceOrders.id, { onDelete: "cascade" }),
3790
+ provider: text("provider").notNull(),
3791
+ externalIntentId: text("external_intent_id"),
3792
+ externalSessionId: text("external_session_id"),
3793
+ status: text("status").notNull().default("created"),
3794
+ amount: doublePrecision("amount").notNull(),
3795
+ currency: text("currency").notNull().default("THB"),
3796
+ idempotencyKey: text("idempotency_key"),
3797
+ metadataJson: text("metadata_json").notNull().default("{}"),
3798
+ createdAt: integer("created_at").notNull().default(epochNow()),
3799
+ updatedAt: integer("updated_at").notNull().default(epochNow()),
3800
+ paidAt: integer("paid_at"),
3801
+ failedAt: integer("failed_at"),
3802
+ cancelledAt: integer("cancelled_at"),
3803
+ refundedAt: integer("refunded_at")
3804
+ }, (t) => [
3805
+ check("service_order_payments_status_check", sql`${t.status} IN ('created','pending','paid','failed','cancelled','refunded')`),
3806
+ uniqueIndex("uniq_service_order_payments_idem").on(t.tenantId, t.idempotencyKey).where(sql`idempotency_key IS NOT NULL`),
3807
+ uniqueIndex("uniq_service_order_payments_provider_intent").on(t.tenantId, t.provider, t.externalIntentId).where(sql`external_intent_id IS NOT NULL`),
3808
+ uniqueIndex("uniq_service_order_payments_provider_session").on(t.tenantId, t.provider, t.externalSessionId).where(sql`external_session_id IS NOT NULL`),
3809
+ index("idx_service_order_payments_order").on(t.tenantId, t.orderId),
3810
+ index("idx_service_order_payments_status").on(t.tenantId, t.status)
3811
+ ]);
3812
+ var serviceOrderCommissions = pgTable("service_order_commissions", {
3813
+ id: serial("id").primaryKey(),
3814
+ tenantId: integer("tenant_id").notNull().references(() => tenants.id, { onDelete: "cascade" }),
3815
+ orderId: integer("order_id").notNull().references(() => serviceOrders.id, { onDelete: "cascade" }),
3816
+ providerId: integer("provider_id").references(() => providerProfiles.id, { onDelete: "set null" }),
3817
+ paymentId: integer("payment_id").references(() => serviceOrderPayments.id, { onDelete: "set null" }),
3818
+ status: text("status").notNull().default("pending"),
3819
+ grossAmount: doublePrecision("gross_amount").notNull(),
3820
+ commissionPct: doublePrecision("commission_pct").notNull().default(0),
3821
+ commissionAmount: doublePrecision("commission_amount").notNull().default(0),
3822
+ currency: text("currency").notNull().default("THB"),
3823
+ source: text("source").notNull().default("payment"),
3824
+ idempotencyKey: text("idempotency_key"),
3825
+ metadataJson: text("metadata_json").notNull().default("{}"),
3826
+ earnedAt: integer("earned_at"),
3827
+ refundedAt: integer("refunded_at"),
3828
+ paidOutAt: integer("paid_out_at"),
3829
+ createdAt: integer("created_at").notNull().default(epochNow()),
3830
+ updatedAt: integer("updated_at").notNull().default(epochNow())
3831
+ }, (t) => [
3832
+ check("service_order_commissions_status_check", sql`${t.status} IN ('pending','earned','void','refunded','paid_out')`),
3833
+ uniqueIndex("uniq_service_order_commissions_idem").on(t.tenantId, t.idempotencyKey).where(sql`idempotency_key IS NOT NULL`),
3834
+ index("idx_service_order_commissions_order").on(t.tenantId, t.orderId),
3835
+ index("idx_service_order_commissions_provider").on(t.tenantId, t.providerId),
3836
+ index("idx_service_order_commissions_payment").on(t.tenantId, t.paymentId),
3837
+ index("idx_service_order_commissions_status").on(t.tenantId, t.status)
3838
+ ]);
3637
3839
  // src/integration-helpers.ts
3638
3840
  import { readdirSync, readFileSync } from "node:fs";
3639
3841
  import { join } from "node:path";
@@ -5672,11 +5874,17 @@ export {
5672
5874
  skillOutcomes,
5673
5875
  shadowEvaluations,
5674
5876
  sessions,
5877
+ serviceOrders,
5878
+ serviceOrderPayments,
5879
+ serviceOrderCommissions,
5675
5880
  serviceCatalogItems,
5676
5881
  selfPlayMatches,
5677
5882
  exports_schema as schema,
5678
5883
  referralCodes,
5679
5884
  questionnaireTokens,
5885
+ providerServices,
5886
+ providerRequests,
5887
+ providerProfiles,
5680
5888
  passwordResets,
5681
5889
  partners,
5682
5890
  partnerSettlements,
@@ -5686,6 +5894,7 @@ export {
5686
5894
  outreachCampaigns,
5687
5895
  outreachCampaignLeads,
5688
5896
  outboundQueue,
5897
+ orderEvents,
5689
5898
  operatorSettings,
5690
5899
  notificationTemplates,
5691
5900
  notificationRules,
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider-relay.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-relay.integration.test.d.ts","sourceRoot":"","sources":["../src/provider-relay.integration.test.ts"],"names":[],"mappings":""}