@vrplatform/kysely 1.3.45-stage.6033 → 1.3.45-stage.604
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/build/.data.tar +0 -0
- package/build/main/control-plane/index.d.ts +314 -0
- package/build/main/control-plane/index.js.map +1 -1
- package/build/main/index.d.ts +13 -2
- package/build/main/index.js +217 -23
- package/build/main/index.js.map +1 -1
- package/build/main/local/generate.js +2 -0
- package/build/main/local/generate.js.map +1 -1
- package/build/main/local/index.js +110 -8
- package/build/main/local/index.js.map +1 -1
- package/build/main/postgres-error.d.ts +3 -3
- package/build/main/postgres-error.js +4 -3
- package/build/main/postgres-error.js.map +1 -1
- package/build/main/query/listing/status.js +2 -6
- package/build/main/query/listing/status.js.map +1 -1
- package/build/main/query/tenant/status.d.ts +4 -6
- package/build/main/query/tenant/status.js +17 -14
- package/build/main/query/tenant/status.js.map +1 -1
- package/build/main/regional.d.ts +1 -0
- package/build/main/regional.js +1 -0
- package/build/main/regional.js.map +1 -1
- package/build/main/test/index.d.ts +23 -3
- package/build/main/test/index.js +35 -13
- package/build/main/test/index.js.map +1 -1
- package/build/main/v1.generated.d.ts +560 -7
- package/build/main/v1.generated.js.map +1 -1
- package/build/migrations-v2/0000000000013-ui-permission-bundles.ts +45 -0
- package/build/migrations-v2/0000000000014-ui-permission-bundle-top-up.ts +32 -0
- package/build/migrations-v2/0000000000015-durable-flow-events.ts +163 -0
- package/build/migrations-v2/0000000000016-billing-listing-facts.ts +539 -0
- package/build/migrations-v2/0000000000016-managed-team-access.ts +34 -0
- package/build/migrations-v2/0000000000017-drop-trial-until.ts +42 -0
- package/build/migrations-v2/0000000000017-webhook-secret-reencryption-failures.ts +18 -0
- package/build/migrations-v2/0000000000018-accounting-integrity-monitoring.ts +276 -0
- package/build/migrations-v2/0000000000018-durable-flow-event-sensitive-input.ts +28 -0
- package/build/migrations-v2/0000000000019-accounting-integrity-run-shards.ts +26 -0
- package/build/migrations-v2/0000000000019-asynchronous-operations.ts +736 -0
- package/build/migrations-v2/0000000000020-accounting-integrity-case-classification.ts +52 -0
- package/build/migrations-v2/0000000000021-payment-line-classification-monitor-indexes.ts +31 -0
- package/build/migrations-v2/0000000000022-accounting-integrity-controlled-repairs.ts +281 -0
- package/build/migrations-v2/0000000000023-line-classification-usage.ts +325 -0
- package/build/migrations-v2/0000000000024-tenant-region-migration.ts +108 -0
- package/build/migrations-v2/0000000000025-partner-ach-denial-default.ts +30 -0
- package/build/migrations-v2/0000000000026-line-classification-usage-capture.ts +399 -0
- package/build/migrations-v2/0000000000027-payout-replacement-chain.ts +27 -0
- package/build/migrations-v2/0000000000028-accounting-integrity-operator-workflow.ts +108 -0
- package/build/migrations-v2/0000000000029-accounting-integrity-definition-artifacts.ts +40 -0
- package/build/migrations-v2/0000000000030-accounting-integrity-review-metadata-compatibility.ts +49 -0
- package/build/migrations-v2/0000000000031-ui-permission-role-simplification.ts +70 -0
- package/build/migrations-v2/0000000000032-tenant-delete-ownership-cascades.ts +44 -0
- package/build/migrations-v2/0000000000033-reservation-issue-seed-bucket-index.ts +19 -0
- package/build/migrations-v2/0000000000034-retire-recent-classification-indexes.ts +29 -0
- package/build/migrations-v2/0000000000035-core-change-autovacuum.ts +24 -0
- package/build/migrations-v2/0000000000036-journal-entry-inactive-reason-constraint.ts +33 -0
- package/build/migrations-v2/0000000000037-journal-entry-inactive-reasons.ts +19 -0
- package/build/migrations-v2/0000000000038-journal-entry-group-inactive-reason.ts +14 -0
- package/build/migrations-v2/0000000000039-reservation-guest-breakdown.ts +19 -0
- package/build/migrations-v2/0000000000040-csv-import-source-identity-index.ts +23 -0
- package/build/migrations-v2/0000000000041-payment-returns.ts +38 -0
- package/build/migrations-v2/0000000000042-operation-resource-types.ts +609 -0
- package/build/migrations-v2/0000000000043-reservation-journal-freshness.ts +149 -0
- package/build/migrations-v2/0000000000044-reservation-journal-freshness-backfill.ts +254 -0
- package/build/migrations-v2/0000000000045-reservation-journal-freshness-revision-repair.ts +83 -0
- package/build/migrations-v2/0000000000046-materialized-reservation-journal-operations.ts +709 -0
- package/build/migrations-v2/0000000000047-public-audit-completion.ts +77 -0
- package/build/migrations-v2/0000000000048-reservation-journal-full-refresh-repair.ts +136 -0
- package/build/migrations-v2/0000000000049-queued-journal-operation-failures.ts +60 -0
- package/build/migrations-v2/0000000000050-connection-oauth-state.ts +43 -0
- package/build/migrations-v2/0000000000051-owner-statement-email-delivery.ts +134 -0
- package/build/migrations-v2/0000000000052-fee-journal-lock-skip-operations.ts +59 -0
- package/build/migrations-v2/0000000000053-fee-journal-lock-skip-null-attempt.ts +44 -0
- package/build/migrations-v2/0000000000054-journal-refresh-issue-codes.ts +16 -0
- package/build/module/control-plane/index.d.ts +314 -0
- package/build/module/control-plane/index.js.map +1 -1
- package/build/module/index.d.ts +13 -2
- package/build/module/index.js +219 -24
- package/build/module/index.js.map +1 -1
- package/build/module/local/generate.js +2 -0
- package/build/module/local/generate.js.map +1 -1
- package/build/module/local/index.js +110 -8
- package/build/module/local/index.js.map +1 -1
- package/build/module/postgres-error.d.ts +3 -3
- package/build/module/postgres-error.js +4 -3
- package/build/module/postgres-error.js.map +1 -1
- package/build/module/query/listing/status.js +2 -6
- package/build/module/query/listing/status.js.map +1 -1
- package/build/module/query/tenant/status.d.ts +4 -6
- package/build/module/query/tenant/status.js +17 -14
- package/build/module/query/tenant/status.js.map +1 -1
- package/build/module/regional.d.ts +1 -0
- package/build/module/regional.js +1 -0
- package/build/module/regional.js.map +1 -1
- package/build/module/test/index.d.ts +23 -3
- package/build/module/test/index.js +34 -13
- package/build/module/test/index.js.map +1 -1
- package/build/module/v1.generated.d.ts +560 -7
- package/build/module/v1.generated.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import { sql } from 'kysely';
|
|
3
|
+
|
|
4
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
5
|
+
await sql`
|
|
6
|
+
create type core.flow_event_status as enum (
|
|
7
|
+
'pending', 'processing', 'succeeded', 'dead', 'canceled'
|
|
8
|
+
)
|
|
9
|
+
`.execute(db);
|
|
10
|
+
await sql`
|
|
11
|
+
create type core.flow_event_operation_action as enum (
|
|
12
|
+
'replay', 'abandon'
|
|
13
|
+
)
|
|
14
|
+
`.execute(db);
|
|
15
|
+
|
|
16
|
+
await sql`
|
|
17
|
+
create table core.flow_event (
|
|
18
|
+
id uuid primary key default gen_random_uuid(),
|
|
19
|
+
tenant_id uuid not null,
|
|
20
|
+
event_type text not null,
|
|
21
|
+
entity_type text not null,
|
|
22
|
+
entity_id uuid not null,
|
|
23
|
+
data jsonb not null default '{}'::jsonb,
|
|
24
|
+
sync_id uuid,
|
|
25
|
+
target_connection_id uuid,
|
|
26
|
+
target_flow_id uuid,
|
|
27
|
+
status core.flow_event_status not null default 'pending',
|
|
28
|
+
available_at timestamptz not null default now(),
|
|
29
|
+
claimed_at timestamptz,
|
|
30
|
+
claimed_by text,
|
|
31
|
+
attempts integer not null default 0,
|
|
32
|
+
max_attempts integer not null default 10,
|
|
33
|
+
completed_at timestamptz,
|
|
34
|
+
last_error_code text,
|
|
35
|
+
last_error_details jsonb,
|
|
36
|
+
created_at timestamptz not null default now(),
|
|
37
|
+
updated_at timestamptz not null default now(),
|
|
38
|
+
constraint flow_event_attempts_check check (attempts >= 0),
|
|
39
|
+
constraint flow_event_max_attempts_check check (max_attempts > 0),
|
|
40
|
+
constraint flow_event_target_check check (
|
|
41
|
+
target_connection_id is not null
|
|
42
|
+
or target_flow_id is null
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
`.execute(db);
|
|
46
|
+
|
|
47
|
+
await sql`
|
|
48
|
+
create table core.flow_event_delivery (
|
|
49
|
+
id uuid primary key default gen_random_uuid(),
|
|
50
|
+
event_id uuid not null
|
|
51
|
+
references core.flow_event(id) on delete cascade,
|
|
52
|
+
tenant_id uuid not null,
|
|
53
|
+
connection_id uuid not null,
|
|
54
|
+
flow_id uuid not null,
|
|
55
|
+
status core.flow_event_status not null default 'pending',
|
|
56
|
+
available_at timestamptz not null default now(),
|
|
57
|
+
claimed_at timestamptz,
|
|
58
|
+
claimed_by text,
|
|
59
|
+
attempts integer not null default 0,
|
|
60
|
+
max_attempts integer not null default 10,
|
|
61
|
+
trigger_run_id text,
|
|
62
|
+
completed_at timestamptz,
|
|
63
|
+
last_error_code text,
|
|
64
|
+
last_error_details jsonb,
|
|
65
|
+
created_at timestamptz not null default now(),
|
|
66
|
+
updated_at timestamptz not null default now(),
|
|
67
|
+
constraint flow_event_delivery_attempts_check check (attempts >= 0),
|
|
68
|
+
constraint flow_event_delivery_max_attempts_check
|
|
69
|
+
check (max_attempts > 0),
|
|
70
|
+
constraint flow_event_delivery_listener_key
|
|
71
|
+
unique (event_id, connection_id, flow_id)
|
|
72
|
+
)
|
|
73
|
+
`.execute(db);
|
|
74
|
+
|
|
75
|
+
await sql`
|
|
76
|
+
create table core.flow_event_secret (
|
|
77
|
+
event_id uuid primary key
|
|
78
|
+
references core.flow_event(id) on delete cascade,
|
|
79
|
+
encryption_key_id text not null,
|
|
80
|
+
encrypted_payload text not null,
|
|
81
|
+
expires_at timestamptz not null,
|
|
82
|
+
created_at timestamptz not null default now()
|
|
83
|
+
)
|
|
84
|
+
`.execute(db);
|
|
85
|
+
await sql`
|
|
86
|
+
create table core.flow_event_operation (
|
|
87
|
+
id uuid primary key default gen_random_uuid(),
|
|
88
|
+
event_id uuid not null references core.flow_event(id) on delete cascade,
|
|
89
|
+
action core.flow_event_operation_action not null,
|
|
90
|
+
actor text not null,
|
|
91
|
+
reason_code text not null,
|
|
92
|
+
previous_status core.flow_event_status not null,
|
|
93
|
+
created_at timestamptz not null default now()
|
|
94
|
+
)
|
|
95
|
+
`.execute(db);
|
|
96
|
+
|
|
97
|
+
await sql`
|
|
98
|
+
create index flow_event_pending_idx
|
|
99
|
+
on core.flow_event (available_at, created_at, id)
|
|
100
|
+
where status = 'pending'
|
|
101
|
+
`.execute(db);
|
|
102
|
+
await sql`
|
|
103
|
+
create index flow_event_stale_claim_idx
|
|
104
|
+
on core.flow_event (claimed_at, id)
|
|
105
|
+
where status = 'processing'
|
|
106
|
+
`.execute(db);
|
|
107
|
+
await sql`
|
|
108
|
+
create index flow_event_tenant_history_idx
|
|
109
|
+
on core.flow_event (tenant_id, created_at desc, id desc)
|
|
110
|
+
`.execute(db);
|
|
111
|
+
await sql`
|
|
112
|
+
create index flow_event_sync_idx
|
|
113
|
+
on core.flow_event (tenant_id, sync_id)
|
|
114
|
+
where sync_id is not null
|
|
115
|
+
`.execute(db);
|
|
116
|
+
await sql`
|
|
117
|
+
create index flow_event_delivery_pending_idx
|
|
118
|
+
on core.flow_event_delivery (available_at, created_at, id)
|
|
119
|
+
where status = 'pending'
|
|
120
|
+
`.execute(db);
|
|
121
|
+
await sql`
|
|
122
|
+
create index flow_event_delivery_stale_claim_idx
|
|
123
|
+
on core.flow_event_delivery (claimed_at, id)
|
|
124
|
+
where status = 'processing'
|
|
125
|
+
`.execute(db);
|
|
126
|
+
await sql`
|
|
127
|
+
create index flow_event_delivery_event_idx
|
|
128
|
+
on core.flow_event_delivery (event_id, status, id)
|
|
129
|
+
`.execute(db);
|
|
130
|
+
await sql`
|
|
131
|
+
create index flow_event_secret_expiry_idx
|
|
132
|
+
on core.flow_event_secret (expires_at, event_id)
|
|
133
|
+
`.execute(db);
|
|
134
|
+
await sql`
|
|
135
|
+
create index flow_event_operation_history_idx
|
|
136
|
+
on core.flow_event_operation (event_id, created_at desc, id desc)
|
|
137
|
+
`.execute(db);
|
|
138
|
+
|
|
139
|
+
await sql`
|
|
140
|
+
do $$
|
|
141
|
+
begin
|
|
142
|
+
if exists (select 1 from pg_roles where rolname = 'application') then
|
|
143
|
+
grant usage on schema core to application;
|
|
144
|
+
grant select, insert, update, delete
|
|
145
|
+
on core.flow_event,
|
|
146
|
+
core.flow_event_delivery,
|
|
147
|
+
core.flow_event_operation,
|
|
148
|
+
core.flow_event_secret
|
|
149
|
+
to application;
|
|
150
|
+
end if;
|
|
151
|
+
end
|
|
152
|
+
$$
|
|
153
|
+
`.execute(db);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
157
|
+
await sql`drop table if exists core.flow_event_operation`.execute(db);
|
|
158
|
+
await sql`drop table if exists core.flow_event_secret`.execute(db);
|
|
159
|
+
await sql`drop table if exists core.flow_event_delivery`.execute(db);
|
|
160
|
+
await sql`drop table if exists core.flow_event`.execute(db);
|
|
161
|
+
await sql`drop type if exists core.flow_event_operation_action`.execute(db);
|
|
162
|
+
await sql`drop type if exists core.flow_event_status`.execute(db);
|
|
163
|
+
}
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import { sql } from 'kysely';
|
|
3
|
+
|
|
4
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
5
|
+
await sql`create schema if not exists billing`.execute(db);
|
|
6
|
+
await sql`
|
|
7
|
+
create type billing.listing_fact_source_type as enum (
|
|
8
|
+
'listing_insert',
|
|
9
|
+
'listing_update',
|
|
10
|
+
'listing_delete',
|
|
11
|
+
'ownership_insert',
|
|
12
|
+
'ownership_update',
|
|
13
|
+
'ownership_delete',
|
|
14
|
+
'tenant_insert',
|
|
15
|
+
'tenant_update',
|
|
16
|
+
'tenant_delete',
|
|
17
|
+
'daily_reconciliation',
|
|
18
|
+
'legacy_tracking_import',
|
|
19
|
+
'baseline_backfill'
|
|
20
|
+
)
|
|
21
|
+
`.execute(db);
|
|
22
|
+
await sql`
|
|
23
|
+
create table billing.listing_fact (
|
|
24
|
+
id uuid primary key default gen_random_uuid(),
|
|
25
|
+
tenant_id uuid not null,
|
|
26
|
+
billing_partner_id uuid,
|
|
27
|
+
active_listings integer not null,
|
|
28
|
+
tenant_name text not null,
|
|
29
|
+
data_region text not null,
|
|
30
|
+
effective_at timestamptz not null,
|
|
31
|
+
recorded_at timestamptz not null default now(),
|
|
32
|
+
source_type billing.listing_fact_source_type not null,
|
|
33
|
+
source_id uuid not null,
|
|
34
|
+
source_revision bigint,
|
|
35
|
+
reconciliation boolean not null default false,
|
|
36
|
+
constraint listing_fact_active_listings_check
|
|
37
|
+
check (active_listings >= 0),
|
|
38
|
+
constraint listing_fact_source_revision_check
|
|
39
|
+
check (source_revision is null or source_revision > 0),
|
|
40
|
+
constraint listing_fact_source_key
|
|
41
|
+
unique (tenant_id, source_type, source_id)
|
|
42
|
+
)
|
|
43
|
+
`.execute(db);
|
|
44
|
+
await sql`
|
|
45
|
+
create table billing.listing_fact_readiness (
|
|
46
|
+
singleton boolean primary key default true,
|
|
47
|
+
ready_at timestamptz,
|
|
48
|
+
available_from timestamptz,
|
|
49
|
+
verified_month timestamptz,
|
|
50
|
+
legacy_imported_rows integer,
|
|
51
|
+
legacy_gap_count integer,
|
|
52
|
+
parity_confirmed boolean not null default false,
|
|
53
|
+
constraint listing_fact_readiness_singleton_check check (singleton),
|
|
54
|
+
constraint listing_fact_readiness_count_check check (
|
|
55
|
+
legacy_imported_rows is null or legacy_imported_rows >= 0
|
|
56
|
+
),
|
|
57
|
+
constraint listing_fact_readiness_gap_count_check check (
|
|
58
|
+
legacy_gap_count is null or legacy_gap_count >= 0
|
|
59
|
+
),
|
|
60
|
+
constraint listing_fact_readiness_evidence_check check (
|
|
61
|
+
(
|
|
62
|
+
ready_at is null
|
|
63
|
+
and available_from is null
|
|
64
|
+
and verified_month is null
|
|
65
|
+
and legacy_imported_rows is null
|
|
66
|
+
and legacy_gap_count is null
|
|
67
|
+
and parity_confirmed = false
|
|
68
|
+
) or (
|
|
69
|
+
ready_at is not null
|
|
70
|
+
and available_from is not null
|
|
71
|
+
and verified_month is not null
|
|
72
|
+
and legacy_imported_rows is not null
|
|
73
|
+
and legacy_gap_count is not null
|
|
74
|
+
and parity_confirmed = true
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
`.execute(db);
|
|
79
|
+
await sql`
|
|
80
|
+
insert into billing.listing_fact_readiness (singleton) values (true)
|
|
81
|
+
`.execute(db);
|
|
82
|
+
await sql`
|
|
83
|
+
create index listing_fact_tenant_effective_idx
|
|
84
|
+
on billing.listing_fact (tenant_id, effective_at desc, id desc)
|
|
85
|
+
`.execute(db);
|
|
86
|
+
await sql`
|
|
87
|
+
create index listing_fact_partner_effective_idx
|
|
88
|
+
on billing.listing_fact (billing_partner_id, effective_at, tenant_id)
|
|
89
|
+
`.execute(db);
|
|
90
|
+
await sql`
|
|
91
|
+
create index listing_fact_region_recorded_idx
|
|
92
|
+
on billing.listing_fact (data_region, recorded_at, id)
|
|
93
|
+
`.execute(db);
|
|
94
|
+
|
|
95
|
+
await sql`
|
|
96
|
+
create function billing.active_listing_count(
|
|
97
|
+
selected_tenant_id uuid
|
|
98
|
+
) returns integer
|
|
99
|
+
language sql
|
|
100
|
+
stable
|
|
101
|
+
as $$
|
|
102
|
+
select count(*)::integer
|
|
103
|
+
from public.listing as listing
|
|
104
|
+
inner join public.tenant as tenant
|
|
105
|
+
on tenant.id = listing.tenant_id
|
|
106
|
+
left join public.tenant as billing_partner
|
|
107
|
+
on billing_partner.id = tenant.calculated_billing_partner_id
|
|
108
|
+
where listing.tenant_id = selected_tenant_id
|
|
109
|
+
and tenant.calculated_status = 'active'
|
|
110
|
+
and (
|
|
111
|
+
tenant.calculated_billing_partner_id is null
|
|
112
|
+
or billing_partner.calculated_status = 'active'
|
|
113
|
+
)
|
|
114
|
+
and (
|
|
115
|
+
(
|
|
116
|
+
tenant.is_general_ledger = false
|
|
117
|
+
and listing.calculated_status = 'active'
|
|
118
|
+
) or (
|
|
119
|
+
tenant.is_general_ledger = true
|
|
120
|
+
and not exists (
|
|
121
|
+
select 1
|
|
122
|
+
from public.listing_ownership_period as ownership
|
|
123
|
+
where ownership.listing_id = coalesce(
|
|
124
|
+
listing.parent_id,
|
|
125
|
+
listing.id
|
|
126
|
+
)
|
|
127
|
+
and ownership.tenant_id = listing.tenant_id
|
|
128
|
+
and ownership.set_listing_inactive = true
|
|
129
|
+
and ownership.start_at <= current_date
|
|
130
|
+
and (
|
|
131
|
+
ownership.end_at is null
|
|
132
|
+
or ownership.end_at >= current_date
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
$$
|
|
138
|
+
`.execute(db);
|
|
139
|
+
await sql`
|
|
140
|
+
create function billing.record_listing_facts(
|
|
141
|
+
selected_tenant_ids uuid[],
|
|
142
|
+
selected_source_type billing.listing_fact_source_type,
|
|
143
|
+
selected_source_id uuid,
|
|
144
|
+
selected_effective_at timestamptz,
|
|
145
|
+
selected_reconciliation boolean
|
|
146
|
+
) returns void
|
|
147
|
+
language sql
|
|
148
|
+
as $$
|
|
149
|
+
with calculated as materialized (
|
|
150
|
+
select tenant.id as tenant_id,
|
|
151
|
+
tenant.calculated_billing_partner_id as billing_partner_id,
|
|
152
|
+
billing.active_listing_count(tenant.id) as active_listings,
|
|
153
|
+
tenant.name as tenant_name,
|
|
154
|
+
tenant.data_region as data_region
|
|
155
|
+
from public.tenant as tenant
|
|
156
|
+
where tenant.id = any(selected_tenant_ids)
|
|
157
|
+
and tenant.type = 'propertyManager'
|
|
158
|
+
), changed as (
|
|
159
|
+
select calculated.*
|
|
160
|
+
from calculated
|
|
161
|
+
left join lateral (
|
|
162
|
+
select fact.id,
|
|
163
|
+
fact.billing_partner_id,
|
|
164
|
+
fact.active_listings,
|
|
165
|
+
fact.tenant_name,
|
|
166
|
+
fact.data_region
|
|
167
|
+
from billing.listing_fact as fact
|
|
168
|
+
where fact.tenant_id = calculated.tenant_id
|
|
169
|
+
order by fact.effective_at desc, fact.recorded_at desc, fact.id desc
|
|
170
|
+
limit 1
|
|
171
|
+
) as latest on true
|
|
172
|
+
where latest.id is null
|
|
173
|
+
or (
|
|
174
|
+
latest.billing_partner_id,
|
|
175
|
+
latest.active_listings,
|
|
176
|
+
latest.tenant_name,
|
|
177
|
+
latest.data_region
|
|
178
|
+
) is distinct from (
|
|
179
|
+
calculated.billing_partner_id,
|
|
180
|
+
calculated.active_listings,
|
|
181
|
+
calculated.tenant_name,
|
|
182
|
+
calculated.data_region
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
insert into billing.listing_fact (
|
|
186
|
+
tenant_id,
|
|
187
|
+
billing_partner_id,
|
|
188
|
+
active_listings,
|
|
189
|
+
tenant_name,
|
|
190
|
+
data_region,
|
|
191
|
+
effective_at,
|
|
192
|
+
source_type,
|
|
193
|
+
source_id,
|
|
194
|
+
reconciliation
|
|
195
|
+
)
|
|
196
|
+
select tenant_id,
|
|
197
|
+
billing_partner_id,
|
|
198
|
+
active_listings,
|
|
199
|
+
tenant_name,
|
|
200
|
+
data_region,
|
|
201
|
+
selected_effective_at,
|
|
202
|
+
selected_source_type,
|
|
203
|
+
selected_source_id,
|
|
204
|
+
selected_reconciliation
|
|
205
|
+
from changed
|
|
206
|
+
on conflict (tenant_id, source_type, source_id) do nothing
|
|
207
|
+
$$
|
|
208
|
+
`.execute(db);
|
|
209
|
+
await sql`
|
|
210
|
+
create function billing.capture_listing_insert() returns trigger
|
|
211
|
+
language plpgsql as $$
|
|
212
|
+
begin
|
|
213
|
+
perform billing.record_listing_facts(
|
|
214
|
+
array(select distinct tenant_id from new_rows),
|
|
215
|
+
'listing_insert',
|
|
216
|
+
gen_random_uuid(),
|
|
217
|
+
now(),
|
|
218
|
+
false
|
|
219
|
+
);
|
|
220
|
+
return null;
|
|
221
|
+
end
|
|
222
|
+
$$
|
|
223
|
+
`.execute(db);
|
|
224
|
+
await sql`
|
|
225
|
+
create function billing.capture_listing_update() returns trigger
|
|
226
|
+
language plpgsql as $$
|
|
227
|
+
begin
|
|
228
|
+
perform billing.record_listing_facts(
|
|
229
|
+
array(
|
|
230
|
+
select new_row.tenant_id
|
|
231
|
+
from new_rows as new_row
|
|
232
|
+
inner join old_rows as old_row on old_row.id = new_row.id
|
|
233
|
+
where (
|
|
234
|
+
new_row.tenant_id,
|
|
235
|
+
new_row.parent_id,
|
|
236
|
+
new_row.calculated_status
|
|
237
|
+
) is distinct from (
|
|
238
|
+
old_row.tenant_id,
|
|
239
|
+
old_row.parent_id,
|
|
240
|
+
old_row.calculated_status
|
|
241
|
+
)
|
|
242
|
+
union
|
|
243
|
+
select old_row.tenant_id
|
|
244
|
+
from new_rows as new_row
|
|
245
|
+
inner join old_rows as old_row on old_row.id = new_row.id
|
|
246
|
+
where (
|
|
247
|
+
new_row.tenant_id,
|
|
248
|
+
new_row.parent_id,
|
|
249
|
+
new_row.calculated_status
|
|
250
|
+
) is distinct from (
|
|
251
|
+
old_row.tenant_id,
|
|
252
|
+
old_row.parent_id,
|
|
253
|
+
old_row.calculated_status
|
|
254
|
+
)
|
|
255
|
+
),
|
|
256
|
+
'listing_update',
|
|
257
|
+
gen_random_uuid(),
|
|
258
|
+
now(),
|
|
259
|
+
false
|
|
260
|
+
);
|
|
261
|
+
return null;
|
|
262
|
+
end
|
|
263
|
+
$$
|
|
264
|
+
`.execute(db);
|
|
265
|
+
await sql`
|
|
266
|
+
create function billing.capture_listing_delete() returns trigger
|
|
267
|
+
language plpgsql as $$
|
|
268
|
+
begin
|
|
269
|
+
perform billing.record_listing_facts(
|
|
270
|
+
array(select distinct tenant_id from old_rows),
|
|
271
|
+
'listing_delete',
|
|
272
|
+
gen_random_uuid(),
|
|
273
|
+
now(),
|
|
274
|
+
false
|
|
275
|
+
);
|
|
276
|
+
return null;
|
|
277
|
+
end
|
|
278
|
+
$$
|
|
279
|
+
`.execute(db);
|
|
280
|
+
await sql`
|
|
281
|
+
create function billing.capture_ownership_insert() returns trigger
|
|
282
|
+
language plpgsql as $$
|
|
283
|
+
begin
|
|
284
|
+
perform billing.record_listing_facts(
|
|
285
|
+
array(select distinct tenant_id from new_rows),
|
|
286
|
+
'ownership_insert',
|
|
287
|
+
gen_random_uuid(),
|
|
288
|
+
now(),
|
|
289
|
+
false
|
|
290
|
+
);
|
|
291
|
+
return null;
|
|
292
|
+
end
|
|
293
|
+
$$
|
|
294
|
+
`.execute(db);
|
|
295
|
+
await sql`
|
|
296
|
+
create function billing.capture_ownership_update() returns trigger
|
|
297
|
+
language plpgsql as $$
|
|
298
|
+
begin
|
|
299
|
+
perform billing.record_listing_facts(
|
|
300
|
+
array(
|
|
301
|
+
select new_row.tenant_id
|
|
302
|
+
from new_rows as new_row
|
|
303
|
+
inner join old_rows as old_row on old_row.id = new_row.id
|
|
304
|
+
where (
|
|
305
|
+
new_row.tenant_id,
|
|
306
|
+
new_row.listing_id,
|
|
307
|
+
new_row.set_listing_inactive,
|
|
308
|
+
new_row.start_at,
|
|
309
|
+
new_row.end_at
|
|
310
|
+
) is distinct from (
|
|
311
|
+
old_row.tenant_id,
|
|
312
|
+
old_row.listing_id,
|
|
313
|
+
old_row.set_listing_inactive,
|
|
314
|
+
old_row.start_at,
|
|
315
|
+
old_row.end_at
|
|
316
|
+
)
|
|
317
|
+
union
|
|
318
|
+
select old_row.tenant_id
|
|
319
|
+
from new_rows as new_row
|
|
320
|
+
inner join old_rows as old_row on old_row.id = new_row.id
|
|
321
|
+
where (
|
|
322
|
+
new_row.tenant_id,
|
|
323
|
+
new_row.listing_id,
|
|
324
|
+
new_row.set_listing_inactive,
|
|
325
|
+
new_row.start_at,
|
|
326
|
+
new_row.end_at
|
|
327
|
+
) is distinct from (
|
|
328
|
+
old_row.tenant_id,
|
|
329
|
+
old_row.listing_id,
|
|
330
|
+
old_row.set_listing_inactive,
|
|
331
|
+
old_row.start_at,
|
|
332
|
+
old_row.end_at
|
|
333
|
+
)
|
|
334
|
+
),
|
|
335
|
+
'ownership_update',
|
|
336
|
+
gen_random_uuid(),
|
|
337
|
+
now(),
|
|
338
|
+
false
|
|
339
|
+
);
|
|
340
|
+
return null;
|
|
341
|
+
end
|
|
342
|
+
$$
|
|
343
|
+
`.execute(db);
|
|
344
|
+
await sql`
|
|
345
|
+
create function billing.capture_ownership_delete() returns trigger
|
|
346
|
+
language plpgsql as $$
|
|
347
|
+
begin
|
|
348
|
+
perform billing.record_listing_facts(
|
|
349
|
+
array(select distinct tenant_id from old_rows),
|
|
350
|
+
'ownership_delete',
|
|
351
|
+
gen_random_uuid(),
|
|
352
|
+
now(),
|
|
353
|
+
false
|
|
354
|
+
);
|
|
355
|
+
return null;
|
|
356
|
+
end
|
|
357
|
+
$$
|
|
358
|
+
`.execute(db);
|
|
359
|
+
await sql`
|
|
360
|
+
create function billing.capture_tenant_insert() returns trigger
|
|
361
|
+
language plpgsql as $$
|
|
362
|
+
begin
|
|
363
|
+
perform billing.record_listing_facts(
|
|
364
|
+
array(select distinct id from new_rows),
|
|
365
|
+
'tenant_insert',
|
|
366
|
+
gen_random_uuid(),
|
|
367
|
+
now(),
|
|
368
|
+
false
|
|
369
|
+
);
|
|
370
|
+
return null;
|
|
371
|
+
end
|
|
372
|
+
$$
|
|
373
|
+
`.execute(db);
|
|
374
|
+
await sql`
|
|
375
|
+
create function billing.capture_tenant_update() returns trigger
|
|
376
|
+
language plpgsql as $$
|
|
377
|
+
begin
|
|
378
|
+
perform billing.record_listing_facts(
|
|
379
|
+
array(
|
|
380
|
+
select new_row.id
|
|
381
|
+
from new_rows as new_row
|
|
382
|
+
inner join old_rows as old_row on old_row.id = new_row.id
|
|
383
|
+
where (
|
|
384
|
+
new_row.type,
|
|
385
|
+
new_row.name,
|
|
386
|
+
new_row.data_region,
|
|
387
|
+
new_row.calculated_billing_partner_id,
|
|
388
|
+
new_row.calculated_status,
|
|
389
|
+
new_row.is_general_ledger
|
|
390
|
+
) is distinct from (
|
|
391
|
+
old_row.type,
|
|
392
|
+
old_row.name,
|
|
393
|
+
old_row.data_region,
|
|
394
|
+
old_row.calculated_billing_partner_id,
|
|
395
|
+
old_row.calculated_status,
|
|
396
|
+
old_row.is_general_ledger
|
|
397
|
+
)
|
|
398
|
+
),
|
|
399
|
+
'tenant_update',
|
|
400
|
+
gen_random_uuid(),
|
|
401
|
+
now(),
|
|
402
|
+
false
|
|
403
|
+
);
|
|
404
|
+
return null;
|
|
405
|
+
end
|
|
406
|
+
$$
|
|
407
|
+
`.execute(db);
|
|
408
|
+
await sql`
|
|
409
|
+
create function billing.capture_tenant_delete() returns trigger
|
|
410
|
+
language plpgsql as $$
|
|
411
|
+
begin
|
|
412
|
+
if old.type = 'propertyManager' then
|
|
413
|
+
insert into billing.listing_fact (
|
|
414
|
+
tenant_id,
|
|
415
|
+
billing_partner_id,
|
|
416
|
+
active_listings,
|
|
417
|
+
tenant_name,
|
|
418
|
+
data_region,
|
|
419
|
+
effective_at,
|
|
420
|
+
source_type,
|
|
421
|
+
source_id
|
|
422
|
+
) values (
|
|
423
|
+
old.id,
|
|
424
|
+
old.calculated_billing_partner_id,
|
|
425
|
+
0,
|
|
426
|
+
old.name,
|
|
427
|
+
old.data_region,
|
|
428
|
+
now(),
|
|
429
|
+
'tenant_delete',
|
|
430
|
+
gen_random_uuid()
|
|
431
|
+
);
|
|
432
|
+
end if;
|
|
433
|
+
return old;
|
|
434
|
+
end
|
|
435
|
+
$$
|
|
436
|
+
`.execute(db);
|
|
437
|
+
|
|
438
|
+
await sql`
|
|
439
|
+
create trigger billing_listing_insert
|
|
440
|
+
after insert on public.listing
|
|
441
|
+
referencing new table as new_rows
|
|
442
|
+
for each statement execute function billing.capture_listing_insert()
|
|
443
|
+
`.execute(db);
|
|
444
|
+
await sql`
|
|
445
|
+
create trigger billing_listing_update
|
|
446
|
+
after update on public.listing
|
|
447
|
+
referencing old table as old_rows new table as new_rows
|
|
448
|
+
for each statement execute function billing.capture_listing_update()
|
|
449
|
+
`.execute(db);
|
|
450
|
+
await sql`
|
|
451
|
+
create trigger billing_listing_delete
|
|
452
|
+
after delete on public.listing
|
|
453
|
+
referencing old table as old_rows
|
|
454
|
+
for each statement execute function billing.capture_listing_delete()
|
|
455
|
+
`.execute(db);
|
|
456
|
+
await sql`
|
|
457
|
+
create trigger billing_ownership_insert
|
|
458
|
+
after insert on public.listing_ownership_period
|
|
459
|
+
referencing new table as new_rows
|
|
460
|
+
for each statement execute function billing.capture_ownership_insert()
|
|
461
|
+
`.execute(db);
|
|
462
|
+
await sql`
|
|
463
|
+
create trigger billing_ownership_update
|
|
464
|
+
after update on public.listing_ownership_period
|
|
465
|
+
referencing old table as old_rows new table as new_rows
|
|
466
|
+
for each statement execute function billing.capture_ownership_update()
|
|
467
|
+
`.execute(db);
|
|
468
|
+
await sql`
|
|
469
|
+
create trigger billing_ownership_delete
|
|
470
|
+
after delete on public.listing_ownership_period
|
|
471
|
+
referencing old table as old_rows
|
|
472
|
+
for each statement execute function billing.capture_ownership_delete()
|
|
473
|
+
`.execute(db);
|
|
474
|
+
await sql`
|
|
475
|
+
create trigger billing_tenant_insert
|
|
476
|
+
after insert on public.tenant
|
|
477
|
+
referencing new table as new_rows
|
|
478
|
+
for each statement execute function billing.capture_tenant_insert()
|
|
479
|
+
`.execute(db);
|
|
480
|
+
await sql`
|
|
481
|
+
create trigger billing_tenant_update
|
|
482
|
+
after update on public.tenant
|
|
483
|
+
referencing old table as old_rows new table as new_rows
|
|
484
|
+
for each statement execute function billing.capture_tenant_update()
|
|
485
|
+
`.execute(db);
|
|
486
|
+
await sql`
|
|
487
|
+
create trigger billing_tenant_delete
|
|
488
|
+
before delete on public.tenant
|
|
489
|
+
for each row execute function billing.capture_tenant_delete()
|
|
490
|
+
`.execute(db);
|
|
491
|
+
|
|
492
|
+
await sql`
|
|
493
|
+
do $$
|
|
494
|
+
begin
|
|
495
|
+
if exists (select 1 from pg_roles where rolname = 'application') then
|
|
496
|
+
grant usage on schema billing to application;
|
|
497
|
+
grant select, insert on billing.listing_fact to application;
|
|
498
|
+
grant select on billing.listing_fact_readiness to application;
|
|
499
|
+
grant execute on function billing.active_listing_count(uuid)
|
|
500
|
+
to application;
|
|
501
|
+
grant execute on function billing.record_listing_facts(
|
|
502
|
+
uuid[], billing.listing_fact_source_type, uuid, timestamptz, boolean
|
|
503
|
+
) to application;
|
|
504
|
+
end if;
|
|
505
|
+
end
|
|
506
|
+
$$
|
|
507
|
+
`.execute(db);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
511
|
+
await sql`drop trigger if exists billing_tenant_delete on public.tenant`.execute(
|
|
512
|
+
db
|
|
513
|
+
);
|
|
514
|
+
await sql`drop trigger if exists billing_tenant_update on public.tenant`.execute(
|
|
515
|
+
db
|
|
516
|
+
);
|
|
517
|
+
await sql`drop trigger if exists billing_tenant_insert on public.tenant`.execute(
|
|
518
|
+
db
|
|
519
|
+
);
|
|
520
|
+
await sql`drop trigger if exists billing_ownership_delete on public.listing_ownership_period`.execute(
|
|
521
|
+
db
|
|
522
|
+
);
|
|
523
|
+
await sql`drop trigger if exists billing_ownership_update on public.listing_ownership_period`.execute(
|
|
524
|
+
db
|
|
525
|
+
);
|
|
526
|
+
await sql`drop trigger if exists billing_ownership_insert on public.listing_ownership_period`.execute(
|
|
527
|
+
db
|
|
528
|
+
);
|
|
529
|
+
await sql`drop trigger if exists billing_listing_delete on public.listing`.execute(
|
|
530
|
+
db
|
|
531
|
+
);
|
|
532
|
+
await sql`drop trigger if exists billing_listing_update on public.listing`.execute(
|
|
533
|
+
db
|
|
534
|
+
);
|
|
535
|
+
await sql`drop trigger if exists billing_listing_insert on public.listing`.execute(
|
|
536
|
+
db
|
|
537
|
+
);
|
|
538
|
+
await sql`drop schema if exists billing cascade`.execute(db);
|
|
539
|
+
}
|