WIP knex > kysely
This commit is contained in:
parent
9d9ee1b4ce
commit
72eeb03425
@ -11,7 +11,7 @@ get {
|
|||||||
}
|
}
|
||||||
|
|
||||||
params:path {
|
params:path {
|
||||||
id: 1000
|
id: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@ -12,7 +12,6 @@ get {
|
|||||||
|
|
||||||
params:query {
|
params:query {
|
||||||
supplier: 150
|
supplier: 150
|
||||||
:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@ -5,11 +5,17 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{base_url}}/api/invoices
|
url: {{base_url}}/api/invoices?limit=2&supplierId=10
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
limit: 2
|
||||||
|
supplierId: 10
|
||||||
|
~year: 2015
|
||||||
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
timeout: 0
|
timeout: 0
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
meta {
|
meta {
|
||||||
name: /api/objects/:id
|
name: /api/journals
|
||||||
type: http
|
type: http
|
||||||
seq: 12
|
seq: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{base_url}}/api/objects/10
|
url: {{base_url}}/api/journals
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
timeout: 0
|
|
||||||
}
|
}
|
||||||
20
.bruno/API/api-objects--id-transactions.bru
Normal file
20
.bruno/API/api-objects--id-transactions.bru
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
meta {
|
||||||
|
name: /api/objects/:id/transactions
|
||||||
|
type: http
|
||||||
|
seq: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{base_url}}/api/objects/:id/transactions
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
params:path {
|
||||||
|
id: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
@ -5,11 +5,15 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{base_url}}/api/results/2018
|
url: {{base_url}}/api/results/:year
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params:path {
|
||||||
|
year: 2017
|
||||||
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
timeout: 0
|
timeout: 0
|
||||||
|
|||||||
@ -5,14 +5,14 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{base_url}}/api/transactions?year=2020&accountNumber=4800
|
url: {{base_url}}/api/transactions?year=2020
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
params:query {
|
params:query {
|
||||||
year: 2020
|
year: 2020
|
||||||
accountNumber: 4800
|
~accountNumber: 4800
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@ -1,9 +1,25 @@
|
|||||||
import { type SelectQueryBuilder, type ReferenceExpression, type OperandValueExpression, sql } from 'kysely'
|
import { type SelectQueryBuilder, type ReferenceExpression, type OperandValueExpression, sql } from 'kysely'
|
||||||
|
|
||||||
|
type ColumnsOf<DB, T extends keyof DB> = T extends any ? keyof DB[T] & string : never
|
||||||
|
|
||||||
|
type AppearsIn<DB, TB extends keyof DB, Col extends string> = {
|
||||||
|
[T in TB]: Col extends ColumnsOf<DB, T> ? T : never
|
||||||
|
}[TB]
|
||||||
|
|
||||||
|
type IsUnion<T, U = T> = T extends any ? ([U] extends [T] ? false : true) : never
|
||||||
|
|
||||||
|
type UniqueColumn<DB, TB extends keyof DB> = {
|
||||||
|
[C in ColumnsOf<DB, TB>]: IsUnion<AppearsIn<DB, TB, C>> extends true ? never : C
|
||||||
|
}[ColumnsOf<DB, TB>]
|
||||||
|
|
||||||
|
type PrefixedColumn<DB, TB extends keyof DB> = TB extends any ? `${TB & string}.${keyof DB[TB] & string}` : never
|
||||||
|
|
||||||
|
type UnambiguousColumn<DB, TB extends keyof DB> = PrefixedColumn<DB, TB> | UniqueColumn<DB, TB>
|
||||||
|
|
||||||
type WhereInput<DB, TB extends keyof DB> = Partial<{
|
type WhereInput<DB, TB extends keyof DB> = Partial<{
|
||||||
[C in keyof DB[TB]]:
|
[C in UnambiguousColumn<DB, TB>]:
|
||||||
| OperandValueExpression<DB, TB, DB[TB][C]>
|
| OperandValueExpression<DB, TB, any>
|
||||||
| readonly OperandValueExpression<DB, TB, DB[TB][C]>[]
|
| readonly OperandValueExpression<DB, TB, any>[]
|
||||||
| null
|
| null
|
||||||
}>
|
}>
|
||||||
|
|
||||||
@ -16,10 +32,16 @@ export function applyWhere<DB, TB extends keyof DB, O>(
|
|||||||
|
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
return builder.where(column, 'is', null)
|
return builder.where(column, 'is', null)
|
||||||
} else if (Array.isArray(value)) {
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
return builder.where(column, 'in', value)
|
return builder.where(column, 'in', value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value === undefined) {
|
||||||
|
return builder
|
||||||
|
}
|
||||||
|
|
||||||
return builder.where(column, '=', value)
|
return builder.where(column, '=', value)
|
||||||
}, builder)
|
}, builder)
|
||||||
}
|
}
|
||||||
@ -28,20 +50,19 @@ interface PaginationInput<DB, TB extends keyof DB> {
|
|||||||
where: WhereInput<DB, TB>
|
where: WhereInput<DB, TB>
|
||||||
limit?: number
|
limit?: number
|
||||||
offset?: number
|
offset?: number
|
||||||
orderBy?: {
|
sort?: UnambiguousColumn<DB, TB> | `-${UnambiguousColumn<DB, TB>}`
|
||||||
column: ReferenceExpression<DB, TB>
|
|
||||||
direction?: 'asc' | 'desc'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyPagination<DB, TB extends keyof DB, O>(
|
export function applyPagination<DB, TB extends keyof DB, O>(
|
||||||
builder: SelectQueryBuilder<DB, TB, O>,
|
builder: SelectQueryBuilder<DB, TB, O>,
|
||||||
{ limit, offset, orderBy }: PaginationInput<DB, TB>,
|
{ limit, offset, sort }: PaginationInput<DB, TB>,
|
||||||
): SelectQueryBuilder<DB, TB, O> {
|
): SelectQueryBuilder<DB, TB, O> {
|
||||||
let qb = builder
|
let qb = builder
|
||||||
|
|
||||||
if (orderBy) {
|
if (sort) {
|
||||||
qb = qb.orderBy(orderBy.column, orderBy.direction ?? 'asc')
|
const columnName = (sort.startsWith('-') ? sort.slice(1) : sort) as UnambiguousColumn<DB, TB>
|
||||||
|
|
||||||
|
qb = qb.orderBy(columnName, columnName === sort ? 'asc' : 'desc')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (limit !== undefined) {
|
if (limit !== undefined) {
|
||||||
@ -68,13 +89,15 @@ export async function paginate<DB, TB extends keyof DB, O>(
|
|||||||
countQuery = applyWhere(countQuery, input.where)
|
countQuery = applyWhere(countQuery, input.where)
|
||||||
}
|
}
|
||||||
|
|
||||||
const [countRow, items] = await Promise.all([countQuery.executeTakeFirstOrThrow(), dataQuery.execute()])
|
const [countRow, data] = await Promise.all([countQuery.executeTakeFirstOrThrow(), dataQuery.execute()])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items,
|
data,
|
||||||
totalCount: (countRow as { totalCount: number }).totalCount,
|
meta: {
|
||||||
limit: input.limit,
|
totalCount: (countRow as { totalCount: number }).totalCount,
|
||||||
offset: input.offset,
|
limit: input.limit,
|
||||||
count: items.length,
|
offset: input.offset,
|
||||||
|
count: data.length,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,30 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
import { sql } from 'kysely'
|
||||||
|
|
||||||
const balanceRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
const balanceRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
async handler() {
|
async handler() {
|
||||||
const financialYears = await knex('financialYear').select('*').orderBy('year', 'asc')
|
const financialYears = await db.selectFrom('financialYear').select('year').orderBy('year').execute()
|
||||||
|
|
||||||
return knex('accountBalance AS ab')
|
return db
|
||||||
|
.selectFrom('accountBalance as ab')
|
||||||
|
.innerJoin('financialYear as fy', 'fy.id', 'ab.financialYearId')
|
||||||
|
.innerJoin('account as a', 'a.number', 'ab.accountNumber')
|
||||||
|
.select(['ab.accountNumber', 'a.description'])
|
||||||
.select(
|
.select(
|
||||||
'ab.accountNumber',
|
financialYears.map((fy) =>
|
||||||
'a.description',
|
sql`SUM(CASE WHEN fy.year = ${fy.year} THEN ab."out" ELSE 0 END)`.as(fy.year.toString()),
|
||||||
Object.fromEntries(
|
|
||||||
financialYears.map((fy) => [
|
|
||||||
fy.year,
|
|
||||||
knex.raw(`SUM(CASE WHEN fy.year = ${fy.year} THEN ab."out" ELSE 0 END)`),
|
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.innerJoin('financialYear AS fy', 'fy.id', 'ab.financialYearId')
|
.groupBy(['ab.accountNumber', 'a.description'])
|
||||||
.innerJoin('account AS a', function () {
|
|
||||||
this.on('ab.accountNumber', '=', 'a.number')
|
|
||||||
})
|
|
||||||
.groupBy('ab.accountNumber', 'a.description')
|
|
||||||
.where('ab.accountNumber', '<', 3000)
|
|
||||||
.orderBy('ab.accountNumber')
|
.orderBy('ab.accountNumber')
|
||||||
|
.where('ab.accountNumber', '<', 3000)
|
||||||
|
.execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,34 +1,38 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
import { jsonArrayFrom } from 'kysely/helpers/postgres'
|
||||||
|
import { applyWhere } from '../../lib/kysely_helpers.ts'
|
||||||
|
|
||||||
const entryRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
const entryRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
journal: z.string(),
|
journal: z.string().optional(),
|
||||||
year: z.coerce.number(),
|
year: z.coerce.number().optional(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
async handler(request) {
|
||||||
const financialYearId = (await knex('financialYear').first('id').where('year', req.query.year))?.id
|
return applyWhere(
|
||||||
const journalId = (await knex('journal').first('id').where('identifier', req.query.journal))?.id
|
db
|
||||||
|
.selectFrom('entry as e')
|
||||||
if (!financialYearId || !journalId) {
|
.innerJoin('transaction as t', 'e.id', 't.entryId')
|
||||||
return null
|
.innerJoin('journal as j', 'j.id', 'e.journalId')
|
||||||
}
|
.innerJoin('financialYear as fy', 'fy.id', 'e.financialYearId')
|
||||||
|
.selectAll('e')
|
||||||
return knex('entry AS e')
|
.select((eb) => eb.fn.sum('t.amount').as('amount'))
|
||||||
.select('e.*')
|
.orderBy('e.id')
|
||||||
.sum('t.amount AS amount')
|
.where('t.amount', '>', 0 as ANY)
|
||||||
.innerJoin('transaction AS t', 'e.id', 't.entryId')
|
.groupBy('e.id'),
|
||||||
.orderBy('e.id')
|
{
|
||||||
.where({ financialYearId, journalId })
|
year: request.query.year,
|
||||||
.andWhere('t.amount', '>', 0)
|
'j.identifier': request.query.journal,
|
||||||
.groupBy('e.id')
|
},
|
||||||
|
).execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -37,27 +41,35 @@ const entryRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.number(),
|
id: z.coerce.number(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
async handler(req) {
|
||||||
return knex('entry AS e')
|
return db
|
||||||
.first('e.id', 'j.identifier AS journal', 'e.number', 'e.entryDate', 'e.transactionDate', 'e.description', {
|
.selectFrom('entry as e')
|
||||||
transactions: knex
|
.innerJoin('journal as j', 'e.journalId', 'j.id')
|
||||||
.select(knex.raw('json_agg(transactions)'))
|
.innerJoin('transaction as t', 'e.id', 't.entryId')
|
||||||
.from(
|
.select([
|
||||||
knex('transaction')
|
'e.id',
|
||||||
.select('accountNumber', 'objectId')
|
'j.identifier as journal',
|
||||||
.where('transaction.entryId', knex.ref('e.id'))
|
'e.number',
|
||||||
.as('transactions'),
|
'e.entryDate',
|
||||||
),
|
'e.transactionDate',
|
||||||
})
|
'e.description',
|
||||||
.sum('t.amount AS amount')
|
(eb) => eb.fn.sum('t.amount').as('amount'),
|
||||||
.innerJoin('journal AS j', 'e.journalId', 'j.id')
|
(eb) =>
|
||||||
.innerJoin('transaction AS t', 'e.id', 't.entryId')
|
jsonArrayFrom(
|
||||||
.where('e.id', req.params.id)
|
eb
|
||||||
.andWhere('t.amount', '>', 0)
|
.selectFrom('transaction as t')
|
||||||
.groupBy('e.id', 'j.identifier')
|
.select(['id', 'accountNumber', 'amount', 'description'])
|
||||||
|
.selectAll()
|
||||||
|
.whereRef('t.entryId', '=', 'e.id'),
|
||||||
|
).as('transactions'),
|
||||||
|
])
|
||||||
|
.groupBy(['e.id', 'j.identifier'])
|
||||||
|
.where('e.id', '=', req.params.id)
|
||||||
|
.where('t.amount', '>', 0 as ANY)
|
||||||
|
.execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
|
||||||
|
|
||||||
const financialYearRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
const financialYearRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
handler() {
|
handler() {
|
||||||
return knex('financialYear').select('*').orderBy('startDate', 'desc')
|
return db.selectFrom('financialYear').selectAll().orderBy('startDate', 'desc').execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,52 +1,52 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
import { jsonArrayFrom } from 'kysely/helpers/postgres'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
import { applyWhere, paginate } from '../../lib/kysely_helpers.ts'
|
||||||
import StatusError from '../../lib/status_error.ts'
|
|
||||||
|
|
||||||
const invoiceRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
const invoiceRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
year: z.number().optional(),
|
year: z.coerce.number().optional(),
|
||||||
supplier: z.number().optional(),
|
supplierId: z.coerce.number().optional(),
|
||||||
|
limit: z.coerce.number().default(100),
|
||||||
|
offset: z.coerce.number().optional(),
|
||||||
|
sort: z.literal(['supplierId', 'i.id', 'invoiceDate', 'dueDate']).default('i.id'),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
async handler(request) {
|
||||||
let query: { financialYearId?: number; supplierId?: number } = {}
|
const { offset, limit, sort, ...where } = request.query
|
||||||
|
const baseQuery = db.selectFrom('invoice as i').leftJoin('financialYear as fy', 'fy.id', 'i.financialYearId')
|
||||||
|
|
||||||
if (req.query.year) {
|
return paginate(
|
||||||
const year = await knex('financialYear').first('*').where('year', req.query.year)
|
baseQuery
|
||||||
|
.selectAll('i')
|
||||||
if (!year) throw new StatusError(404, `Year ${req.query.year} not found.`)
|
.select(['fy.year'])
|
||||||
query.financialYearId = year.id
|
.select((eb) => [
|
||||||
}
|
jsonArrayFrom(
|
||||||
|
eb
|
||||||
if (req.query.supplier) {
|
.selectFrom('file as f')
|
||||||
query.supplierId = req.query.supplier
|
.innerJoin('filesToInvoice as fi', 'f.id', 'fi.fileId')
|
||||||
}
|
.select(['id', 'filename'])
|
||||||
|
.whereRef('fi.invoiceId', '=', 'i.id'),
|
||||||
return knex('invoice AS i')
|
).as('files'),
|
||||||
.select('i.*', 'fy.year', {
|
jsonArrayFrom(eb.selectFrom('transaction as t').selectAll().whereRef('t.invoiceId', '=', 'i.id')).as(
|
||||||
files: knex
|
'transactions',
|
||||||
.select(knex.raw('json_agg(files)'))
|
|
||||||
.from(
|
|
||||||
knex
|
|
||||||
.select('id', 'filename')
|
|
||||||
.from('file AS f')
|
|
||||||
.innerJoin('filesToInvoice AS fi', 'f.id', 'fi.fileId')
|
|
||||||
.where('fi.invoiceId', knex.ref('i.id'))
|
|
||||||
.as('files'),
|
|
||||||
),
|
),
|
||||||
transactions: knex
|
]),
|
||||||
.select(knex.raw('json_agg(transactions)'))
|
baseQuery,
|
||||||
.from(knex.select('*').from('transaction AS t').where('t.invoiceId', knex.ref('i.id')).as('transactions')),
|
{
|
||||||
})
|
where,
|
||||||
.leftOuterJoin('financialYear AS fy', 'i.financialYearId', 'fy.id')
|
limit,
|
||||||
.orderBy('i.invoiceDate')
|
offset,
|
||||||
.where(query)
|
sort,
|
||||||
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -55,25 +55,19 @@ const invoiceRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
year: z.number().optional(),
|
supplierId: z.number().optional(),
|
||||||
supplier: z.number().optional(),
|
|
||||||
}),
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
totalAmount: z.coerce.number(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
async handler(req) {
|
handler(request) {
|
||||||
let query: { financialYearId?: number; supplierId?: number } = {}
|
return applyWhere(
|
||||||
|
db.selectFrom('invoice').select((eb) => eb.fn.sum('amount').as('totalAmount')),
|
||||||
if (req.query.year) {
|
request.query,
|
||||||
const year = await knex('financialYear').first('*').where('year', req.query.year)
|
).executeTakeFirst()
|
||||||
|
|
||||||
if (!year) throw new StatusError(404, `Year ${req.query.year} not found.`)
|
|
||||||
query.financialYearId = year.id
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.query.supplier) {
|
|
||||||
query.supplierId = req.query.supplier
|
|
||||||
}
|
|
||||||
|
|
||||||
return knex('invoice AS i').first().sum('i.amount AS amount').where(query)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -82,71 +76,29 @@ const invoiceRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.number(),
|
id: z.coerce.number(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
handler(req) {
|
handler(request) {
|
||||||
return knex('invoice AS i')
|
return db
|
||||||
.first('i.*', 'fy.year', {
|
.selectFrom('invoice as i')
|
||||||
files: knex
|
.leftJoin('financialYear as fy', 'fy.id', 'i.financialYearId')
|
||||||
.select(knex.raw('json_agg(files)'))
|
.selectAll('i')
|
||||||
.from(
|
.select(['fy.year'])
|
||||||
knex
|
.select((eb) => [
|
||||||
.select('id', 'filename')
|
jsonArrayFrom(
|
||||||
.from('file AS f')
|
eb
|
||||||
.innerJoin('filesToInvoice AS fi', 'f.id', 'fi.fileId')
|
.selectFrom('file as f')
|
||||||
.where('fi.invoiceId', knex.ref('i.id'))
|
.innerJoin('filesToInvoice as fi', 'f.id', 'fi.fileId')
|
||||||
.as('files'),
|
.select(['id', 'filename'])
|
||||||
),
|
.whereRef('fi.invoiceId', '=', 'i.id'),
|
||||||
transactions: knex
|
).as('files'),
|
||||||
.select(knex.raw('json_agg(transactions)'))
|
jsonArrayFrom(eb.selectFrom('transaction as t').selectAll().whereRef('t.invoiceId', '=', 'i.id')).as(
|
||||||
.from(knex.select('*').from('transaction AS t').where('t.invoiceId', knex.ref('i.id')).as('transactions')),
|
'transactions',
|
||||||
})
|
),
|
||||||
.leftOuterJoin('financialYear AS fy', 'i.financialYearId', 'fy.id')
|
])
|
||||||
.where('i.id', req.params.id)
|
.where('i.id', '=', request.params.id)
|
||||||
},
|
.execute()
|
||||||
})
|
|
||||||
|
|
||||||
fastify.route({
|
|
||||||
url: '/by-supplier/:supplier',
|
|
||||||
method: 'GET',
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
supplier: z.number(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
handler(req) {
|
|
||||||
return knex('invoice AS i')
|
|
||||||
.select('*', {
|
|
||||||
files: knex
|
|
||||||
.select(knex.raw('json_agg(files)'))
|
|
||||||
.from(
|
|
||||||
knex
|
|
||||||
.select('id', 'filename')
|
|
||||||
.from('file AS f')
|
|
||||||
.innerJoin('filesToInvoice AS fi', 'f.id', 'fi.fileId')
|
|
||||||
.where('fi.invoiceId', knex.ref('i.id'))
|
|
||||||
.as('files'),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
.where('supplierId', req.params.supplier)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
fastify.route({
|
|
||||||
url: '/by-year/:year',
|
|
||||||
method: 'GET',
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
year: z.number(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
async handler(req) {
|
|
||||||
const year = await knex('financialYear').first('*').where('year', req.params.year)
|
|
||||||
|
|
||||||
if (!year) throw new StatusError(404, `Year ${req.params.year} not found.`)
|
|
||||||
|
|
||||||
return knex('invoice').select('*').where('financialYearId', year.id)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
|
||||||
|
|
||||||
const journalRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
const journalRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
handler() {
|
handler() {
|
||||||
return knex('journal').select('*').orderBy('identifier')
|
return db.selectFrom('journal').selectAll().orderBy('identifier').execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,39 +1,38 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
|
||||||
|
|
||||||
const objectRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
const objectRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
handler() {
|
handler() {
|
||||||
return knex('object AS o')
|
return db
|
||||||
.select('o.id', 'o.number', 'o.name', 'd.number AS dimensionNumber', 'd.name AS dimensionName')
|
.selectFrom('object as o')
|
||||||
.innerJoin('dimension AS d', function () {
|
.innerJoin('dimension as d', 'd.id', 'o.dimensionId')
|
||||||
this.on('o.dimensionId', '=', 'd.id')
|
.select(['o.id', 'o.number', 'o.name', 'd.number as dimensionNumber', 'd.name as dimensionName'])
|
||||||
})
|
.execute()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/:id',
|
url: '/:id/transactions',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.number(),
|
id: z.coerce.number(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
async handler(request) {
|
||||||
return knex('transaction AS t')
|
return db
|
||||||
.select('t.entryId', 'e.transactionDate', 't.accountNumber', 't.amount')
|
.selectFrom('transaction as t')
|
||||||
.innerJoin('transactions_to_objects AS to', function () {
|
.innerJoin('transactionsToObjects as to', 't.id', 'to.transactionId')
|
||||||
this.on('t.id', 'to.transactionId')
|
.innerJoin('entry as e', 'e.id', 't.entryId')
|
||||||
})
|
.select(['t.entryId', 'e.transactionDate', 't.accountNumber', 't.amount'])
|
||||||
.innerJoin('entry AS e', function () {
|
.where('to.objectId', '=', request.params.id)
|
||||||
this.on('e.id', '=', 't.entryId')
|
.execute()
|
||||||
})
|
|
||||||
.where('to.objectId', req.params.id)
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,69 +1,33 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod'
|
||||||
import knex from '../../lib/knex.ts'
|
import { sql } from 'kysely'
|
||||||
|
|
||||||
const resultRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
const resultRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
||||||
|
const { db } = fastify
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
url: '/',
|
url: '/',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
async handler() {
|
async handler() {
|
||||||
const financialYears = await knex('financialYear').select('*').orderBy('year', 'asc')
|
const financialYears = await db.selectFrom('financialYear').selectAll().orderBy('year', 'asc').execute()
|
||||||
|
|
||||||
return knex('transaction AS t')
|
return db
|
||||||
|
.selectFrom('transaction as t')
|
||||||
|
.innerJoin('entry as e', 't.entryId', 'e.id')
|
||||||
|
.innerJoin('financialYear as fy', 'fy.id', 'e.financialYearId')
|
||||||
|
.innerJoin('account as a', 't.accountNumber', 'a.number')
|
||||||
|
.select(['t.accountNumber', 'a.description'])
|
||||||
.select(
|
.select(
|
||||||
't.accountNumber',
|
financialYears.map((fy) =>
|
||||||
'a.description',
|
sql`SUM(CASE WHEN fy.year = ${fy.year} THEN t.amount ELSE 0 END)`.as(fy.year.toString()),
|
||||||
Object.fromEntries(
|
|
||||||
financialYears.map((fy) => [
|
|
||||||
fy.year,
|
|
||||||
knex.raw(`SUM(CASE WHEN fy.year = ${fy.year} THEN t.amount ELSE 0 END)`),
|
|
||||||
]),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.sum('t.amount AS amount')
|
.groupBy(['t.accountNumber', 'a.description'])
|
||||||
.innerJoin('entry AS e', function () {
|
|
||||||
this.on('t.entryId', '=', 'e.id')
|
|
||||||
})
|
|
||||||
.innerJoin('financialYear AS fy', 'fy.id', 'e.financialYearId')
|
|
||||||
.innerJoin('account AS a', function () {
|
|
||||||
this.on('t.accountNumber', '=', 'a.number')
|
|
||||||
})
|
|
||||||
.groupBy('t.accountNumber', 'a.description')
|
|
||||||
.where('t.accountNumber', '>=', 3000)
|
.where('t.accountNumber', '>=', 3000)
|
||||||
.orderBy('t.accountNumber')
|
.orderBy('t.accountNumber')
|
||||||
|
.execute()
|
||||||
},
|
},
|
||||||
// async handler() {
|
|
||||||
// const years = await knex('financialYear').select('*')
|
|
||||||
|
|
||||||
// const accounts = await knex('account').select('*')
|
|
||||||
|
|
||||||
// return Promise.all(
|
|
||||||
// years.map((year) =>
|
|
||||||
// knex('account AS a')
|
|
||||||
// .select('a.number', 'a.description')
|
|
||||||
// .sum('t.amount as amount')
|
|
||||||
// .innerJoin('transaction AS t', function () {
|
|
||||||
// this.on('t.accountNumber', '=', 'a.number')
|
|
||||||
// })
|
|
||||||
// .innerJoin('entry AS e', function () {
|
|
||||||
// this.on('t.entryId', '=', 'e.id')
|
|
||||||
// })
|
|
||||||
// .groupBy('a.number', 'a.description')
|
|
||||||
// .where('a.number', '>=', 3000)
|
|
||||||
// .where('e.financialYearId', year.id)
|
|
||||||
// .orderBy('a.number')
|
|
||||||
// .then((result) => ({
|
|
||||||
// startDate: year.startDate,
|
|
||||||
// endDate: year.endDate,
|
|
||||||
// result,
|
|
||||||
// })),
|
|
||||||
// ),
|
|
||||||
// ).then((years) => ({
|
|
||||||
// accounts,
|
|
||||||
// years,
|
|
||||||
// }))
|
|
||||||
// },
|
|
||||||
})
|
})
|
||||||
|
|
||||||
fastify.route({
|
fastify.route({
|
||||||
@ -71,27 +35,24 @@ const resultRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
year: z.number(),
|
year: z.coerce.number(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(req) {
|
async handler(request) {
|
||||||
const year = await knex('financialYear').first('*').where('year', req.params.year)
|
return (
|
||||||
|
db
|
||||||
if (!year) return null
|
.selectFrom('transaction as t')
|
||||||
|
.innerJoin('entry as e', 't.entryId', 'e.id')
|
||||||
return knex('transaction AS t')
|
.innerJoin('financialYear as fy', 'fy.id', 'e.financialYearId')
|
||||||
.select('t.accountNumber', 'a.description')
|
.innerJoin('account as a', 't.accountNumber', 'a.number')
|
||||||
.sum('t.amount as amount')
|
.select(['t.accountNumber', 'a.description', (eb) => eb.fn.sum('t.amount').as('amount')])
|
||||||
.innerJoin('account AS a', function () {
|
// .sum('t.amount AS amount')
|
||||||
this.on('t.accountNumber', '=', 'a.number')
|
.groupBy(['t.accountNumber', 'a.description'])
|
||||||
})
|
.where('t.accountNumber', '>=', 3000)
|
||||||
.innerJoin('entry AS e', function () {
|
.where('year', '=', request.params.year)
|
||||||
this.on('t.entryId', '=', 'e.id')
|
.orderBy('t.accountNumber')
|
||||||
})
|
.execute()
|
||||||
.groupBy('t.accountNumber', 'a.description')
|
)
|
||||||
.where('t.accountNumber', '>=', 3000)
|
|
||||||
.where('e.financialYearId', year.id)
|
|
||||||
.orderBy('t.accountNumber')
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -13,20 +13,20 @@ const transactionRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
|||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
year: z.optional(z.coerce.number()),
|
year: z.optional(z.coerce.number()),
|
||||||
accountNumber: z.optional(z.coerce.number()),
|
accountNumber: z.optional(z.coerce.number()),
|
||||||
limit: z.number().default(2),
|
limit: z.coerce.number().default(100),
|
||||||
sort: z.string().default('t.id'),
|
offset: z.coerce.number().optional(),
|
||||||
offset: z.number().optional(),
|
sort: z.literal(['accountNumber', 'e.transactionDate', 't.id']).default('t.id'),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
async handler(request) {
|
async handler(request) {
|
||||||
const { offset, limit, sort: _sort, ...where } = request.query
|
const { offset, limit, sort, ...where } = request.query
|
||||||
|
|
||||||
const baseQuery = db
|
const baseQuery = db
|
||||||
.selectFrom('transaction as t')
|
.selectFrom('transaction as t')
|
||||||
.innerJoin('entry as e', 't.entryId', 'e.id')
|
.innerJoin('entry as e', 't.entryId', 'e.id')
|
||||||
.innerJoin('financialYear as fy', 'e.financialYearId', 'fy.id')
|
.innerJoin('financialYear as fy', 'e.financialYearId', 'fy.id')
|
||||||
|
|
||||||
const result = await paginate(
|
return paginate(
|
||||||
baseQuery.select([
|
baseQuery.select([
|
||||||
't.accountNumber',
|
't.accountNumber',
|
||||||
'e.transactionDate',
|
'e.transactionDate',
|
||||||
@ -38,14 +38,12 @@ const transactionRoutes: FastifyPluginCallbackZod = (fastify, _, done) => {
|
|||||||
]),
|
]),
|
||||||
baseQuery,
|
baseQuery,
|
||||||
{
|
{
|
||||||
// @ts-ignore
|
|
||||||
where,
|
where,
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
|
sort,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user