@accounter/server
Version:
The test suite is split into three Vitest projects for efficiency and isolation:
319 lines • 12.4 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import DataLoader from 'dataloader';
import { Injectable, Scope } from 'graphql-modules';
import { sql } from '@pgtyped/runtime';
import { getCacheInstance } from '../../../shared/helpers/index.js';
import { DBProvider } from '../../app-providers/db.provider.js';
const getChargesByIds = sql `
SELECT *
FROM accounter_schema.charges
WHERE id IN $$chargeIds;`;
const getChargesByTransactionIds = sql `
SELECT t.id AS transaction_id, c.* FROM accounter_schema.transactions t
LEFT JOIN accounter_schema.charges c
ON t.charge_id = c.id
WHERE t.id IN $$transactionIds;`;
const getChargesByMissingRequiredInfo = sql `
SELECT c.*
FROM accounter_schema.charges c
LEFT JOIN accounter_schema.charge_tags t
ON t.charge_id = c.id
WHERE c.user_description IS NULL
OR t.tag_id IS NULL;`;
const updateCharge = sql `
UPDATE accounter_schema.charges
SET
owner_id = COALESCE(
$ownerId,
owner_id
),
user_description = COALESCE(
$userDescription,
user_description
),
type = COALESCE(
$type,
type
),
invoice_payment_currency_diff = COALESCE(
$isInvoicePaymentDifferentCurrency,
invoice_payment_currency_diff
),
accountant_status = COALESCE(
$accountantStatus,
accountant_status
),
tax_category_id = COALESCE(
$taxCategoryId,
tax_category_id
),
optional_vat = COALESCE(
$optionalVAT,
optional_vat
),
documents_optional_flag = COALESCE(
$optionalDocuments,
documents_optional_flag
),
is_property = COALESCE(
$isProperty,
is_property
)
WHERE
id = $chargeId
RETURNING *;
`;
const batchUpdateCharges = sql `
UPDATE accounter_schema.charges
SET
owner_id = COALESCE(
$ownerId,
owner_id
),
user_description = COALESCE(
$userDescription,
user_description
),
type = COALESCE(
$type,
type
),
invoice_payment_currency_diff = COALESCE(
$isInvoicePaymentDifferentCurrency,
invoice_payment_currency_diff
),
accountant_status = COALESCE(
$accountantStatus,
accountant_status
),
tax_category_id = COALESCE(
$taxCategoryId,
tax_category_id
),
optional_vat = COALESCE(
$optionalVAT,
optional_vat
),
documents_optional_flag = COALESCE(
$optionalDocuments,
documents_optional_flag
),
is_property = COALESCE(
$isProperty,
is_property
)
WHERE
id in $$chargeIds
RETURNING *;
`;
const updateAccountantApproval = sql `
UPDATE accounter_schema.charges
SET
accountant_status = $accountantStatus
WHERE
id = $chargeId
RETURNING *;
`;
const generateCharge = sql `
INSERT INTO accounter_schema.charges (owner_id, type, accountant_status, user_description, tax_category_id, optional_vat, documents_optional_flag, is_property)
VALUES ($ownerId, $type, $accountantStatus, $userDescription, $taxCategoryId, $optionalVAT, $optionalDocuments, $isProperty)
RETURNING *;
`;
const getChargesByFilters = sql `
SELECT
ec.*,
ABS(ec.event_amount) as abs_event_amount
FROM accounter_schema.charges c
LEFT JOIN accounter_schema.extended_charges ec
ON c.id = ec.id
WHERE
($isIDs = 0 OR c.id IN $$IDs)
AND ($isOwnerIds = 0 OR c.owner_id IN $$ownerIds)
AND ($isBusinessIds = 0 OR ec.business_array && $businessIds)
AND ($fromDate ::TEXT IS NULL OR COALESCE(ec.documents_min_date, ec.transactions_min_event_date)::TEXT::DATE >= date_trunc('day', $fromDate ::DATE))
AND ($fromAnyDate ::TEXT IS NULL OR GREATEST(ec.documents_max_date, ec.transactions_max_event_date, ec.transactions_max_debit_date, ec.ledger_max_invoice_date, ec.ledger_max_value_date)::TEXT::DATE >= date_trunc('day', $fromAnyDate ::DATE))
AND ($toDate ::TEXT IS NULL OR COALESCE(ec.documents_max_date, ec.transactions_max_event_date)::TEXT::DATE <= date_trunc('day', $toDate ::DATE))
AND ($toAnyDate ::TEXT IS NULL OR LEAST(ec.documents_min_date, ec.transactions_min_event_date, ec.transactions_min_debit_date, ec.ledger_min_invoice_date, ec.ledger_min_value_date)::TEXT::DATE <= date_trunc('day', $toAnyDate ::DATE))
AND ($chargeType = 'ALL' OR ($chargeType = 'INCOME' AND ec.transactions_event_amount > 0) OR ($chargeType = 'EXPENSE' AND ec.transactions_event_amount <= 0))
AND ($withoutInvoice = FALSE OR COALESCE(ec.invoices_count, 0) = 0)
AND ($withoutReceipt = FALSE OR (COALESCE(ec.receipts_count, 0) = 0 AND (ec.no_invoices_required IS FALSE)))
AND ($withoutDocuments = FALSE OR COALESCE(ec.documents_count, 0) = 0)
AND ($withoutTransactions = FALSE OR COALESCE(ec.transactions_count, 0) = 0)
AND ($withOpenDocuments = FALSE OR ec.open_docs_flag IS TRUE)
AND ($withoutLedger = FALSE OR COALESCE(ec.ledger_count, 0) = 0)
AND ($isAccountantStatuses = 0 OR ec.accountant_status = ANY ($accountantStatuses::accounter_schema.accountant_status[]))
AND ($isTags = 0 OR ec.tags && $tags)
ORDER BY
CASE WHEN $asc = true AND $sortColumn = 'event_date' THEN (COALESCE(ec.transactions_min_debit_date, ec.transactions_min_event_date, ec.documents_min_date, ec.ledger_min_value_date, ec.ledger_min_invoice_date), COALESCE(ec.documents_min_date, ec.transactions_min_event_date), ec.id) END ASC,
CASE WHEN $asc = false AND $sortColumn = 'event_date' THEN (COALESCE(ec.transactions_min_debit_date, ec.transactions_min_event_date, ec.documents_min_date, ec.ledger_min_value_date, ec.ledger_min_invoice_date), COALESCE(ec.documents_min_date, ec.transactions_min_event_date), ec.id) END DESC,
CASE WHEN $asc = true AND $sortColumn = 'event_amount' THEN (ec.event_amount, ec.id) END ASC,
CASE WHEN $asc = false AND $sortColumn = 'event_amount' THEN (ec.event_amount, ec.id) END DESC,
CASE WHEN $asc = true AND $sortColumn = 'abs_event_amount' THEN ABS(cast(ec.event_amount as DECIMAL)) END ASC,
CASE WHEN $asc = false AND $sortColumn = 'abs_event_amount' THEN ABS(cast(ec.event_amount as DECIMAL)) END DESC, ID;
`;
const getSimilarCharges = sql `
SELECT *
FROM accounter_schema.extended_charges
WHERE (CASE WHEN $withMissingTags IS TRUE THEN
tags IS NULL
ELSE
TRUE
END)
AND (CASE WHEN $withMissingDescription IS TRUE THEN
user_description IS NULL
ELSE
TRUE
END)
AND (CASE WHEN $tagsDifferentThan::UUID[] IS NOT NULL THEN
NOT((tags @> $tagsDifferentThan) AND (tags <@ $tagsDifferentThan))
ELSE
TRUE
END)
AND (CASE WHEN $descriptionDifferentThan::TEXT IS NOT NULL THEN
NOT(user_description = $descriptionDifferentThan)
ELSE
TRUE
END)
AND (
(business_id IS NOT NULL AND business_id = $businessId)
OR (business_array IS NOT NULL AND business_array @> $businessArray AND business_array <@ $businessArray)
) AND owner_id = $ownerId
ORDER BY (COALESCE(documents_min_date, transactions_min_debit_date, transactions_min_event_date, ledger_min_value_date, ledger_min_invoice_date), COALESCE(transactions_min_event_date, documents_min_date), id) DESC;`;
const deleteChargesByIds = sql `
DELETE FROM accounter_schema.charges
WHERE id IN $$chargeIds;`;
let ChargesProvider = class ChargesProvider {
dbProvider;
cache = getCacheInstance({
stdTTL: 60 * 60, // 1 hours
});
constructor(dbProvider) {
this.dbProvider = dbProvider;
}
async batchChargesByIds(ids) {
const charges = await getChargesByIds.run({
chargeIds: ids,
}, this.dbProvider);
return ids.map(id => charges.find(charge => charge.id === id));
}
getChargeByIdLoader = new DataLoader((keys) => this.batchChargesByIds(keys), {
cacheKeyFn: id => `charge-${id}`,
cacheMap: this.cache,
});
async batchChargesByTransactionIds(transactionIds) {
const charges = await getChargesByTransactionIds.run({
transactionIds,
}, this.dbProvider);
charges.map(c => this.getChargeByIdLoader.prime(c.id, c));
return transactionIds.map(id => charges.find(charge => charge.transaction_id === id));
}
getChargeByTransactionIdLoader = new DataLoader((transactionIds) => this.batchChargesByTransactionIds(transactionIds), { cache: false });
async getChargesByMissingRequiredInfo() {
return getChargesByMissingRequiredInfo.run(undefined, this.dbProvider).then(charges => charges.map(c => {
this.getChargeByIdLoader.prime(c.id, c);
return c;
}));
}
updateCharge(params) {
return updateCharge.run(params, this.dbProvider).then(([newCharge]) => {
if (newCharge) {
this.invalidateCharge(newCharge.id);
this.getChargeByIdLoader.prime(newCharge.id, newCharge);
}
return newCharge;
});
}
batchUpdateCharges(params) {
return batchUpdateCharges.run(params, this.dbProvider).then(charges => {
charges.map(charge => this.getChargeByIdLoader.prime(charge.id, charge));
return charges;
});
}
updateAccountantApproval(params) {
return updateAccountantApproval.run(params, this.dbProvider).then(([newCharge]) => {
if (newCharge) {
this.getChargeByIdLoader.prime(newCharge.id, newCharge);
}
return newCharge;
});
}
generateCharge(params) {
const fullParams = {
isProperty: false,
userDescription: null,
optionalVAT: false,
optionalDocuments: false,
accountantStatus: 'UNAPPROVED',
...params,
};
return generateCharge.run(fullParams, this.dbProvider).then(([newCharge]) => {
if (newCharge) {
this.getChargeByIdLoader.prime(newCharge.id, newCharge);
}
return newCharge;
});
}
getChargesByFilters(params) {
const isOwnerIds = !!params?.ownerIds?.filter(Boolean).length;
const isBusinessIds = !!params?.businessIds?.filter(Boolean).length;
const isIDs = !!params?.IDs?.length;
const isTags = !!params?.tags?.length;
const isAccountantStatuses = !!params?.accountantStatuses?.length;
const defaults = {
asc: false,
sortColumn: 'event_date',
};
const fullParams = {
...defaults,
isOwnerIds: isOwnerIds ? 1 : 0,
isBusinessIds: isBusinessIds ? 1 : 0,
isIDs: isIDs ? 1 : 0,
isTags: isTags ? 1 : 0,
isAccountantStatuses: isAccountantStatuses ? 1 : 0,
...params,
fromDate: params.fromDate ?? null,
toDate: params.toDate ?? null,
ownerIds: isOwnerIds ? params.ownerIds : [null],
businessIds: isBusinessIds ? params.businessIds : null,
IDs: isIDs ? params.IDs : [null],
tags: isTags ? params.tags : null,
chargeType: params.chargeType ?? 'ALL',
withoutInvoice: params.withoutInvoice ?? false,
withoutReceipt: params.withoutReceipt ?? false,
withoutDocuments: params.withoutDocuments ?? false,
withOpenDocuments: params.withOpenDocuments ?? false,
withoutTransactions: params.withoutTransactions ?? false,
withoutLedger: params.withoutLedger ?? false,
accountantStatuses: isAccountantStatuses ? params.accountantStatuses : null,
};
return getChargesByFilters.run(fullParams, this.dbProvider);
}
async getSimilarCharges(params) {
try {
return getSimilarCharges.run(params, this.dbProvider);
}
catch (error) {
const message = 'Failed to fetch similar charges';
console.error(message, error);
throw new Error(message);
}
}
deleteChargesByIds(params) {
return deleteChargesByIds.run(params, this.dbProvider);
}
async invalidateCharge(chargeId) {
this.getChargeByIdLoader.clear(chargeId);
}
clearCache() {
this.cache.clear();
}
};
ChargesProvider = __decorate([
Injectable({
scope: Scope.Singleton,
global: true,
}),
__metadata("design:paramtypes", [DBProvider])
], ChargesProvider);
export { ChargesProvider };
//# sourceMappingURL=charges.provider.js.map