feat: transactions table
This commit is contained in:
parent
1ead9fe359
commit
d6166ce752
29 changed files with 1204 additions and 726 deletions
|
|
@ -3,6 +3,10 @@
|
|||
"version": "11.0.0-beta.0",
|
||||
"license": "../LICENSE",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"kysely": "^0.28.2",
|
||||
"pg": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/pg": "^8.11.10",
|
||||
|
|
@ -18,11 +22,17 @@
|
|||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"dev": "tsc --watch",
|
||||
"generate-types": "kysely-codegen --camel-case --out-file ./src/types/types.d.ts",
|
||||
"generate-types": "kysely-codegen",
|
||||
"postinstall": "npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"kysely": "^0.28.2",
|
||||
"pg": "^8.16.0"
|
||||
"kysely-codegen": {
|
||||
"camelCase": true,
|
||||
"outFile": "./src/types/types.d.ts",
|
||||
"overrides": {
|
||||
"columns": {
|
||||
"customers.id_card_data": "{firstName:string, lastName:string}",
|
||||
"edited_customer_data.id_card_data": "{firstName:string, lastName:string}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
import { sql } from 'kysely'
|
||||
import db from './db.js'
|
||||
import { ExpressionBuilder } from 'kysely'
|
||||
import { Customers, DB, EditedCustomerData } from './types/types.js'
|
||||
import { jsonArrayFrom } from 'kysely/helpers/postgres'
|
||||
|
||||
type CustomerEB = ExpressionBuilder<DB & { c: Customers }, 'c'>
|
||||
type CustomerWithEditedEB = ExpressionBuilder<
|
||||
DB & { c: Customers } & { e: EditedCustomerData | null },
|
||||
'c' | 'e'
|
||||
>
|
||||
import type {
|
||||
CustomerEB,
|
||||
CustomerWithEditedDataEB,
|
||||
} from './types/manual.types.js'
|
||||
|
||||
const ANON_ID = '47ac1184-8102-11e7-9079-8f13a7117867'
|
||||
const TX_PASSTHROUGH_ERROR_CODES = [
|
||||
|
|
@ -28,7 +25,7 @@ function transactionUnion(eb: CustomerEB) {
|
|||
])
|
||||
.where(({ eb, and, or, ref }) =>
|
||||
and([
|
||||
eb('customerId', '=', ref('c.id')),
|
||||
eb('customerId', '=', ref('cst.id')),
|
||||
or([eb('sendConfirmed', '=', true), eb('batched', '=', true)]),
|
||||
]),
|
||||
)
|
||||
|
|
@ -44,7 +41,7 @@ function transactionUnion(eb: CustomerEB) {
|
|||
])
|
||||
.where(({ eb, and, ref }) =>
|
||||
and([
|
||||
eb('customerId', '=', ref('c.id')),
|
||||
eb('customerId', '=', ref('cst.id')),
|
||||
eb('confirmedAt', 'is not', null),
|
||||
]),
|
||||
),
|
||||
|
|
@ -92,20 +89,20 @@ function joinTxsTotals(eb: CustomerEB) {
|
|||
.as('txStats')
|
||||
}
|
||||
|
||||
function selectNewestIdCardData(eb: CustomerWithEditedEB, ref: any) {
|
||||
function selectNewestIdCardData({ eb, ref }: CustomerWithEditedDataEB) {
|
||||
return eb
|
||||
.case()
|
||||
.when(
|
||||
eb.and([
|
||||
eb(ref('e.idCardDataAt'), 'is not', null),
|
||||
eb(ref('cstED.idCardDataAt'), 'is not', null),
|
||||
eb.or([
|
||||
eb(ref('c.idCardDataAt'), 'is', null),
|
||||
eb(ref('e.idCardDataAt'), '>', ref('c.idCardDataAt')),
|
||||
eb(ref('cst.idCardDataAt'), 'is', null),
|
||||
eb(ref('cstED.idCardDataAt'), '>', ref('cst.idCardDataAt')),
|
||||
]),
|
||||
]),
|
||||
)
|
||||
.then(ref('e.idCardData'))
|
||||
.else(ref('c.idCardData'))
|
||||
.then(ref('cstED.idCardData'))
|
||||
.else(ref('cst.idCardData'))
|
||||
.end()
|
||||
}
|
||||
|
||||
|
|
@ -122,58 +119,58 @@ function getCustomerList(
|
|||
options: GetCustomerListOptions = defaultOptions,
|
||||
): Promise<any[]> {
|
||||
return db
|
||||
.selectFrom('customers as c')
|
||||
.leftJoin('editedCustomerData as e', 'e.customerId', 'c.id')
|
||||
.selectFrom('customers as cst')
|
||||
.leftJoin('editedCustomerData as cstED', 'cstED.customerId', 'cst.id')
|
||||
.leftJoinLateral(joinTxsTotals, join => join.onTrue())
|
||||
.leftJoinLateral(joinLatestTx, join => join.onTrue())
|
||||
.select(({ eb, fn, val, ref }) => [
|
||||
'c.id',
|
||||
'c.phone',
|
||||
'c.authorizedOverride',
|
||||
'c.frontCameraPath',
|
||||
'c.frontCameraOverride',
|
||||
'c.idCardPhotoPath',
|
||||
'c.idCardPhotoOverride',
|
||||
selectNewestIdCardData(eb, ref).as('idCardData'),
|
||||
'c.idCardDataOverride',
|
||||
'c.email',
|
||||
'c.usSsn',
|
||||
'c.usSsnOverride',
|
||||
'c.sanctions',
|
||||
'c.sanctionsOverride',
|
||||
.select(({ eb, fn, val }) => [
|
||||
'cst.id',
|
||||
'cst.phone',
|
||||
'cst.authorizedOverride',
|
||||
'cst.frontCameraPath',
|
||||
'cst.frontCameraOverride',
|
||||
'cst.idCardPhotoPath',
|
||||
'cst.idCardPhotoOverride',
|
||||
selectNewestIdCardData(eb).as('idCardData'),
|
||||
'cst.idCardDataOverride',
|
||||
'cst.email',
|
||||
'cst.usSsn',
|
||||
'cst.usSsnOverride',
|
||||
'cst.sanctions',
|
||||
'cst.sanctionsOverride',
|
||||
'txStats.totalSpent',
|
||||
'txStats.totalTxs',
|
||||
ref('lastTx.fiatCode').as('lastTxFiatCode'),
|
||||
ref('lastTx.fiat').as('lastTxFiat'),
|
||||
ref('lastTx.txClass').as('lastTxClass'),
|
||||
'lastTx.fiatCode as lastTxFiatCode',
|
||||
'lastTx.fiat as lastTxFiat',
|
||||
'lastTx.txClass as lastTxClass',
|
||||
fn<Date>('GREATEST', [
|
||||
'c.created',
|
||||
'cst.created',
|
||||
'lastTx.created',
|
||||
'c.phoneAt',
|
||||
'c.emailAt',
|
||||
'c.idCardDataAt',
|
||||
'c.frontCameraAt',
|
||||
'c.idCardPhotoAt',
|
||||
'c.usSsnAt',
|
||||
'c.lastAuthAttempt',
|
||||
'cst.phoneAt',
|
||||
'cst.emailAt',
|
||||
'cst.idCardDataAt',
|
||||
'cst.frontCameraAt',
|
||||
'cst.idCardPhotoAt',
|
||||
'cst.usSsnAt',
|
||||
'cst.lastAuthAttempt',
|
||||
]).as('lastActive'),
|
||||
eb('c.suspendedUntil', '>', fn<Date>('NOW', [])).as('isSuspended'),
|
||||
eb('cst.suspendedUntil', '>', fn<Date>('NOW', [])).as('isSuspended'),
|
||||
fn<number>('GREATEST', [
|
||||
val(0),
|
||||
fn<number>('date_part', [
|
||||
val('day'),
|
||||
eb('c.suspendedUntil', '-', fn<Date>('NOW', [])),
|
||||
eb('cst.suspendedUntil', '-', fn<Date>('NOW', [])),
|
||||
]),
|
||||
]).as('daysSuspended'),
|
||||
])
|
||||
.where('c.id', '!=', ANON_ID)
|
||||
.where('cst.id', '!=', ANON_ID)
|
||||
.$if(options.withCustomInfoRequest, qb =>
|
||||
qb.select(({ eb, ref }) =>
|
||||
jsonArrayFrom(
|
||||
eb
|
||||
.selectFrom('customersCustomInfoRequests')
|
||||
.selectAll()
|
||||
.where('customerId', '=', ref('c.id')),
|
||||
.where('customerId', '=', ref('cst.id')),
|
||||
).as('customInfoRequestData'),
|
||||
),
|
||||
)
|
||||
|
|
@ -181,4 +178,39 @@ function getCustomerList(
|
|||
.execute()
|
||||
}
|
||||
|
||||
export { getCustomerList }
|
||||
function searchCustomers(searchTerm: string, limit: number = 20): Promise<any> {
|
||||
const searchPattern = `%${searchTerm}%`
|
||||
|
||||
return db
|
||||
.selectFrom(
|
||||
db
|
||||
.selectFrom('customers as cst')
|
||||
.leftJoin('editedCustomerData as cstED', 'cstED.customerId', 'cst.id')
|
||||
.select(({ eb, fn }) => [
|
||||
'cst.id',
|
||||
'cst.phone',
|
||||
'cst.email',
|
||||
sql`CONCAT(
|
||||
COALESCE(${selectNewestIdCardData(eb)}->>'firstName', ''),
|
||||
' ',
|
||||
COALESCE(${selectNewestIdCardData(eb)}->>'lastName', '')
|
||||
)`.as('customerName'),
|
||||
])
|
||||
.where('cst.id', '!=', ANON_ID)
|
||||
.as('customers_with_names'),
|
||||
)
|
||||
.selectAll()
|
||||
.select('customerName as name')
|
||||
.where(({ eb, or }) =>
|
||||
or([
|
||||
eb('phone', 'ilike', searchPattern),
|
||||
eb('email', 'ilike', searchPattern),
|
||||
eb('customerName', 'ilike', searchPattern),
|
||||
]),
|
||||
)
|
||||
.orderBy('id')
|
||||
.limit(limit)
|
||||
.execute()
|
||||
}
|
||||
|
||||
export { getCustomerList, selectNewestIdCardData, searchCustomers }
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export * as customers from './customers.js'
|
||||
export * as transactions from './transactions.js'
|
||||
|
|
|
|||
30
packages/typesafe-db/src/interpolled-query-logger.ts
Normal file
30
packages/typesafe-db/src/interpolled-query-logger.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
export function logQuery(compiledQuery: {
|
||||
sql: string
|
||||
parameters: readonly unknown[]
|
||||
}) {
|
||||
const { sql, parameters } = compiledQuery
|
||||
|
||||
let interpolatedSql = sql
|
||||
let paramIndex = 0
|
||||
|
||||
interpolatedSql = sql.replace(/\$\d+|\?/g, () => {
|
||||
const param = parameters[paramIndex++]
|
||||
|
||||
if (param === null || param === undefined) {
|
||||
return 'NULL'
|
||||
} else if (typeof param === 'string') {
|
||||
return `'${param.replace(/'/g, "''")}'`
|
||||
} else if (typeof param === 'boolean') {
|
||||
return param.toString()
|
||||
} else if (param instanceof Date) {
|
||||
return `'${param.toISOString()}'`
|
||||
} else if (typeof param === 'object') {
|
||||
return `'${JSON.stringify(param).replace(/'/g, "''")}'`
|
||||
} else {
|
||||
return String(param)
|
||||
}
|
||||
})
|
||||
|
||||
console.log('📝 Query:', interpolatedSql)
|
||||
return interpolatedSql
|
||||
}
|
||||
319
packages/typesafe-db/src/transactions.ts
Normal file
319
packages/typesafe-db/src/transactions.ts
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
import { sql } from 'kysely'
|
||||
import db from './db.js'
|
||||
import type {
|
||||
CashInWithBatchEB,
|
||||
CashOutEB,
|
||||
CustomerWithEditedDataEB,
|
||||
DevicesAndUnpairedDevicesEB,
|
||||
} from './types/manual.types.js'
|
||||
import { selectNewestIdCardData } from './customers.js'
|
||||
|
||||
const PENDING_INTERVAL = '60 minutes'
|
||||
const REDEEMABLE_INTERVAL = '24 hours'
|
||||
|
||||
function getDeviceName(eb: DevicesAndUnpairedDevicesEB) {
|
||||
return eb
|
||||
.case()
|
||||
.when(eb('ud.name', 'is not', null))
|
||||
.then(eb('ud.name', '||', ' (unpaired)'))
|
||||
.when(eb('d.name', 'is not', null))
|
||||
.then(eb.ref('d.name'))
|
||||
.else('Unpaired')
|
||||
.end()
|
||||
}
|
||||
|
||||
function customerData({ eb, ref }: CustomerWithEditedDataEB) {
|
||||
return [
|
||||
ref('cst.phone').as('customerPhone'),
|
||||
ref('cst.email').as('customerEmail'),
|
||||
selectNewestIdCardData(eb).as('customerIdCardData'),
|
||||
ref('cst.frontCameraPath').as('customerFrontCameraPath'),
|
||||
ref('cst.idCardPhotoPath').as('customerIdCardPhotoPath'),
|
||||
ref('cst.isTestCustomer').as('isTestCustomer'),
|
||||
]
|
||||
}
|
||||
|
||||
function isCashInExpired(eb: CashInWithBatchEB) {
|
||||
return eb.and([
|
||||
eb.not('txIn.sendConfirmed'),
|
||||
eb(
|
||||
'txIn.created',
|
||||
'<=',
|
||||
sql<Date>`now() - interval '${sql.raw(PENDING_INTERVAL)}'`,
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
function isCashOutExpired(eb: CashOutEB) {
|
||||
return eb.and([
|
||||
eb.not('txOut.dispense'),
|
||||
eb(
|
||||
eb.fn.coalesce('txOut.confirmed_at', 'txOut.created'),
|
||||
'<=',
|
||||
sql<Date>`now() - interval '${sql.raw(REDEEMABLE_INTERVAL)}'`,
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
function cashOutTransactionStates(eb: CashOutEB) {
|
||||
return eb
|
||||
.case()
|
||||
.when(eb('txOut.error', '=', eb.val('Operator cancel')))
|
||||
.then('Cancelled')
|
||||
.when(eb('txOut.error', 'is not', null))
|
||||
.then('Error')
|
||||
.when(eb.ref('txOut.dispense'))
|
||||
.then('Success')
|
||||
.when(isCashOutExpired(eb))
|
||||
.then('Expired')
|
||||
.else('Pending')
|
||||
.end()
|
||||
}
|
||||
|
||||
function cashInTransactionStates(eb: CashInWithBatchEB) {
|
||||
const operatorCancel = eb.and([
|
||||
eb.ref('txIn.operatorCompleted'),
|
||||
eb('txIn.error', '=', eb.val('Operator cancel')),
|
||||
])
|
||||
|
||||
const hasError = eb.or([
|
||||
eb('txIn.error', 'is not', null),
|
||||
eb('txInB.errorMessage', 'is not', null),
|
||||
])
|
||||
|
||||
return eb
|
||||
.case()
|
||||
.when(operatorCancel)
|
||||
.then('Cancelled')
|
||||
.when(hasError)
|
||||
.then('Error')
|
||||
.when(eb.ref('txIn.sendConfirmed'))
|
||||
.then('Sent')
|
||||
.when(isCashInExpired(eb))
|
||||
.then('Expired')
|
||||
.else('Pending')
|
||||
.end()
|
||||
}
|
||||
|
||||
function getCashOutTransactionList() {
|
||||
return db
|
||||
.selectFrom('cashOutTxs as txOut')
|
||||
.leftJoin('customers as cst', 'cst.id', 'txOut.customerId')
|
||||
.leftJoin('editedCustomerData as cstED', 'cst.id', 'cstED.customerId')
|
||||
.innerJoin('cashOutActions as txOutActions', join =>
|
||||
join
|
||||
.onRef('txOut.id', '=', 'txOutActions.txId')
|
||||
.on('txOutActions.action', '=', 'provisionAddress'),
|
||||
)
|
||||
.leftJoin('devices as d', 'd.deviceId', 'txOut.deviceId')
|
||||
.leftJoin('unpairedDevices as ud', join =>
|
||||
join
|
||||
.onRef('txOut.deviceId', '=', 'ud.deviceId')
|
||||
.on('ud.unpaired', '>=', eb => eb.ref('txOut.created'))
|
||||
.on('txOut.created', '>=', eb => eb.ref('ud.paired')),
|
||||
)
|
||||
.select(({ eb, val }) => [
|
||||
'txOut.id',
|
||||
val('cashOut').as('txClass'),
|
||||
'txOut.deviceId',
|
||||
'txOut.toAddress',
|
||||
'txOut.cryptoAtoms',
|
||||
'txOut.cryptoCode',
|
||||
'txOut.fiat',
|
||||
'txOut.fiatCode',
|
||||
'txOut.phone', // TODO why does this has phone? Why not get from customer?
|
||||
'txOut.error',
|
||||
'txOut.created',
|
||||
'txOut.timedout',
|
||||
'txOut.errorCode',
|
||||
'txOut.fixedFee',
|
||||
'txOut.txVersion',
|
||||
'txOut.termsAccepted',
|
||||
'txOut.commissionPercentage',
|
||||
'txOut.rawTickerPrice',
|
||||
isCashOutExpired(eb).as('expired'),
|
||||
getDeviceName(eb).as('machineName'),
|
||||
'txOut.discount',
|
||||
cashOutTransactionStates(eb).as('status'),
|
||||
'txOut.customerId',
|
||||
...customerData(eb),
|
||||
'txOut.txCustomerPhotoPath',
|
||||
'txOut.txCustomerPhotoAt',
|
||||
'txOut.walletScore',
|
||||
// cash-in only
|
||||
val(null).as('fee'),
|
||||
val(null).as('txHash'),
|
||||
val(false).as('send'),
|
||||
val(false).as('sendConfirmed'),
|
||||
val(null).as('sendTime'),
|
||||
val(false).as('operatorCompleted'),
|
||||
val(false).as('sendPending'),
|
||||
val(0).as('minimumTx'),
|
||||
val(null).as('isPaperWallet'),
|
||||
val(false).as('batched'),
|
||||
val(null).as('batchTime'),
|
||||
val(null).as('batchError'),
|
||||
// cash-out only
|
||||
'txOut.dispense',
|
||||
'txOut.swept',
|
||||
])
|
||||
}
|
||||
|
||||
function getCashInTransactionList() {
|
||||
return db
|
||||
.selectFrom('cashInTxs as txIn')
|
||||
.leftJoin('customers as cst', 'cst.id', 'txIn.customerId')
|
||||
.leftJoin('editedCustomerData as cstED', 'cst.id', 'cstED.customerId')
|
||||
.leftJoin('transactionBatches as txInB', 'txInB.id', 'txIn.batchId')
|
||||
.leftJoin('devices as d', 'd.deviceId', 'txIn.deviceId')
|
||||
.leftJoin('unpairedDevices as ud', join =>
|
||||
join
|
||||
.onRef('txIn.deviceId', '=', 'ud.deviceId')
|
||||
.on('ud.unpaired', '>=', eb => eb.ref('txIn.created'))
|
||||
.on('txIn.created', '>=', eb => eb.ref('ud.paired')),
|
||||
)
|
||||
.select(({ eb, val }) => [
|
||||
'txIn.id',
|
||||
val('cashIn').as('txClass'),
|
||||
'txIn.deviceId',
|
||||
'txIn.toAddress',
|
||||
'txIn.cryptoAtoms',
|
||||
'txIn.cryptoCode',
|
||||
'txIn.fiat',
|
||||
'txIn.fiatCode',
|
||||
'txIn.phone', // TODO why does this has phone? Why not get from customer?
|
||||
'txIn.error',
|
||||
'txIn.created',
|
||||
'txIn.timedout',
|
||||
'txIn.errorCode',
|
||||
'txIn.cashInFee as fixedFee',
|
||||
'txIn.txVersion',
|
||||
'txIn.termsAccepted',
|
||||
'txIn.commissionPercentage',
|
||||
'txIn.rawTickerPrice',
|
||||
isCashInExpired(eb).as('expired'),
|
||||
getDeviceName(eb).as('machineName'),
|
||||
'txIn.discount',
|
||||
cashInTransactionStates(eb).as('status'),
|
||||
'txIn.customerId',
|
||||
...customerData(eb),
|
||||
'txIn.txCustomerPhotoPath',
|
||||
'txIn.txCustomerPhotoAt',
|
||||
'txIn.walletScore',
|
||||
// cash-in only
|
||||
'txIn.fee',
|
||||
'txIn.txHash',
|
||||
'txIn.send',
|
||||
'txIn.sendConfirmed',
|
||||
'txIn.sendTime',
|
||||
'txIn.operatorCompleted',
|
||||
'txIn.sendPending',
|
||||
'txIn.minimumTx',
|
||||
'txIn.isPaperWallet',
|
||||
'txInB.errorMessage as batchError',
|
||||
'txIn.batched',
|
||||
'txIn.batchTime',
|
||||
// cash-out only
|
||||
val(false).as('dispense'),
|
||||
val(false).as('swept'),
|
||||
])
|
||||
}
|
||||
|
||||
interface PaginationParams {
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
interface FilterParams {
|
||||
from?: Date
|
||||
until?: Date
|
||||
toAddress?: string
|
||||
txClass?: string
|
||||
deviceId?: string
|
||||
customerId?: string
|
||||
cryptoCode?: string
|
||||
swept?: boolean
|
||||
status?: string
|
||||
excludeTestingCustomers?: boolean
|
||||
}
|
||||
|
||||
async function getTransactionList(
|
||||
filters: FilterParams,
|
||||
pagination?: PaginationParams,
|
||||
) {
|
||||
let query = db
|
||||
.selectFrom(() =>
|
||||
getCashInTransactionList()
|
||||
.unionAll(getCashOutTransactionList())
|
||||
.as('transactions'),
|
||||
)
|
||||
.selectAll('transactions')
|
||||
.select(eb =>
|
||||
sql<{
|
||||
totalCount: number
|
||||
}>`json_build_object(${sql.lit('totalCount')}, ${eb.fn.count('transactions.id').over()})`.as(
|
||||
'paginationStats',
|
||||
),
|
||||
)
|
||||
.orderBy('transactions.created', 'desc')
|
||||
|
||||
if (filters.toAddress) {
|
||||
query = query.where(
|
||||
'transactions.toAddress',
|
||||
'like',
|
||||
`%${filters.toAddress}%`,
|
||||
)
|
||||
}
|
||||
|
||||
if (filters.from) {
|
||||
query = query.where('transactions.created', '>=', filters.from)
|
||||
}
|
||||
|
||||
if (filters.until) {
|
||||
query = query.where('transactions.created', '<=', filters.until)
|
||||
}
|
||||
|
||||
if (filters.deviceId) {
|
||||
query = query.where('transactions.deviceId', '=', filters.deviceId)
|
||||
}
|
||||
|
||||
if (filters.txClass) {
|
||||
query = query.where('transactions.txClass', '=', filters.txClass)
|
||||
}
|
||||
|
||||
if (filters.customerId) {
|
||||
query = query.where('transactions.customerId', '=', filters.customerId)
|
||||
}
|
||||
|
||||
if (filters.cryptoCode) {
|
||||
query = query.where('transactions.cryptoCode', '=', filters.cryptoCode)
|
||||
}
|
||||
|
||||
if (filters.swept) {
|
||||
query = query.where('transactions.swept', '=', filters.swept)
|
||||
}
|
||||
|
||||
if (filters.status) {
|
||||
query = query.where('transactions.status', '=', filters.status)
|
||||
}
|
||||
|
||||
if (filters.excludeTestingCustomers) {
|
||||
query = query.where('transactions.isTestCustomer', '=', false)
|
||||
}
|
||||
|
||||
if (pagination?.limit) {
|
||||
query = query.limit(pagination.limit)
|
||||
}
|
||||
|
||||
if (pagination?.offset) {
|
||||
query = query.offset(pagination.offset)
|
||||
}
|
||||
|
||||
return query.execute()
|
||||
}
|
||||
|
||||
export {
|
||||
getTransactionList,
|
||||
getCashInTransactionList,
|
||||
getCashOutTransactionList,
|
||||
}
|
||||
35
packages/typesafe-db/src/types/manual.types.d.ts
vendored
Normal file
35
packages/typesafe-db/src/types/manual.types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import type { ExpressionBuilder } from 'kysely'
|
||||
import {
|
||||
CashInTxs,
|
||||
Customers,
|
||||
DB,
|
||||
Devices,
|
||||
EditedCustomerData,
|
||||
TransactionBatches,
|
||||
UnpairedDevices,
|
||||
} from './types.js'
|
||||
import { Nullable } from 'kysely/dist/esm/index.js'
|
||||
|
||||
export type CustomerEB = ExpressionBuilder<DB & { cst: Customers }, 'cst'>
|
||||
export type CustomerWithEditedDataEB = ExpressionBuilder<
|
||||
DB & { cst: Customers } & { cstED: EditedCustomerData },
|
||||
'cst' | 'cstED'
|
||||
>
|
||||
export type CashInEB = ExpressionBuilder<DB & { txIn: CashInTxs }, 'txIn'>
|
||||
export type CashInWithBatchEB = ExpressionBuilder<
|
||||
DB & { txIn: CashInTxs } & {
|
||||
txInB: TransactionBatches
|
||||
},
|
||||
'txIn' | 'txInB'
|
||||
>
|
||||
|
||||
export type CashOutEB = ExpressionBuilder<DB & { txOut: CashOutTxs }, 'txOut'>
|
||||
|
||||
export type DevicesAndUnpairedDevicesEB = ExpressionBuilder<
|
||||
DB & { d: Nullable<Devices> } & {
|
||||
ud: Nullable<UnpairedDevices>
|
||||
},
|
||||
'd' | 'ud'
|
||||
>
|
||||
|
||||
export type GenericEB = ExpressionBuilder<DB, any>
|
||||
4
packages/typesafe-db/src/types/types.d.ts
vendored
4
packages/typesafe-db/src/types/types.d.ts
vendored
|
|
@ -399,7 +399,7 @@ export interface Customers {
|
|||
frontCameraOverrideBy: string | null
|
||||
frontCameraPath: string | null
|
||||
id: string
|
||||
idCardData: Json | null
|
||||
idCardData: { firstName: string; lastName: string }
|
||||
idCardDataAt: Timestamp | null
|
||||
idCardDataExpiration: Timestamp | null
|
||||
idCardDataNumber: string | null
|
||||
|
|
@ -495,7 +495,7 @@ export interface EditedCustomerData {
|
|||
frontCameraAt: Timestamp | null
|
||||
frontCameraBy: string | null
|
||||
frontCameraPath: string | null
|
||||
idCardData: Json | null
|
||||
idCardData: { firstName: string; lastName: string }
|
||||
idCardDataAt: Timestamp | null
|
||||
idCardDataBy: string | null
|
||||
idCardPhotoAt: Timestamp | null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue