@accounter/server
Version:
Accounter GraphQL server
230 lines (205 loc) • 7.92 kB
text/typescript
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { sql } from '@pgtyped/runtime';
import { reassureOwnerIdExists } from '../../../shared/helpers/index.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { TenantAwareDBClient } from '../../app-providers/tenant-db-client.js';
import type {
BusinessTripProto,
IDeleteChargeBusinessTripQuery,
IGetAllBusinessTripsQuery,
IGetBusinessTripsByChargeIdsQuery,
IGetBusinessTripsByDatesParams,
IGetBusinessTripsByDatesQuery,
IGetBusinessTripsByIdsQuery,
IGetChargeIdsByBusinessTripIdQuery,
IInsertBusinessTripParams,
IInsertBusinessTripQuery,
IUpdateAccountantApprovalParams,
IUpdateAccountantApprovalQuery,
IUpdateBusinessTripParams,
IUpdateBusinessTripQuery,
IUpdateChargeBusinessTripQuery,
} from '../types.js';
const getAllBusinessTrips = sql<IGetAllBusinessTripsQuery>`
SELECT *
FROM accounter_schema.business_trips`;
const getBusinessTripsByIds = sql<IGetBusinessTripsByIdsQuery>`
SELECT *
FROM accounter_schema.business_trips
WHERE id IN $$businessTripsIds;`;
const getBusinessTripsByChargeIds = sql<IGetBusinessTripsByChargeIdsQuery>`
SELECT btc.charge_id, bt.*
FROM accounter_schema.business_trip_charges btc
LEFT JOIN accounter_schema.business_trips bt
ON btc.business_trip_id = bt.id
WHERE btc.charge_id IN $$chargeIds;`;
const getChargeIdsByBusinessTripId = sql<IGetChargeIdsByBusinessTripIdQuery>`
SELECT charge_id, business_trip_id
FROM accounter_schema.business_trip_charges
WHERE business_trip_id IN $$businessTripsIds;`;
const getBusinessTripsByDates = sql<IGetBusinessTripsByDatesQuery>`
WITH business_trips_dates AS (SELECT business_trips_attendees.business_trip_id AS id,
min(business_trips_attendees.arrival) AS from_date,
max(business_trips_attendees.departure) AS to_date
FROM accounter_schema.business_trips_attendees
GROUP BY business_trips_attendees.business_trip_id)
SELECT DISTINCT ON (bt.id) bt.*,
btc.charge_id,
business_trips_dates.from_date,
business_trips_dates.to_date
FROM accounter_schema.business_trips bt
LEFT JOIN accounter_schema.business_trip_charges btc
ON bt.id = btc.business_trip_id
LEFT JOIN business_trips_dates
ON business_trips_dates.id = bt.id
WHERE business_trips_dates.from_date >= $fromDate AND business_trips_dates.to_date < $toDate ;`;
const updateChargeBusinessTrip = sql<IUpdateChargeBusinessTripQuery>`
INSERT INTO accounter_schema.business_trip_charges (charge_id, business_trip_id, owner_id)
VALUES($chargeId, $businessTripId, $ownerId)
ON CONFLICT (charge_id)
DO
UPDATE SET business_trip_id = $businessTripId
RETURNING *;`;
const deleteChargeBusinessTrip = sql<IDeleteChargeBusinessTripQuery>`
DELETE FROM accounter_schema.business_trip_charges
WHERE charge_id = $chargeId;`;
const updateBusinessTrip = sql<IUpdateBusinessTripQuery>`
UPDATE accounter_schema.business_trips
SET
name = COALESCE(
$name,
name
),
destination = COALESCE(
$destinationCode,
destination
),
trip_purpose = COALESCE(
$tripPurpose,
trip_purpose
),
accountant_status = COALESCE(
$accountantStatus,
accountant_status
)
WHERE
id = $businessTripId
RETURNING id;
`;
const insertBusinessTrip = sql<IInsertBusinessTripQuery>`
INSERT INTO accounter_schema.business_trips (name, destination, trip_purpose, owner_id)
VALUES($name, $destinationCode, $tripPurpose, $ownerId)
RETURNING id;`;
const updateAccountantApproval = sql<IUpdateAccountantApprovalQuery>`
UPDATE accounter_schema.business_trips
SET
accountant_status = $accountantStatus
WHERE
id = $businessTripId
RETURNING *;
`;
({
scope: Scope.Operation,
global: true,
})
export class BusinessTripsProvider {
constructor(
private db: TenantAwareDBClient,
private adminContextProvider: AdminContextProvider,
) {}
public getAllBusinessTrips() {
return getAllBusinessTrips.run(undefined, this.db) as Promise<BusinessTripProto[]>;
}
private async batchBusinessTripsByIds(businessTripsIds: readonly string[]) {
const businessTrips = await getBusinessTripsByIds.run(
{
businessTripsIds,
},
this.db,
);
return businessTripsIds.map(id => businessTrips.find(record => record.id === id));
}
public getBusinessTripsByIdLoader = new DataLoader(
(ids: readonly string[]) =>
this.batchBusinessTripsByIds(ids) as Promise<(BusinessTripProto | undefined)[]>,
);
private async batchBusinessTripByChargeIds(chargeIds: readonly string[]) {
const businessTrips = await getBusinessTripsByChargeIds.run(
{
chargeIds,
},
this.db,
);
return chargeIds.map(id => businessTrips.find(record => record.charge_id === id));
}
public getBusinessTripsByChargeIdLoader = new DataLoader((ids: readonly string[]) =>
this.batchBusinessTripByChargeIds(ids),
);
private async batchChargeIdsByBusinessTripIds(businessTripsIds: readonly string[]) {
const businessTripsChargeIds = await getChargeIdsByBusinessTripId.run(
{
businessTripsIds,
},
this.db,
);
return businessTripsIds.map(tripId =>
businessTripsChargeIds
.filter(({ business_trip_id }) => business_trip_id === tripId)
.map(match => match.charge_id),
);
}
public getChargeIdsByBusinessTripIdLoader = new DataLoader((ids: readonly string[]) =>
this.batchChargeIdsByBusinessTripIds(ids),
);
public getBusinessTripsByDates(params: IGetBusinessTripsByDatesParams) {
return getBusinessTripsByDates.run(params, this.db);
}
public async updateChargeBusinessTrip(chargeId: string, businessTripId: string | null) {
const trip = await this.getBusinessTripsByChargeIdLoader.load(chargeId);
if (trip) {
this.getBusinessTripsByIdLoader.clear(trip.id);
this.getChargeIdsByBusinessTripIdLoader.clear(trip.id);
}
this.getBusinessTripsByChargeIdLoader.clear(chargeId);
if (businessTripId) {
this.getBusinessTripsByIdLoader.clear(businessTripId);
this.getChargeIdsByBusinessTripIdLoader.clear(businessTripId);
const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
return updateChargeBusinessTrip.run(
reassureOwnerIdExists({ chargeId, businessTripId, ownerId: null }, ownerId),
this.db,
);
}
return deleteChargeBusinessTrip.run({ chargeId }, this.db);
}
public async updateBusinessTrip(params: IUpdateBusinessTripParams) {
if (params.businessTripId) {
const chargeIds = await this.getChargeIdsByBusinessTripIdLoader.load(params.businessTripId);
if (chargeIds) {
chargeIds.map(chargeId => this.getBusinessTripsByChargeIdLoader.clear(chargeId));
}
this.getBusinessTripsByIdLoader.clear(params.businessTripId);
}
return updateBusinessTrip.run(params, this.db);
}
public async insertBusinessTrip(params: IInsertBusinessTripParams) {
const { ownerId } = await this.adminContextProvider.getVerifiedAdminContext();
return insertBusinessTrip.run(reassureOwnerIdExists(params, ownerId), this.db);
}
public async updateAccountantApproval(params: IUpdateAccountantApprovalParams) {
if (params.businessTripId) {
const chargeIds = await this.getChargeIdsByBusinessTripIdLoader.load(params.businessTripId);
if (chargeIds) {
chargeIds.map(chargeId => this.getBusinessTripsByChargeIdLoader.clear(chargeId));
}
this.getBusinessTripsByIdLoader.clear(params.businessTripId);
}
return updateAccountantApproval.run(params, this.db);
}
public clearCache() {
this.getBusinessTripsByIdLoader.clearAll();
this.getBusinessTripsByChargeIdLoader.clearAll();
this.getChargeIdsByBusinessTripIdLoader.clearAll();
}
}