@vrplatform/kysely 1.3.45-736 → 1.3.45-819
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/main/v1.generated.d.ts +9 -0
- package/build/main/v1.generated.js.map +1 -1
- package/build/migrations-v2/0000000000033-plaid-connect-session-telemetry.ts +31 -0
- package/build/migrations-v2/0000000000056-journal-operation-failure-precedence.ts +306 -0
- package/build/migrations-v2/0000000000057-journal-invalidation-failure-code.ts +28 -0
- package/build/migrations-v2/0000000000058-journal-invalidation-locked-failure.ts +16 -0
- package/build/migrations-v2/0000000000059-billing-active-listing-cross-region-partner.ts +135 -0
- package/build/module/v1.generated.d.ts +9 -0
- package/build/module/v1.generated.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { type Kysely, sql } from 'kysely';
|
|
2
|
+
|
|
3
|
+
// The served `core.operation.failure_code` projection must agree with the
|
|
4
|
+
// TypeScript classifier in `packages/repository/src/journal-freshness/load.ts`:
|
|
5
|
+
// a governing accounting lock wins over a retained failure (CUS-2936), and a
|
|
6
|
+
// retained unbalanced attempt is recognized by its message prefix as well as by
|
|
7
|
+
// the typed result code (CUS-2894).
|
|
8
|
+
const failureProjection = (ordering: 'lock-first' | 'failure-first') => sql`
|
|
9
|
+
create or replace function core.refresh_operation_state(
|
|
10
|
+
target_operation_id uuid
|
|
11
|
+
) returns void
|
|
12
|
+
language plpgsql
|
|
13
|
+
as $$
|
|
14
|
+
declare
|
|
15
|
+
next_status core.operation_status;
|
|
16
|
+
next_started_at timestamptz;
|
|
17
|
+
next_finished_at timestamptz;
|
|
18
|
+
next_failure_code text;
|
|
19
|
+
next_failure_message text;
|
|
20
|
+
target_operation_type core.operation_type;
|
|
21
|
+
target_public_id_format core.operation_public_id_format;
|
|
22
|
+
target_action_id uuid;
|
|
23
|
+
begin
|
|
24
|
+
select
|
|
25
|
+
operation.type,
|
|
26
|
+
operation.public_id_format,
|
|
27
|
+
source.source_id
|
|
28
|
+
into
|
|
29
|
+
target_operation_type,
|
|
30
|
+
target_public_id_format,
|
|
31
|
+
target_action_id
|
|
32
|
+
from core.operation as operation
|
|
33
|
+
left join core.operation_source as source
|
|
34
|
+
on source.operation_id = operation.id
|
|
35
|
+
and source.source_type = 'audit-action'
|
|
36
|
+
where operation.id = target_operation_id;
|
|
37
|
+
|
|
38
|
+
select
|
|
39
|
+
case
|
|
40
|
+
when count(*) filter (where required) = 0 then 'queued'
|
|
41
|
+
when bool_and(status = 'completed') filter (where required)
|
|
42
|
+
then 'completed'
|
|
43
|
+
when bool_and(status in ('completed', 'failed')) filter (where required)
|
|
44
|
+
then 'failed'
|
|
45
|
+
when bool_or(status = 'running') filter (where required)
|
|
46
|
+
or bool_or(status in ('completed', 'failed')) filter (where required)
|
|
47
|
+
then 'running'
|
|
48
|
+
else 'queued'
|
|
49
|
+
end::core.operation_status,
|
|
50
|
+
min(started_at) filter (where required),
|
|
51
|
+
max(finished_at) filter (where required)
|
|
52
|
+
into next_status, next_started_at, next_finished_at
|
|
53
|
+
from core.operation_source
|
|
54
|
+
where operation_id = target_operation_id;
|
|
55
|
+
|
|
56
|
+
if next_status = 'failed' then
|
|
57
|
+
if target_operation_type = 'reservation-journal-refresh'
|
|
58
|
+
and target_action_id is not null then
|
|
59
|
+
select
|
|
60
|
+
case
|
|
61
|
+
${sql.raw(
|
|
62
|
+
ordering === 'lock-first'
|
|
63
|
+
? `when core.journal_refresh_all_outstanding_locked(
|
|
64
|
+
bool_and(
|
|
65
|
+
case
|
|
66
|
+
when invalidation.id is not null
|
|
67
|
+
and invalidation.applied_at is null
|
|
68
|
+
then effect.status = 'completed'
|
|
69
|
+
and attempt.status is not distinct from 'skipped_locked'
|
|
70
|
+
end
|
|
71
|
+
),
|
|
72
|
+
bool_and(
|
|
73
|
+
case
|
|
74
|
+
when invalidation.id is not null
|
|
75
|
+
and invalidation.applied_at is null
|
|
76
|
+
then core.journal_reservation_refresh_locked(
|
|
77
|
+
invalidation.tenant_id,
|
|
78
|
+
invalidation.reservation_id
|
|
79
|
+
)
|
|
80
|
+
end
|
|
81
|
+
)
|
|
82
|
+
) then 'journalLocked'
|
|
83
|
+
when bool_or(
|
|
84
|
+
invalidation.applied_at is null
|
|
85
|
+
and attempt.result_code = 'journal_configuration_invalid'
|
|
86
|
+
) then 'journalConfigurationInvalid'
|
|
87
|
+
when bool_or(
|
|
88
|
+
invalidation.applied_at is null
|
|
89
|
+
and (
|
|
90
|
+
attempt.result_code = 'journal_unbalanced'
|
|
91
|
+
or attempt.error_message like 'Journal refresh produced unbalanced%'
|
|
92
|
+
)
|
|
93
|
+
) then 'journalUnbalanced'`
|
|
94
|
+
: `when bool_or(
|
|
95
|
+
invalidation.applied_at is null
|
|
96
|
+
and attempt.result_code = 'journal_unbalanced'
|
|
97
|
+
) then 'journalUnbalanced'
|
|
98
|
+
when bool_or(
|
|
99
|
+
invalidation.applied_at is null
|
|
100
|
+
and attempt.result_code = 'journal_configuration_invalid'
|
|
101
|
+
) then 'journalConfigurationInvalid'
|
|
102
|
+
when bool_or(
|
|
103
|
+
invalidation.applied_at is null
|
|
104
|
+
and attempt.result_code in (
|
|
105
|
+
'statement_period_locked',
|
|
106
|
+
'skipped_locked'
|
|
107
|
+
)
|
|
108
|
+
) then 'journalLocked'`
|
|
109
|
+
)}
|
|
110
|
+
else 'journalRefreshFailed'
|
|
111
|
+
end,
|
|
112
|
+
case
|
|
113
|
+
${sql.raw(
|
|
114
|
+
ordering === 'lock-first'
|
|
115
|
+
? `when core.journal_refresh_all_outstanding_locked(
|
|
116
|
+
bool_and(
|
|
117
|
+
case
|
|
118
|
+
when invalidation.id is not null
|
|
119
|
+
and invalidation.applied_at is null
|
|
120
|
+
then effect.status = 'completed'
|
|
121
|
+
and attempt.status is not distinct from 'skipped_locked'
|
|
122
|
+
end
|
|
123
|
+
),
|
|
124
|
+
bool_and(
|
|
125
|
+
case
|
|
126
|
+
when invalidation.id is not null
|
|
127
|
+
and invalidation.applied_at is null
|
|
128
|
+
then core.journal_reservation_refresh_locked(
|
|
129
|
+
invalidation.tenant_id,
|
|
130
|
+
invalidation.reservation_id
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
)
|
|
134
|
+
) then 'Journal refresh is blocked by accounting locks'
|
|
135
|
+
when bool_or(
|
|
136
|
+
invalidation.applied_at is null
|
|
137
|
+
and attempt.result_code = 'journal_configuration_invalid'
|
|
138
|
+
) then 'Journal configuration is invalid'
|
|
139
|
+
when bool_or(
|
|
140
|
+
invalidation.applied_at is null
|
|
141
|
+
and (
|
|
142
|
+
attempt.result_code = 'journal_unbalanced'
|
|
143
|
+
or attempt.error_message like 'Journal refresh produced unbalanced%'
|
|
144
|
+
)
|
|
145
|
+
) then 'Journal refresh produced unbalanced entries'`
|
|
146
|
+
: `when bool_or(
|
|
147
|
+
invalidation.applied_at is null
|
|
148
|
+
and attempt.result_code = 'journal_unbalanced'
|
|
149
|
+
) then 'Journal refresh produced unbalanced entries'
|
|
150
|
+
when bool_or(
|
|
151
|
+
invalidation.applied_at is null
|
|
152
|
+
and attempt.result_code = 'journal_configuration_invalid'
|
|
153
|
+
) then 'Journal configuration is invalid'
|
|
154
|
+
when bool_or(
|
|
155
|
+
invalidation.applied_at is null
|
|
156
|
+
and attempt.result_code in (
|
|
157
|
+
'statement_period_locked',
|
|
158
|
+
'skipped_locked'
|
|
159
|
+
)
|
|
160
|
+
) then 'Journal refresh is blocked by accounting locks'`
|
|
161
|
+
)}
|
|
162
|
+
else 'Journal refresh failed'
|
|
163
|
+
end
|
|
164
|
+
into next_failure_code, next_failure_message
|
|
165
|
+
from audit.effect as effect
|
|
166
|
+
left join accounting.reservation_journal_invalidation as invalidation
|
|
167
|
+
on invalidation.tenant_id = effect.tenant_id
|
|
168
|
+
and invalidation.effect_id = effect.id
|
|
169
|
+
left join lateral (
|
|
170
|
+
select
|
|
171
|
+
effect_attempt.status,
|
|
172
|
+
effect_attempt.result_code,
|
|
173
|
+
effect_attempt.error_message
|
|
174
|
+
from audit.effect_attempt as effect_attempt
|
|
175
|
+
where effect_attempt.tenant_id = effect.tenant_id
|
|
176
|
+
and effect_attempt.effect_id = effect.id
|
|
177
|
+
order by effect_attempt.attempt_num desc
|
|
178
|
+
limit 1
|
|
179
|
+
) as attempt on true
|
|
180
|
+
where effect.action_id = target_action_id
|
|
181
|
+
and effect.effect_type = 'REFRESH_RESERVATION_JOURNAL'
|
|
182
|
+
and effect.entity_type = 'reservation';
|
|
183
|
+
else
|
|
184
|
+
next_failure_code := 'operation_failed';
|
|
185
|
+
next_failure_message := 'Operation failed';
|
|
186
|
+
end if;
|
|
187
|
+
else
|
|
188
|
+
next_failure_code := null;
|
|
189
|
+
next_failure_message := null;
|
|
190
|
+
end if;
|
|
191
|
+
|
|
192
|
+
update core.operation
|
|
193
|
+
set
|
|
194
|
+
status = next_status,
|
|
195
|
+
started_at = coalesce(core.operation.started_at, next_started_at),
|
|
196
|
+
finished_at = case
|
|
197
|
+
when next_status in ('completed', 'failed')
|
|
198
|
+
then coalesce(next_finished_at, now())
|
|
199
|
+
else null
|
|
200
|
+
end,
|
|
201
|
+
failure_code = next_failure_code,
|
|
202
|
+
failure_message = next_failure_message,
|
|
203
|
+
updated_at = now(),
|
|
204
|
+
source_version = source_version + 1
|
|
205
|
+
where id = target_operation_id
|
|
206
|
+
and (
|
|
207
|
+
status is distinct from next_status
|
|
208
|
+
or started_at is distinct from coalesce(started_at, next_started_at)
|
|
209
|
+
or finished_at is distinct from case
|
|
210
|
+
when next_status in ('completed', 'failed')
|
|
211
|
+
then coalesce(next_finished_at, finished_at, now())
|
|
212
|
+
else null
|
|
213
|
+
end
|
|
214
|
+
or failure_code is distinct from next_failure_code
|
|
215
|
+
or failure_message is distinct from next_failure_message
|
|
216
|
+
);
|
|
217
|
+
end;
|
|
218
|
+
$$
|
|
219
|
+
`;
|
|
220
|
+
|
|
221
|
+
// The reservation lock driver from `isReservationRefreshLocked`: the refresh is
|
|
222
|
+
// locked when every active reservation entry sits in a published statement or
|
|
223
|
+
// in closed books.
|
|
224
|
+
const reservationRefreshLocked = sql`
|
|
225
|
+
create or replace function core.journal_reservation_refresh_locked(
|
|
226
|
+
target_tenant_id uuid,
|
|
227
|
+
target_reservation_id uuid
|
|
228
|
+
) returns boolean
|
|
229
|
+
language sql
|
|
230
|
+
stable
|
|
231
|
+
as $$
|
|
232
|
+
select exists (
|
|
233
|
+
select 1
|
|
234
|
+
from accounting.journal_entry as entry
|
|
235
|
+
inner join public.tenant as tenant on tenant.id = entry.tenant_id
|
|
236
|
+
where entry.tenant_id = target_tenant_id
|
|
237
|
+
and entry.reservation_id = target_reservation_id
|
|
238
|
+
and entry.status = 'active'
|
|
239
|
+
and (entry.entity_type is null or entry.entity_type = 'reservation')
|
|
240
|
+
and (
|
|
241
|
+
entry.attached_to_owner_statement_id is not null
|
|
242
|
+
or entry.transfer_owner_statement_id is not null
|
|
243
|
+
or entry.txn_at < tenant.books_closed_at
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
$$
|
|
247
|
+
`;
|
|
248
|
+
|
|
249
|
+
// `bool_and` over no outstanding invalidation returns null; the classifier
|
|
250
|
+
// treats that as "not locked".
|
|
251
|
+
const allOutstandingLocked = sql`
|
|
252
|
+
create or replace function core.journal_refresh_all_outstanding_locked(
|
|
253
|
+
all_lock_skipped boolean,
|
|
254
|
+
all_reservations_locked boolean
|
|
255
|
+
) returns boolean
|
|
256
|
+
language sql
|
|
257
|
+
immutable
|
|
258
|
+
as $$
|
|
259
|
+
select coalesce(all_lock_skipped, false) or coalesce(all_reservations_locked, false)
|
|
260
|
+
$$
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
async function recomputeFailedJournalOperations(db: Kysely<unknown>) {
|
|
264
|
+
// Derived operation state only. Attempts, invalidations and journals are untouched.
|
|
265
|
+
await sql`
|
|
266
|
+
select core.refresh_operation_state(operation.id)
|
|
267
|
+
from core.operation as operation
|
|
268
|
+
where operation.type = 'reservation-journal-refresh'
|
|
269
|
+
and operation.status = 'failed'
|
|
270
|
+
and exists (
|
|
271
|
+
select 1 from core.operation_source as source
|
|
272
|
+
where source.operation_id = operation.id
|
|
273
|
+
and source.source_type = 'audit-action'
|
|
274
|
+
)
|
|
275
|
+
`.execute(db);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
279
|
+
await sql`
|
|
280
|
+
do $$
|
|
281
|
+
begin
|
|
282
|
+
if position(
|
|
283
|
+
'and target_public_id_format = ''action'''
|
|
284
|
+
in pg_get_functiondef('core.refresh_operation_state(uuid)'::regprocedure)
|
|
285
|
+
) > 0 then
|
|
286
|
+
raise exception 'Journal operation failure projection has an unexpected definition';
|
|
287
|
+
end if;
|
|
288
|
+
end;
|
|
289
|
+
$$
|
|
290
|
+
`.execute(db);
|
|
291
|
+
await reservationRefreshLocked.execute(db);
|
|
292
|
+
await allOutstandingLocked.execute(db);
|
|
293
|
+
await failureProjection('lock-first').execute(db);
|
|
294
|
+
await recomputeFailedJournalOperations(db);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
298
|
+
await failureProjection('failure-first').execute(db);
|
|
299
|
+
await recomputeFailedJournalOperations(db);
|
|
300
|
+
await sql`drop function if exists core.journal_refresh_all_outstanding_locked(boolean, boolean)`.execute(
|
|
301
|
+
db
|
|
302
|
+
);
|
|
303
|
+
await sql`drop function if exists core.journal_reservation_refresh_locked(uuid, uuid)`.execute(
|
|
304
|
+
db
|
|
305
|
+
);
|
|
306
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import { sql } from 'kysely';
|
|
3
|
+
|
|
4
|
+
// An inline reservation journal refresh has no effect row, so a refresh that
|
|
5
|
+
// completes without writing the reservation's postings had nowhere to record
|
|
6
|
+
// why. Storage only: nullable column, no default, no backfill, no rewrite.
|
|
7
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
8
|
+
await sql`
|
|
9
|
+
create type accounting.reservation_journal_invalidation_failure as enum (
|
|
10
|
+
'journal_configuration_invalid'
|
|
11
|
+
)
|
|
12
|
+
`.execute(db);
|
|
13
|
+
await sql`
|
|
14
|
+
alter table accounting.reservation_journal_invalidation
|
|
15
|
+
add column failure_code
|
|
16
|
+
accounting.reservation_journal_invalidation_failure
|
|
17
|
+
`.execute(db);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
21
|
+
await sql`
|
|
22
|
+
alter table accounting.reservation_journal_invalidation
|
|
23
|
+
drop column if exists failure_code
|
|
24
|
+
`.execute(db);
|
|
25
|
+
await sql`
|
|
26
|
+
drop type if exists accounting.reservation_journal_invalidation_failure
|
|
27
|
+
`.execute(db);
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import { sql } from 'kysely';
|
|
3
|
+
|
|
4
|
+
// An inline reservation journal refresh refused by an accounting lock records
|
|
5
|
+
// why on its invalidation, like a discarded build does. Storage only: the
|
|
6
|
+
// value must be readable on every channel before the writer starts using it.
|
|
7
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
8
|
+
await sql`
|
|
9
|
+
alter type accounting.reservation_journal_invalidation_failure
|
|
10
|
+
add value if not exists 'journal_locked'
|
|
11
|
+
`.execute(db);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function down(_db: Kysely<unknown>): Promise<void> {
|
|
15
|
+
// PostgreSQL enum values cannot be removed safely without rebuilding the type.
|
|
16
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import { sql } from 'kysely';
|
|
3
|
+
|
|
4
|
+
// CUS-2977: a billing partner homed in another data region has no row in this
|
|
5
|
+
// regional database. That absence is valid and must not make the team count
|
|
6
|
+
// zero active listings. Only a locally present, non-active billing partner
|
|
7
|
+
// does, matching kysely `whereTenantActive` (PRO-17712). `create or replace`
|
|
8
|
+
// keeps the existing grants.
|
|
9
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
10
|
+
await sql`
|
|
11
|
+
create or replace function billing.active_listing_count(
|
|
12
|
+
selected_tenant_id uuid
|
|
13
|
+
) returns integer
|
|
14
|
+
language sql
|
|
15
|
+
stable
|
|
16
|
+
as $$
|
|
17
|
+
select count(*)::integer
|
|
18
|
+
from public.listing as listing
|
|
19
|
+
inner join public.tenant as tenant
|
|
20
|
+
on tenant.id = listing.tenant_id
|
|
21
|
+
where listing.tenant_id = selected_tenant_id
|
|
22
|
+
and tenant.calculated_status = 'active'
|
|
23
|
+
and not exists (
|
|
24
|
+
select 1
|
|
25
|
+
from public.tenant as billing_partner
|
|
26
|
+
where billing_partner.id = coalesce(
|
|
27
|
+
tenant.calculated_billing_partner_id,
|
|
28
|
+
'7bfec41f-aa3f-4602-95d2-f7996e4f0e59'::uuid
|
|
29
|
+
)
|
|
30
|
+
and billing_partner.calculated_status is distinct from 'active'
|
|
31
|
+
)
|
|
32
|
+
and (
|
|
33
|
+
(
|
|
34
|
+
tenant.is_general_ledger = false
|
|
35
|
+
and listing.calculated_status = 'active'
|
|
36
|
+
) or (
|
|
37
|
+
tenant.is_general_ledger = true
|
|
38
|
+
and not exists (
|
|
39
|
+
select 1
|
|
40
|
+
from public.listing_ownership_period as ownership
|
|
41
|
+
where ownership.listing_id = coalesce(
|
|
42
|
+
listing.parent_id,
|
|
43
|
+
listing.id
|
|
44
|
+
)
|
|
45
|
+
and ownership.tenant_id = listing.tenant_id
|
|
46
|
+
and ownership.set_listing_inactive = true
|
|
47
|
+
and ownership.start_at <= current_date
|
|
48
|
+
and (
|
|
49
|
+
ownership.end_at is null
|
|
50
|
+
or ownership.end_at >= current_date
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
$$
|
|
56
|
+
`.execute(db);
|
|
57
|
+
|
|
58
|
+
// Append a corrected fact, effective now, for every active team whose
|
|
59
|
+
// billing partner has no local row. These are the only teams whose count
|
|
60
|
+
// changes. Completed-month billing reads the latest fact before the month
|
|
61
|
+
// ends, so applying this before the month closes corrects that month.
|
|
62
|
+
// Earlier wrong facts stay as recorded history.
|
|
63
|
+
await sql`
|
|
64
|
+
select billing.record_listing_facts(
|
|
65
|
+
array(
|
|
66
|
+
select tenant.id
|
|
67
|
+
from public.tenant as tenant
|
|
68
|
+
where tenant.type = 'propertyManager'
|
|
69
|
+
and tenant.calculated_status = 'active'
|
|
70
|
+
and tenant.calculated_billing_partner_id is not null
|
|
71
|
+
and not exists (
|
|
72
|
+
select 1
|
|
73
|
+
from public.tenant as billing_partner
|
|
74
|
+
where billing_partner.id = tenant.calculated_billing_partner_id
|
|
75
|
+
)
|
|
76
|
+
and exists (
|
|
77
|
+
select 1
|
|
78
|
+
from billing.listing_fact as fact
|
|
79
|
+
where fact.tenant_id = tenant.id
|
|
80
|
+
)
|
|
81
|
+
),
|
|
82
|
+
'daily_reconciliation',
|
|
83
|
+
gen_random_uuid(),
|
|
84
|
+
now(),
|
|
85
|
+
true
|
|
86
|
+
)
|
|
87
|
+
`.execute(db);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
91
|
+
await sql`
|
|
92
|
+
create or replace function billing.active_listing_count(
|
|
93
|
+
selected_tenant_id uuid
|
|
94
|
+
) returns integer
|
|
95
|
+
language sql
|
|
96
|
+
stable
|
|
97
|
+
as $$
|
|
98
|
+
select count(*)::integer
|
|
99
|
+
from public.listing as listing
|
|
100
|
+
inner join public.tenant as tenant
|
|
101
|
+
on tenant.id = listing.tenant_id
|
|
102
|
+
left join public.tenant as billing_partner
|
|
103
|
+
on billing_partner.id = tenant.calculated_billing_partner_id
|
|
104
|
+
where listing.tenant_id = selected_tenant_id
|
|
105
|
+
and tenant.calculated_status = 'active'
|
|
106
|
+
and (
|
|
107
|
+
tenant.calculated_billing_partner_id is null
|
|
108
|
+
or billing_partner.calculated_status = 'active'
|
|
109
|
+
)
|
|
110
|
+
and (
|
|
111
|
+
(
|
|
112
|
+
tenant.is_general_ledger = false
|
|
113
|
+
and listing.calculated_status = 'active'
|
|
114
|
+
) or (
|
|
115
|
+
tenant.is_general_ledger = true
|
|
116
|
+
and not exists (
|
|
117
|
+
select 1
|
|
118
|
+
from public.listing_ownership_period as ownership
|
|
119
|
+
where ownership.listing_id = coalesce(
|
|
120
|
+
listing.parent_id,
|
|
121
|
+
listing.id
|
|
122
|
+
)
|
|
123
|
+
and ownership.tenant_id = listing.tenant_id
|
|
124
|
+
and ownership.set_listing_inactive = true
|
|
125
|
+
and ownership.start_at <= current_date
|
|
126
|
+
and (
|
|
127
|
+
ownership.end_at is null
|
|
128
|
+
or ownership.end_at >= current_date
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
$$
|
|
134
|
+
`.execute(db);
|
|
135
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { ColumnType } from "kysely";
|
|
6
6
|
export type AccountingJournalEntryInactiveReason = "cancelledReservationLine" | "currencyMismatch" | "generalLedgerStartAt" | "historicalLedger" | "inactiveAccount" | "listingInactive" | "listingOwnershipPeriodNotFound" | "nonPostingAccount" | "reservationGeneralLedgerStatusInactive" | "transactionInactive" | "unbalancedGroupInactiveLeg" | "unknownInactiveReason";
|
|
7
7
|
export type AccountingJournalEntryLedger = "historical" | "operating" | "trust";
|
|
8
|
+
export type AccountingReservationJournalInvalidationFailure = "journal_configuration_invalid" | "journal_locked";
|
|
8
9
|
export type AccountingTransactionAttachmentStatus = "attached" | "deleting" | "ready" | "uploading";
|
|
9
10
|
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>;
|
|
10
11
|
export type ArrayTypeImpl<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S[], I[], U[]> : T[];
|
|
@@ -418,6 +419,7 @@ export interface AccountingReservationJournalInvalidation {
|
|
|
418
419
|
coveredByEffectId: string | null;
|
|
419
420
|
createdAt: Generated<Timestamp>;
|
|
420
421
|
effectId: string | null;
|
|
422
|
+
failureCode: AccountingReservationJournalInvalidationFailure | null;
|
|
421
423
|
id: Generated<string>;
|
|
422
424
|
reservationId: string;
|
|
423
425
|
revision: Int8;
|
|
@@ -2040,8 +2042,15 @@ export interface PublicPlaidConnectSession {
|
|
|
2040
2042
|
environment: string;
|
|
2041
2043
|
errorCode: string | null;
|
|
2042
2044
|
errorRetryable: boolean | null;
|
|
2045
|
+
exitedAt: Timestamp | null;
|
|
2046
|
+
exitErrorCode: string | null;
|
|
2047
|
+
exitErrorType: string | null;
|
|
2048
|
+
exitRequestId: string | null;
|
|
2049
|
+
exitView: string | null;
|
|
2043
2050
|
expiresAt: Timestamp;
|
|
2044
2051
|
id: Generated<string>;
|
|
2052
|
+
institutionId: string | null;
|
|
2053
|
+
institutionName: string | null;
|
|
2045
2054
|
itemId: string | null;
|
|
2046
2055
|
leaseExpiresAt: Timestamp | null;
|
|
2047
2056
|
linkSessionId: string | null;
|