Fix/transaction inconsitensies (#190)

* fix: request explicit page size from Enable Banking API to fetch all transactions

The API defaults to ~10 transactions per page when no limit is specified,
causing incomplete syncs for users with more transactions.


* fix: enhance DELETE operations and add missing RLS policies for multi-tenant support
This commit is contained in:
Mattsson
2026-04-08 11:25:46 +02:00
committed by GitHub
parent 3dd1f782c3
commit 6486e0d9e2
10 changed files with 381 additions and 45 deletions
+16
View File
@@ -3,6 +3,8 @@ import type { BokioResourceConfig, RateLimitConfig } from '../types';
import {
mapBokioToSalesInvoice,
mapBokioToCustomer,
mapBokioToSupplier,
mapBokioToSupplierInvoice,
mapBokioToJournal,
mapBokioToAccountingAccount,
mapBokioToCompanyInformation,
@@ -26,6 +28,20 @@ export const BOKIO_RESOURCE_CONFIGS: Partial<Record<ResourceType, BokioResourceC
mapper: mapBokioToCustomer,
paginated: true,
},
[ResourceType.Suppliers]: {
listEndpoint: '/suppliers',
detailEndpoint: '/suppliers/{id}',
idField: 'id',
mapper: mapBokioToSupplier,
paginated: true,
},
[ResourceType.SupplierInvoices]: {
listEndpoint: '/supplier-invoices',
detailEndpoint: '/supplier-invoices/{id}',
idField: 'id',
mapper: mapBokioToSupplierInvoice,
paginated: true,
},
[ResourceType.Journals]: {
listEndpoint: '/journal-entries',
detailEndpoint: '/journal-entries/{id}',
+106
View File
@@ -2,6 +2,8 @@ import type {
SalesInvoiceDto, SalesInvoiceLineDto, InvoiceStatusCode,
LegalMonetaryTotalDto, PaymentStatusDto,
CustomerDto,
SupplierDto,
SupplierInvoiceDto, SupplierInvoiceLineDto,
JournalDto, AccountingEntryDto,
AccountingAccountDto, AccountType,
CompanyInformationDto,
@@ -195,6 +197,110 @@ export function mapBokioToAccountingAccount(raw: Record<string, unknown>): Accou
};
}
/**
* Map Bokio Supplier to SupplierDto.
*
* Bokio Supplier fields:
* - id, name, orgNumber, vatNumber, paymentTerms
* - address: { line1, line2, city, postalCode, country }
* - contactsDetails: [{ email, phone, name }]
* - bankAccount, bankgiro, plusgiro
*/
export function mapBokioToSupplier(raw: Record<string, unknown>): SupplierDto {
const name = (raw['name'] as string) ?? '';
const orgNumber = raw['orgNumber'] as string | undefined;
const address = raw['address'] as Record<string, unknown> | undefined;
const contacts = (raw['contactsDetails'] as Record<string, unknown>[] | undefined) ?? [];
const firstContact = contacts[0];
const party = buildParty(name, orgNumber, address);
if (firstContact) {
party.contact = {
email: firstContact['email'] as string | undefined,
telephone: firstContact['phone'] as string | undefined,
name: firstContact['name'] as string | undefined,
};
}
return {
id: String(raw['id'] ?? ''),
supplierNumber: String(raw['id'] ?? ''),
party,
active: true,
vatNumber: raw['vatNumber'] as string | undefined,
bankAccount: raw['bankAccount'] as string | undefined,
bankGiro: raw['bankgiro'] as string | undefined,
plusGiro: raw['plusgiro'] as string | undefined,
defaultPaymentTermsDays: raw['paymentTerms'] != null ? Number(raw['paymentTerms']) : undefined,
_raw: raw,
};
}
/**
* Map Bokio Supplier Invoice to SupplierInvoiceDto.
*
* Bokio Supplier Invoice fields:
* - id, invoiceNumber, status (draft|published|paid|overdue|cancelled)
* - invoiceDate, dueDate, currency, totalAmount, totalTax, paidAmount
* - supplierRef: { id, name }, lineItems: [{ id, description, quantity, unitPrice, taxRate, unitType }]
* - ocrNumber
*/
export function mapBokioToSupplierInvoice(raw: Record<string, unknown>): SupplierInvoiceDto {
const currency = (raw['currency'] as string) ?? 'SEK';
const totalAmount = (raw['totalAmount'] as number) ?? 0;
const totalTax = (raw['totalTax'] as number) ?? 0;
const paidAmount = (raw['paidAmount'] as number) ?? 0;
const balance = totalAmount - paidAmount;
const supplierRef = raw['supplierRef'] as Record<string, unknown> | undefined;
const rawLines = (raw['lineItems'] as Record<string, unknown>[] | undefined) ?? [];
const lines: SupplierInvoiceLineDto[] = rawLines.map((line, idx) => {
const unitPrice = line['unitPrice'] as number | undefined;
const quantity = line['quantity'] as number | undefined;
const lineTotal = unitPrice != null && quantity != null ? unitPrice * quantity : 0;
return {
id: String(line['id'] ?? idx + 1),
description: line['description'] as string | undefined,
quantity,
unitCode: line['unitType'] as string | undefined,
unitPrice: unitPrice != null ? amount(unitPrice, currency) : undefined,
lineExtensionAmount: amount(lineTotal, currency),
taxPercent: line['taxRate'] as number | undefined,
};
});
const legalMonetaryTotal: LegalMonetaryTotalDto = {
lineExtensionAmount: amount(totalAmount - totalTax, currency),
taxInclusiveAmount: amount(totalAmount, currency),
payableAmount: amount(totalAmount, currency),
};
const paymentStatus: PaymentStatusDto = {
paid: paidAmount >= totalAmount && totalAmount > 0,
balance: amount(balance, currency),
};
return {
id: String(raw['id'] ?? ''),
invoiceNumber: String(raw['invoiceNumber'] ?? raw['id'] ?? ''),
issueDate: (raw['invoiceDate'] as string) ?? '',
dueDate: raw['dueDate'] as string | undefined,
currencyCode: currency,
status: deriveInvoiceStatus(raw),
supplier: buildParty(
(supplierRef?.['name'] as string) ?? '',
),
buyer: buildParty(''),
lines,
legalMonetaryTotal,
paymentStatus,
ocrNumber: raw['ocrNumber'] as string | undefined,
_raw: raw,
};
}
/**
* Map Bokio Company to CompanyInformationDto.
*