@@ -13,12 +13,12 @@ Core knowledge for building Bkper apps using `bkper-js` SDK and Workers for Plat
1313
1414### Account Types
1515
16- | Type | Nature | Balance Behavior |
17- | ------| --------| ------------------|
18- | ` ASSET ` | Permanent | Increases with debits |
16+ | Type | Nature | Balance Behavior |
17+ | ----------- | --------- | ---------------------- |
18+ | ` ASSET ` | Permanent | Increases with debits |
1919| ` LIABILITY ` | Permanent | Increases with credits |
20- | ` INCOMING ` | Temporary | Increases with credits |
21- | ` OUTGOING ` | Temporary | Increases with debits |
20+ | ` INCOMING ` | Temporary | Increases with credits |
21+ | ` OUTGOING ` | Temporary | Increases with debits |
2222
2323### The Zero-Sum Invariant
2424
@@ -35,15 +35,14 @@ bun add bkper-js
3535### Initialization (Cloudflare Workers)
3636
3737``` typescript
38- import { Bkper , Book } from ' bkper-js' ;
38+ import { Bkper , Book } from " bkper-js" ;
3939
4040// Per-request Bkper instance (recommended for Workers)
4141function createBkper(env : Env , oauthToken ? : string ): Bkper {
42- return new Bkper ({
43- apiKeyProvider : async () => env .BKPER_API_KEY ,
44- oauthTokenProvider : async () => oauthToken ,
45- agentIdProvider : async () => env .BKPER_AGENT_ID ,
46- });
42+ return new Bkper ({
43+ oauthTokenProvider : async () => oauthToken ,
44+ agentIdProvider : async () => env .BKPER_AGENT_ID ,
45+ });
4746}
4847```
4948
@@ -58,12 +57,11 @@ const book = await bkper.getBook(bookId, true);
5857
5958// Book properties
6059book .getName ();
61- book .getDatePattern (); // dd/MM/yyyy | MM/dd/yyyy | yyyy/MM/dd
62- book .getFractionDigits (); // decimal places (0-8)
60+ book .getDatePattern (); // dd/MM/yyyy | MM/dd/yyyy | yyyy/MM/dd
61+ book .getFractionDigits (); // decimal places (0-8)
6362book .formatDate (new Date ());
6463book .parseDate (" 25/01/2024" );
6564book .formatValue (1234.56 );
66- book .round (amount );
6765
6866// Get accounts
6967const accounts = await book .getAccounts ();
@@ -73,26 +71,26 @@ const account = await book.getAccount("Account Name");
7371### Transaction Operations
7472
7573``` typescript
76- import { Transaction } from ' bkper-js' ;
74+ import { Transaction } from " bkper-js" ;
7775
7876// Create and post a transaction
7977const tx = new Transaction (book )
80- .setDate (" 2024-01-25" )
81- .setAmount (100.50 )
82- .from (creditAccount )
83- .to (debitAccount )
84- .setDescription (" Payment #invoice123" )
85- .setProperty (" external_id" , " 123" )
86- .addRemoteId (" external-system-id" );
78+ .setDate (" 2024-01-25" )
79+ .setAmount (100.5 )
80+ .from (creditAccount )
81+ .to (debitAccount )
82+ .setDescription (" Payment #invoice123" )
83+ .setProperty (" external_id" , " 123" )
84+ .addRemoteId (" external-system-id" );
8785
88- await tx .create (); // Create as draft
89- await tx .post (); // Post (affects balances)
86+ await tx .create (); // Create as draft
87+ await tx .post (); // Post (affects balances)
9088
9189// Transaction lifecycle
92- await tx .check (); // Mark as reconciled
93- await tx .uncheck (); // Unmark
94- await tx .trash (); // Move to trash
95- await tx .update (); // Update
90+ await tx .check (); // Mark as reconciled
91+ await tx .uncheck (); // Unmark
92+ await tx .trash (); // Move to trash
93+ await tx .update (); // Update
9694
9795// Query transactions
9896const txList = await book .listTransactions (" account:'Bank' after:2024-01-01" );
@@ -117,92 +115,97 @@ book.getProperty("base_currency");
117115
118116``` typescript
119117// Transaction events
120- TRANSACTION_CREATED // Draft created
121- TRANSACTION_POSTED // Posted to accounts
122- TRANSACTION_CHECKED // Marked as reconciled
123- TRANSACTION_UNCHECKED // Unmarked
124- TRANSACTION_UPDATED // Modified
125- TRANSACTION_DELETED // Trashed
126- TRANSACTION_RESTORED // Restored from trash
118+ TRANSACTION_CREATED ; // Draft created
119+ TRANSACTION_POSTED ; // Posted to accounts
120+ TRANSACTION_CHECKED ; // Marked as reconciled
121+ TRANSACTION_UNCHECKED ; // Unmarked
122+ TRANSACTION_UPDATED ; // Modified
123+ TRANSACTION_DELETED ; // Trashed
124+ TRANSACTION_RESTORED ; // Restored from trash
127125
128126// Account events
129- ACCOUNT_CREATED | ACCOUNT_UPDATED | ACCOUNT_DELETED
127+ ACCOUNT_CREATED | ACCOUNT_UPDATED | ACCOUNT_DELETED ;
130128
131129// Other events
132- GROUP_CREATED | GROUP_UPDATED | GROUP_DELETED
133- FILE_CREATED | FILE_UPDATED
134- BOOK_UPDATED | BOOK_DELETED
130+ GROUP_CREATED | GROUP_UPDATED | GROUP_DELETED ;
131+ FILE_CREATED | FILE_UPDATED ;
132+ BOOK_UPDATED | BOOK_DELETED ;
135133```
136134
137135### Event Handler Pattern
138136
139137``` typescript
140- import { Hono } from ' hono' ;
141- import { Bkper , Book } from ' bkper-js' ;
138+ import { Hono } from " hono" ;
139+ import { Bkper , Book } from " bkper-js" ;
142140
143141const app = new Hono <{ Bindings: Env }>();
144142
145- app .post (' /events' , async (c ) => {
146- const event: bkper .Event = await c .req .json ();
147-
148- const bkper = new Bkper ({
149- oauthTokenProvider : async () => c .req .header (' bkper-oauth-token' ),
150- agentIdProvider : async () => c .req .header (' bkper-agent-id' ),
151- });
152-
153- const book = new Book (event .book , bkper .getConfig ());
154-
155- switch (event .type ) {
156- case ' TRANSACTION_CHECKED' :
157- return c .json (await handleTransactionChecked (book , event ));
158- default :
159- return c .json ({ result: false });
160- }
143+ app .post (" /events" , async (c ) => {
144+ const event: bkper .Event = await c .req .json ();
145+
146+ const bkper = new Bkper ({
147+ oauthTokenProvider : async () => c .req .header (" bkper-oauth-token" ),
148+ agentIdProvider : async () => c .req .header (" bkper-agent-id" ),
149+ });
150+
151+ const book = new Book (event .book , bkper .getConfig ());
152+
153+ switch (event .type ) {
154+ case " TRANSACTION_CHECKED" :
155+ return c .json (await handleTransactionChecked (book , event ));
156+ default :
157+ return c .json ({ result: false });
158+ }
161159});
162160```
163161
164162### TRANSACTION_CHECKED Handler Example
165163
166164``` typescript
167- async function handleTransactionChecked(
168- book : Book ,
169- event : bkper .Event
170- ): Promise <Result > {
171- const operation = event .data .object as bkper .TransactionOperation ;
172- const transaction = operation .transaction ;
173-
174- if (! transaction ?.posted ) {
175- return { result: false };
176- }
165+ async function handleTransactionChecked(book : Book , event : bkper .Event ): Promise <Result > {
166+ const operation = event .data .object as bkper .TransactionOperation ;
167+ const transaction = operation .transaction ;
177168
178- // Prevent bot loops
179- if (transaction .agentId === ' my-bot-id' ) {
180- return { result: false };
181- }
182-
183- // Your logic here
184- console .log (` Transaction checked: ${transaction .id } ` );
185-
186- return {
187- result: ` CHECKED: ${transaction .date } ${transaction .amount } `
188- };
169+ if (! transaction ?.posted ) {
170+ return { result: false };
171+ }
172+
173+ // Prevent bot loops
174+ if (transaction .agentId === " my-bot-id" ) {
175+ return { result: false };
176+ }
177+
178+ // Your logic here
179+ console .log (` Transaction checked: ${transaction .id } ` );
180+
181+ return {
182+ result: ` CHECKED: ${transaction .date } ${transaction .amount } ` ,
183+ };
189184}
190185```
191186
192187### Response Format
193188
194189``` typescript
195190type Result = {
196- result? : string | string [] | boolean ; // Success message(s)
197- error? : string ; // Error (red in UI)
198- warning? : string ; // Warning (yellow in UI)
191+ result? : string | string [] | boolean ; // Success message(s)
192+ error? : string ; // Error (red in UI)
193+ warning? : string ; // Warning (yellow in UI)
199194};
200195
201196// Examples
202- { result : false } // No action
203- { result : " CHECKED: 2024-01-15 100.00" } // Success
204- { result : [" Book A: OK" , " Book B: OK" ] } // Multiple
205- { error : " Rate not found" } // Error
197+ {
198+ result : false ;
199+ } // No action
200+ {
201+ result : " CHECKED: 2024-01-15 100.00" ;
202+ } // Success
203+ {
204+ result : [" Book A: OK" , " Book B: OK" ];
205+ } // Multiple
206+ {
207+ error : " Rate not found" ;
208+ } // Error
206209```
207210
208211## App Configuration (bkperapp.yaml)
@@ -229,25 +232,25 @@ webhookUrl: https://${id}.bkper.app/events
229232webhookUrlDev : http://localhost:8788/events
230233apiVersion : v5
231234events :
232- - TRANSACTION_CHECKED
235+ - TRANSACTION_CHECKED
233236
234237# Developer tooling
235238skills :
236- autoUpdate : true
237- installed :
238- - bkper-app-dev
239- - bkper-web-dev
239+ autoUpdate : true
240+ installed :
241+ - bkper-app-dev
242+ - bkper-web-dev
240243` ` `
241244
242245### Menu URL Variables
243246
244- | Variable | Description |
245- |----------| -------------|
246- | ` ${book.id}` | Current book ID |
247- | `${book.properties.xxx}` | Book property value |
248- | `${account.id}` | Current account ID |
249- | `${transactions.ids}` | Selected transaction IDs |
250- | `${transactions.query}` | Current query |
247+ | Variable | Description |
248+ | ------------------------ | ------------------------ |
249+ | ` ${book.id}` | Current book ID |
250+ | `${book.properties.xxx}` | Book property value |
251+ | `${account.id}` | Current account ID |
252+ | `${transactions.ids}` | Selected transaction IDs |
253+ | `${transactions.query}` | Current query |
251254
252255# # Common Patterns
253256
@@ -256,23 +259,23 @@ skills:
256259` ` ` typescript
257260// Create linked transaction
258261const mirrorTx = new Transaction(connectedBook)
259- .setDate(originalTx.date)
260- .setAmount(originalTx.amount)
261- .addRemoteId(originalTx.id) // Link to original
262- .post();
262+ .setDate(originalTx.date)
263+ .setAmount(originalTx.amount)
264+ .addRemoteId(originalTx.id) // Link to original
265+ .post();
263266
264267// Find linked transaction
265268const linked = await book
266- .listTransactions(` remoteId:${originalTx.id}`)
267- .then(list => list.getFirst());
269+ .listTransactions(` remoteId:${originalTx.id}`)
270+ .then(( list) => list.getFirst());
268271```
269272
270273### Bot Loop Prevention
271274
272275``` typescript
273276// Always check agentId to avoid infinite loops
274277if (transaction .agentId === MY_AGENT_ID ) {
275- return { result: false };
278+ return { result: false };
276279}
277280```
278281
@@ -284,8 +287,8 @@ const collection = await book.getCollection();
284287const connectedBooks = collection ?.getBooks () ?? [];
285288
286289for (const connectedBook of connectedBooks ) {
287- if (connectedBook .getId () !== book .getId ()) {
288- // Process connected book
289- }
290+ if (connectedBook .getId () !== book .getId ()) {
291+ // Process connected book
292+ }
290293}
291294```
0 commit comments