feat(sdk): add banking and vendor credits SDK utilities

- Add SDK types for bank rules, cashflow accounts, and vendor credits
- Update banking controllers with proper OpenAPI annotations
- Update vendor credits controllers with new endpoints and DTOs
- Enhance banking transaction handling for categorize, recognize, pending, and exclude operations
- Add vendor credit apply bills and refund functionality

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ahmed Bouhuolia
2026-03-05 23:06:44 +02:00
parent 4c059d610e
commit 631df56cee
14 changed files with 587 additions and 49 deletions

View File

@@ -11329,6 +11329,47 @@
}
},
"/api/vendor-credits/refunds/{refundCreditId}": {
"get": {
"operationId": "VendorCreditsRefundController_getRefundVendorCreditTransaction",
"summary": "Retrieve a refund vendor credit transaction by id.",
"parameters": [
{
"name": "Authorization",
"in": "header",
"description": "Value must be 'Bearer <token>' where <token> is an API key prefixed with 'bc_' or a JWT token.",
"required": true,
"schema": {
"type": "string",
"example": "Bearer bc_1234567890abcdef"
}
},
{
"name": "organization-id",
"in": "header",
"description": "Required if Authorization is a JWT token. The organization ID to operate within.",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "refundCreditId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Vendor Credits Refunds"
]
},
"delete": {
"operationId": "VendorCreditsRefundController_deleteRefundVendorCredit",
"summary": "Delete a refund for the given vendor credit.",
@@ -12492,7 +12533,17 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/GetPendingTransactionResponseDto"
"allOf": [
{ "$ref": "#/components/schemas/PaginatedResponseDto" },
{
"properties": {
"data": {
"type": "array",
"items": { "$ref": "#/components/schemas/GetPendingTransactionResponseDto" }
}
}
}
]
}
}
}
@@ -12832,10 +12883,17 @@
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GetRecognizedTransactionResponseDto"
}
"allOf": [
{ "$ref": "#/components/schemas/PaginatedResponseDto" },
{
"properties": {
"data": {
"type": "array",
"items": { "$ref": "#/components/schemas/GetRecognizedTransactionResponseDto" }
}
}
}
]
}
}
}
@@ -12871,6 +12929,16 @@
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ExcludeBankTransactionsBulkDto"
}
}
}
},
"responses": {
"200": {
"description": ""
@@ -12904,6 +12972,16 @@
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ExcludeBankTransactionsBulkDto"
}
}
}
},
"responses": {
"200": {
"description": ""
@@ -12945,10 +13023,17 @@
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GetExcludedBankTransactionResponseDto"
}
"allOf": [
{ "$ref": "#/components/schemas/PaginatedResponseDto" },
{
"properties": {
"data": {
"type": "array",
"items": { "$ref": "#/components/schemas/GetExcludedBankTransactionResponseDto" }
}
}
}
]
}
}
}
@@ -45120,6 +45205,18 @@
"bankRuleName"
]
},
"ExcludeBankTransactionsBulkDto": {
"type": "object",
"required": ["ids"],
"properties": {
"ids": {
"type": "array",
"items": { "type": "number" },
"description": "IDs of uncategorized bank transactions to exclude or unexclude",
"example": [1, 2, 3]
}
}
},
"GetExcludedBankTransactionResponseDto": {
"type": "object",
"properties": {

View File

@@ -59,6 +59,15 @@ export type AutofillCategorizeTransactionResponse = OpResponseBody<OpForPath<typ
/** Response for GET /api/banking/recognized (single). */
export type RecognizedTransactionResponse = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.RECOGNIZED, 'get'>>;
/** Paginated list response for GET /api/banking/recognized (from OpenAPI schema). */
export type BankTransactionsListPage = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.RECOGNIZED_LIST, 'get'>>;
/** Paginated list response for GET /api/banking/exclude (from OpenAPI schema). */
export type ExcludedBankTransactionsListPage = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.EXCLUDED_LIST, 'get'>>;
/** Paginated list response for GET /api/banking/pending (from OpenAPI schema). */
export type PendingBankTransactionsListPage = OpResponseBody<OpForPath<typeof BANK_RULES_ROUTES.PENDING, 'get'>>;
export async function fetchBankRules(fetcher: ApiFetcher): Promise<BankRulesListResponse> {
const get = fetcher.path(BANK_RULES_ROUTES.RULES).method('get').create();
const { data } = await get({});
@@ -195,7 +204,7 @@ export async function excludeBankTransactionsBulk(
body: ExcludeBankTransactionsBulkBody
): Promise<void> {
const put = fetcher.path(BANK_RULES_ROUTES.EXCLUDE_BULK).method('put').create();
await put(body);
await (put as (params: ExcludeBankTransactionsBulkBody) => Promise<unknown>)(body);
}
export async function unexcludeBankTransactionsBulk(
@@ -203,7 +212,7 @@ export async function unexcludeBankTransactionsBulk(
body: ExcludeBankTransactionsBulkBody
): Promise<void> {
const del = fetcher.path(BANK_RULES_ROUTES.EXCLUDE_BULK).method('delete').create();
await del(body);
await (del as (params: ExcludeBankTransactionsBulkBody) => Promise<unknown>)(body);
}
export async function fetchRecognizedTransaction(
@@ -218,7 +227,7 @@ export async function fetchRecognizedTransaction(
export async function fetchRecognizedTransactions(
fetcher: ApiFetcher,
params?: Record<string, unknown>
): Promise<unknown> {
): Promise<BankTransactionsListPage> {
const get = fetcher.path(BANK_RULES_ROUTES.RECOGNIZED_LIST).method('get').create();
const { data } = await get(params ?? {});
return data;
@@ -227,7 +236,7 @@ export async function fetchRecognizedTransactions(
export async function fetchExcludedBankTransactions(
fetcher: ApiFetcher,
params?: GetExcludedBankTransactionsQuery
): Promise<unknown> {
): Promise<ExcludedBankTransactionsListPage> {
const get = fetcher.path(BANK_RULES_ROUTES.EXCLUDED_LIST).method('get').create();
const { data } = await get(params ?? {});
return data;
@@ -236,7 +245,7 @@ export async function fetchExcludedBankTransactions(
export async function fetchPendingTransactions(
fetcher: ApiFetcher,
params?: GetPendingTransactionsQuery
): Promise<unknown> {
): Promise<PendingBankTransactionsListPage> {
const get = fetcher.path(BANK_RULES_ROUTES.PENDING).method('get').create();
const { data } = await get(params ?? {});
return data;

View File

@@ -1,10 +1,16 @@
import type { ApiFetcher } from './fetch-utils';
import { paths } from './schema';
import { OpForPath, OpResponseBody } from './utils';
import { OpForPath, OpQueryParams, OpRequestBody, OpResponseBody } from './utils';
export const BANKING_ACCOUNTS_ROUTES = {
LIST: '/api/banking/accounts',
SUMMARY: '/api/banking/accounts/{bankAccountId}/summary',
TRANSACTIONS: '/api/banking/transactions',
TRANSACTION_BY_ID: '/api/banking/transactions/{id}',
UNCATEGORIZED_BY_ACCOUNT: '/api/banking/uncategorized/accounts/{accountId}',
UNCATEGORIZED_BY_ID: '/api/banking/uncategorized/{uncategorizedTransactionId}',
CATEGORIZE: '/api/banking/categorize',
UNCATEGORIZE: '/api/banking/categorize/{id}',
} as const satisfies Record<string, keyof paths>;
export type BankingAccountsListResponse = OpResponseBody<OpForPath<typeof BANKING_ACCOUNTS_ROUTES.LIST, 'get'>>;
@@ -30,3 +36,103 @@ export async function fetchBankingAccountSummary(
const { data } = await get({ bankAccountId });
return data as BankingAccountSummaryResponse;
}
/** Query params for GET /api/banking/transactions. */
export type GetBankingTransactionsQuery = OpQueryParams<
OpForPath<typeof BANKING_ACCOUNTS_ROUTES.TRANSACTIONS, 'get'>
>;
/** Server returns { transactions, pagination } (OpenAPI schema says data). */
export type BankingTransactionsListResponse = OpResponseBody<
OpForPath<typeof BANKING_ACCOUNTS_ROUTES.TRANSACTIONS, 'get'>
> & { transactions?: unknown[] };
/** Query params for GET /api/banking/uncategorized/accounts/{accountId}. */
export type GetUncategorizedTransactionsQuery = OpQueryParams<
OpForPath<typeof BANKING_ACCOUNTS_ROUTES.UNCATEGORIZED_BY_ACCOUNT, 'get'>
>;
/** Body for POST /api/banking/transactions. */
export type CreateBankingTransactionBody = OpRequestBody<
OpForPath<typeof BANKING_ACCOUNTS_ROUTES.TRANSACTIONS, 'post'>
>;
/** Body for POST /api/banking/categorize. */
export type CategorizeTransactionBody = OpRequestBody<
OpForPath<typeof BANKING_ACCOUNTS_ROUTES.CATEGORIZE, 'post'>
>;
export async function fetchBankingTransactions(
fetcher: ApiFetcher,
query: GetBankingTransactionsQuery
): Promise<BankingTransactionsListResponse> {
const get = fetcher.path(BANKING_ACCOUNTS_ROUTES.TRANSACTIONS).method('get').create();
const { data } = await get(query);
return data as BankingTransactionsListResponse;
}
export async function getBankingTransaction(
fetcher: ApiFetcher,
id: string | number
): Promise<unknown> {
const get = fetcher.path(BANKING_ACCOUNTS_ROUTES.TRANSACTION_BY_ID).method('get').create();
const { data } = await get({ id: String(id) });
return data;
}
export async function createBankingTransaction(
fetcher: ApiFetcher,
body: CreateBankingTransactionBody
): Promise<void> {
const post = fetcher.path(BANKING_ACCOUNTS_ROUTES.TRANSACTIONS).method('post').create();
await post(body);
}
export async function deleteBankingTransaction(
fetcher: ApiFetcher,
id: string | number
): Promise<void> {
const del = fetcher.path(BANKING_ACCOUNTS_ROUTES.TRANSACTION_BY_ID).method('delete').create();
await del({ id: String(id) });
}
export async function fetchUncategorizedTransactions(
fetcher: ApiFetcher,
accountId: number,
query: GetUncategorizedTransactionsQuery
): Promise<{ data?: unknown[]; pagination: { page: number; pageSize: number; total: number } }> {
const get = fetcher
.path(BANKING_ACCOUNTS_ROUTES.UNCATEGORIZED_BY_ACCOUNT)
.method('get')
.create();
const { data } = await get({ accountId, ...query });
return data as { data?: unknown[]; pagination: { page: number; pageSize: number; total: number } };
}
export async function getUncategorizedTransaction(
fetcher: ApiFetcher,
uncategorizedTransactionId: number
): Promise<unknown> {
const get = fetcher
.path(BANKING_ACCOUNTS_ROUTES.UNCATEGORIZED_BY_ID)
.method('get')
.create();
const { data } = await get({ uncategorizedTransactionId });
return data;
}
export async function categorizeTransaction(
fetcher: ApiFetcher,
body: CategorizeTransactionBody
): Promise<void> {
const post = fetcher.path(BANKING_ACCOUNTS_ROUTES.CATEGORIZE).method('post').create();
await post(body);
}
export async function uncategorizeTransaction(
fetcher: ApiFetcher,
id: number
): Promise<void> {
const del = fetcher.path(BANKING_ACCOUNTS_ROUTES.UNCATEGORIZE).method('delete').create();
await del({ id });
}

View File

@@ -3018,7 +3018,8 @@ export interface paths {
path?: never;
cookie?: never;
};
get?: never;
/** Retrieve a refund vendor credit transaction by id. */
get: operations["VendorCreditsRefundController_getRefundVendorCreditTransaction"];
put?: never;
post?: never;
/** Delete a refund for the given vendor credit. */
@@ -11258,6 +11259,17 @@ export interface components {
*/
bankRuleName: string;
};
ExcludeBankTransactionsBulkDto: {
/**
* @description IDs of uncategorized bank transactions to exclude or unexclude
* @example [
* 1,
* 2,
* 3
* ]
*/
ids: number[];
};
GetExcludedBankTransactionResponseDto: {
/** @description Transaction amount (positive for deposit, negative for withdrawal) */
amount: number;
@@ -18847,6 +18859,30 @@ export interface operations {
};
};
};
VendorCreditsRefundController_getRefundVendorCreditTransaction: {
parameters: {
query?: never;
header: {
/** @description Value must be 'Bearer <token>' where <token> is an API key prefixed with 'bc_' or a JWT token. */
Authorization: string;
/** @description Required if Authorization is a JWT token. The organization ID to operate within. */
"organization-id": string;
};
path: {
refundCreditId: number;
};
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
VendorCreditsRefundController_deleteRefundVendorCredit: {
parameters: {
query?: never;
@@ -19447,7 +19483,9 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["GetPendingTransactionResponseDto"];
"application/json": components["schemas"]["PaginatedResponseDto"] & {
data?: components["schemas"]["GetPendingTransactionResponseDto"][];
};
};
};
};
@@ -19644,7 +19682,9 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["GetRecognizedTransactionResponseDto"][];
"application/json": components["schemas"]["PaginatedResponseDto"] & {
data?: components["schemas"]["GetRecognizedTransactionResponseDto"][];
};
};
};
};
@@ -19661,7 +19701,11 @@ export interface operations {
path?: never;
cookie?: never;
};
requestBody?: never;
requestBody: {
content: {
"application/json": components["schemas"]["ExcludeBankTransactionsBulkDto"];
};
};
responses: {
200: {
headers: {
@@ -19683,7 +19727,11 @@ export interface operations {
path?: never;
cookie?: never;
};
requestBody?: never;
requestBody: {
content: {
"application/json": components["schemas"]["ExcludeBankTransactionsBulkDto"];
};
};
responses: {
200: {
headers: {
@@ -19713,7 +19761,9 @@ export interface operations {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["GetExcludedBankTransactionResponseDto"][];
"application/json": components["schemas"]["PaginatedResponseDto"] & {
data?: components["schemas"]["GetExcludedBankTransactionResponseDto"][];
};
};
};
};

View File

@@ -1,6 +1,11 @@
import type { ApiFetcher } from './fetch-utils';
import { paths } from './schema';
import { OpForPath, OpRequestBody, OpResponseBody } from './utils';
import {
OpForPath,
OpQueryParams,
OpRequestBody,
OpResponseBody,
} from './utils';
export const VENDOR_CREDITS_ROUTES = {
LIST: '/api/vendor-credits',
@@ -8,20 +13,59 @@ export const VENDOR_CREDITS_ROUTES = {
OPEN: '/api/vendor-credits/{id}/open',
VALIDATE_BULK_DELETE: '/api/vendor-credits/validate-bulk-delete',
BULK_DELETE: '/api/vendor-credits/bulk-delete',
REFUND: '/api/vendor-credits/{vendorCreditId}/refund',
REFUND_BY_ID: '/api/vendor-credits/refunds/{refundCreditId}',
BILLS_TO_APPLY: '/api/vendor-credits/{vendorCreditId}/bills-to-apply',
APPLY_TO_BILLS: '/api/vendor-credits/{vendorCreditId}/apply-to-bills',
APPLIED_BILLS: '/api/vendor-credits/{vendorCreditId}/applied-bills',
DELETE_APPLIED_BILL: '/api/vendor-credits/applied-bills/{vendorCreditAppliedBillId}',
} as const satisfies Record<string, keyof paths>;
export type VendorCreditsListResponse = OpResponseBody<OpForPath<typeof VENDOR_CREDITS_ROUTES.LIST, 'get'>>;
export type VendorCredit = OpResponseBody<OpForPath<typeof VENDOR_CREDITS_ROUTES.BY_ID, 'get'>>;
export type CreateVendorCreditBody = OpRequestBody<OpForPath<typeof VENDOR_CREDITS_ROUTES.LIST, 'post'>>;
export type EditVendorCreditBody = OpRequestBody<OpForPath<typeof VENDOR_CREDITS_ROUTES.BY_ID, 'put'>>;
export type VendorCreditsListResponse = OpResponseBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.LIST, 'get'>
>;
export type VendorCredit = OpResponseBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.BY_ID, 'get'>
>;
export type CreateVendorCreditBody = OpRequestBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.LIST, 'post'>
>;
export type EditVendorCreditBody = OpRequestBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.BY_ID, 'put'>
>;
export type GetVendorCreditsQuery = OpQueryParams<
OpForPath<typeof VENDOR_CREDITS_ROUTES.LIST, 'get'>
>;
export type BulkDeleteVendorCreditsBody = OpRequestBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.BULK_DELETE, 'post'>
>;
export type ValidateBulkDeleteVendorCreditsResponse = OpResponseBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.VALIDATE_BULK_DELETE, 'post'>
>;
export type CreateRefundVendorCreditBody = OpRequestBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.REFUND, 'post'>
>;
export type ApplyVendorCreditToBillsBody = OpRequestBody<
OpForPath<typeof VENDOR_CREDITS_ROUTES.APPLY_TO_BILLS, 'post'>
>;
export async function fetchVendorCredits(fetcher: ApiFetcher): Promise<VendorCreditsListResponse> {
export async function fetchVendorCredits(
fetcher: ApiFetcher,
query?: GetVendorCreditsQuery
): Promise<VendorCreditsListResponse> {
const get = fetcher.path(VENDOR_CREDITS_ROUTES.LIST).method('get').create();
const { data } = await get({});
const { data } = await (
get as (params?: GetVendorCreditsQuery) => Promise<{
data: VendorCreditsListResponse;
}>
)(query ?? {});
return data;
}
export async function fetchVendorCredit(fetcher: ApiFetcher, id: number): Promise<VendorCredit> {
export async function fetchVendorCredit(
fetcher: ApiFetcher,
id: number
): Promise<VendorCredit> {
const get = fetcher.path(VENDOR_CREDITS_ROUTES.BY_ID).method('get').create();
const { data } = await get({ id });
return data;
@@ -44,12 +88,131 @@ export async function editVendorCredit(
await put({ id, ...values });
}
export async function deleteVendorCredit(fetcher: ApiFetcher, id: number): Promise<void> {
export async function deleteVendorCredit(
fetcher: ApiFetcher,
id: number
): Promise<void> {
const del = fetcher.path(VENDOR_CREDITS_ROUTES.BY_ID).method('delete').create();
await del({ id });
}
export async function openVendorCredit(fetcher: ApiFetcher, id: number): Promise<void> {
export async function openVendorCredit(
fetcher: ApiFetcher,
id: number
): Promise<void> {
const put = fetcher.path(VENDOR_CREDITS_ROUTES.OPEN).method('put').create();
await put({ id });
}
export async function bulkDeleteVendorCredits(
fetcher: ApiFetcher,
body: BulkDeleteVendorCreditsBody
): Promise<void> {
const post = fetcher
.path(VENDOR_CREDITS_ROUTES.BULK_DELETE)
.method('post')
.create();
await post(body);
}
export async function validateBulkDeleteVendorCredits(
fetcher: ApiFetcher,
body: BulkDeleteVendorCreditsBody
): Promise<ValidateBulkDeleteVendorCreditsResponse> {
const post = fetcher
.path(VENDOR_CREDITS_ROUTES.VALIDATE_BULK_DELETE)
.method('post')
.create();
const { data } = await post(body);
return data;
}
// Refunds (path param vendorCreditId is string in OpenAPI)
export async function fetchVendorCreditRefunds(
fetcher: ApiFetcher,
vendorCreditId: number
): Promise<unknown> {
const get = fetcher.path(VENDOR_CREDITS_ROUTES.REFUND).method('get').create();
const { data } = await get({ vendorCreditId: String(vendorCreditId) });
return data;
}
export async function createRefundVendorCredit(
fetcher: ApiFetcher,
vendorCreditId: number,
values: CreateRefundVendorCreditBody
): Promise<void> {
const post = fetcher.path(VENDOR_CREDITS_ROUTES.REFUND).method('post').create();
await post({ vendorCreditId: String(vendorCreditId), ...values });
}
export async function fetchRefundVendorCreditTransaction(
fetcher: ApiFetcher,
refundCreditId: number
): Promise<unknown> {
const get = fetcher
.path(VENDOR_CREDITS_ROUTES.REFUND_BY_ID)
.method('get')
.create();
const { data } = await get({ refundCreditId });
return data;
}
export async function deleteRefundVendorCredit(
fetcher: ApiFetcher,
refundCreditId: number
): Promise<void> {
const del = fetcher
.path(VENDOR_CREDITS_ROUTES.REFUND_BY_ID)
.method('delete')
.create();
await del({ refundCreditId: String(refundCreditId) });
}
// Apply to bills
export async function fetchVendorCreditToApplyBills(
fetcher: ApiFetcher,
vendorCreditId: number
): Promise<unknown> {
const get = fetcher
.path(VENDOR_CREDITS_ROUTES.BILLS_TO_APPLY)
.method('get')
.create();
const { data } = await get({ vendorCreditId });
return data;
}
export async function applyVendorCreditToBills(
fetcher: ApiFetcher,
vendorCreditId: number,
body: ApplyVendorCreditToBillsBody
): Promise<void> {
const post = fetcher
.path(VENDOR_CREDITS_ROUTES.APPLY_TO_BILLS)
.method('post')
.create();
await post({ vendorCreditId, ...body });
}
export async function fetchAppliedBillsToVendorCredit(
fetcher: ApiFetcher,
vendorCreditId: number
): Promise<unknown> {
const get = fetcher
.path(VENDOR_CREDITS_ROUTES.APPLIED_BILLS)
.method('get')
.create();
const { data } = await get({ vendorCreditId });
return data;
}
export async function deleteAppliedBillToVendorCredit(
fetcher: ApiFetcher,
vendorCreditAppliedBillId: number
): Promise<void> {
const del = fetcher
.path(VENDOR_CREDITS_ROUTES.DELETE_APPLIED_BILL)
.method('delete')
.create();
await del({ vendorCreditAppliedBillId });
}