@moatless/bookkeeping 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@
6
6
  */
7
7
  export interface TraktamenteRate {
8
8
  countryCode: string;
9
+ country: string;
9
10
  year: number;
10
11
  fullDay: number;
11
12
  halfDay: number;
@@ -4,6 +4,203 @@
4
4
  * Rates are updated annually and published by Skatteverket.
5
5
  * Source: https://skatteverket.se/utlandstraktamente
6
6
  */
7
+ /**
8
+ * Country names by country code
9
+ */
10
+ const COUNTRY_NAMES = {
11
+ SE: "Sweden",
12
+ AL: "Albania",
13
+ DZ: "Algeria",
14
+ AS: "American Samoa",
15
+ AO: "Angola",
16
+ AI: "Anguilla",
17
+ AG: "Antigua and Barbuda",
18
+ AR: "Argentina",
19
+ AM: "Armenia",
20
+ AW: "Aruba",
21
+ AU: "Australia",
22
+ AZ: "Azerbaijan",
23
+ BS: "Bahamas",
24
+ BH: "Bahrain",
25
+ BD: "Bangladesh",
26
+ BB: "Barbados",
27
+ BY: "Belarus",
28
+ BE: "Belgium",
29
+ BZ: "Belize",
30
+ BJ: "Benin",
31
+ BM: "Bermuda",
32
+ BO: "Bolivia",
33
+ BQ: "Bonaire",
34
+ BA: "Bosnia and Herzegovina",
35
+ BW: "Botswana",
36
+ BR: "Brazil",
37
+ BN: "Brunei",
38
+ BG: "Bulgaria",
39
+ BF: "Burkina Faso",
40
+ BI: "Burundi",
41
+ KY: "Cayman Islands",
42
+ CF: "Central African Republic",
43
+ CL: "Chile",
44
+ CO: "Colombia",
45
+ CK: "Cook Islands",
46
+ CR: "Costa Rica",
47
+ CW: "Curacao",
48
+ CY: "Cyprus",
49
+ DK: "Denmark",
50
+ DJ: "Djibouti",
51
+ DO: "Dominican Republic",
52
+ EC: "Ecuador",
53
+ EG: "Egypt",
54
+ GQ: "Equatorial Guinea",
55
+ CI: "Ivory Coast",
56
+ SV: "El Salvador",
57
+ ER: "Eritrea",
58
+ EE: "Estonia",
59
+ SZ: "Eswatini",
60
+ ET: "Ethiopia",
61
+ FJ: "Fiji",
62
+ PH: "Philippines",
63
+ FI: "Finland",
64
+ FR: "France",
65
+ GF: "French Guiana",
66
+ PF: "French Polynesia",
67
+ AE: "United Arab Emirates",
68
+ GA: "Gabon",
69
+ GM: "Gambia",
70
+ GE: "Georgia",
71
+ GH: "Ghana",
72
+ GI: "Gibraltar",
73
+ GR: "Greece",
74
+ GD: "Grenada",
75
+ GP: "Guadeloupe",
76
+ GU: "Guam",
77
+ GT: "Guatemala",
78
+ GN: "Guinea",
79
+ GY: "Guyana",
80
+ HT: "Haiti",
81
+ HN: "Honduras",
82
+ HK: "Hong Kong",
83
+ IN: "India",
84
+ ID: "Indonesia",
85
+ IQ: "Iraq",
86
+ IE: "Ireland",
87
+ IS: "Iceland",
88
+ IL: "Israel",
89
+ IT: "Italy",
90
+ JM: "Jamaica",
91
+ JP: "Japan",
92
+ JO: "Jordan",
93
+ KH: "Cambodia",
94
+ CM: "Cameroon",
95
+ CA: "Canada",
96
+ KZ: "Kazakhstan",
97
+ KE: "Kenya",
98
+ CN: "China",
99
+ KG: "Kyrgyzstan",
100
+ KI: "Kiribati",
101
+ CG: "Congo (Brazzaville)",
102
+ CD: "Congo (DRC)",
103
+ XK: "Kosovo",
104
+ HR: "Croatia",
105
+ CU: "Cuba",
106
+ KW: "Kuwait",
107
+ LA: "Laos",
108
+ LS: "Lesotho",
109
+ LV: "Latvia",
110
+ LR: "Liberia",
111
+ LY: "Libya",
112
+ LI: "Liechtenstein",
113
+ LT: "Lithuania",
114
+ LU: "Luxembourg",
115
+ MO: "Macao",
116
+ MG: "Madagascar",
117
+ MW: "Malawi",
118
+ MY: "Malaysia",
119
+ MV: "Maldives",
120
+ ML: "Mali",
121
+ MT: "Malta",
122
+ MA: "Morocco",
123
+ MQ: "Martinique",
124
+ MR: "Mauritania",
125
+ MU: "Mauritius",
126
+ MX: "Mexico",
127
+ FM: "Micronesia",
128
+ MZ: "Mozambique",
129
+ MD: "Moldova",
130
+ MC: "Monaco",
131
+ MN: "Mongolia",
132
+ ME: "Montenegro",
133
+ MM: "Myanmar",
134
+ NA: "Namibia",
135
+ NL: "Netherlands",
136
+ NP: "Nepal",
137
+ NI: "Nicaragua",
138
+ NE: "Niger",
139
+ MK: "North Macedonia",
140
+ NO: "Norway",
141
+ NC: "New Caledonia",
142
+ NZ: "New Zealand",
143
+ OM: "Oman",
144
+ PK: "Pakistan",
145
+ PA: "Panama",
146
+ PG: "Papua New Guinea",
147
+ PY: "Paraguay",
148
+ PE: "Peru",
149
+ PL: "Poland",
150
+ PT: "Portugal",
151
+ PR: "Puerto Rico",
152
+ QA: "Qatar",
153
+ RE: "Reunion",
154
+ RO: "Romania",
155
+ RW: "Rwanda",
156
+ RU: "Russia",
157
+ LC: "Saint Lucia",
158
+ VC: "Saint Vincent and the Grenadines",
159
+ SB: "Solomon Islands",
160
+ WS: "Samoa",
161
+ SA: "Saudi Arabia",
162
+ CH: "Switzerland",
163
+ SN: "Senegal",
164
+ RS: "Serbia",
165
+ SC: "Seychelles",
166
+ SL: "Sierra Leone",
167
+ SG: "Singapore",
168
+ SX: "Sint Maarten",
169
+ SK: "Slovakia",
170
+ SI: "Slovenia",
171
+ ES: "Spain",
172
+ LK: "Sri Lanka",
173
+ GB: "United Kingdom",
174
+ SR: "Suriname",
175
+ ZA: "South Africa",
176
+ KR: "South Korea",
177
+ TJ: "Tajikistan",
178
+ TW: "Taiwan",
179
+ TZ: "Tanzania",
180
+ TD: "Chad",
181
+ TH: "Thailand",
182
+ CZ: "Czech Republic",
183
+ TG: "Togo",
184
+ TO: "Tonga",
185
+ TT: "Trinidad and Tobago",
186
+ TN: "Tunisia",
187
+ TR: "Turkey",
188
+ TM: "Turkmenistan",
189
+ DE: "Germany",
190
+ UG: "Uganda",
191
+ UA: "Ukraine",
192
+ HU: "Hungary",
193
+ UY: "Uruguay",
194
+ US: "United States",
195
+ UZ: "Uzbekistan",
196
+ VU: "Vanuatu",
197
+ VE: "Venezuela",
198
+ VN: "Vietnam",
199
+ ZM: "Zambia",
200
+ AT: "Austria",
201
+ TL: "East Timor",
202
+ XX: "Other countries",
203
+ };
7
204
  /**
8
205
  * Traktamente rates by country code and year (fullDay amount in SEK)
9
206
  * halfDay = Math.floor(fullDay / 2)
@@ -219,6 +416,7 @@ export function getTraktamenteRate(countryCode, year) {
219
416
  const isDomestic = code === "SE";
220
417
  return {
221
418
  countryCode: code,
419
+ country: COUNTRY_NAMES[code] ?? code,
222
420
  year,
223
421
  fullDay,
224
422
  halfDay,
@@ -237,6 +435,7 @@ export function getAllRatesForYear(year) {
237
435
  const isDomestic = code === "SE";
238
436
  rates.push({
239
437
  countryCode: code,
438
+ country: COUNTRY_NAMES[code] ?? code,
240
439
  year,
241
440
  fullDay,
242
441
  halfDay,
@@ -160,7 +160,7 @@ export async function loginFortnox(options) {
160
160
  const tokenResponse = await exchangeCodeForToken(config, callbackResult.code);
161
161
  // Save token
162
162
  const storedToken = saveFortnoxToken(cwd, tokenResponse);
163
- onStatus("Token saved to .kvitton/tokens/fortnox.json");
163
+ onStatus("Token saved to .ledgit/tokens/fortnox.json");
164
164
  return storedToken;
165
165
  }
166
166
  finally {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export * from "./types";
2
- export * from "./sync-types";
3
2
  export * from "./accounting";
4
3
  export { readEntryYaml, readDocumentsYaml, readDocumentYaml, writeEntryYaml, appendLinesToEntry, readAccountsYaml, validateAccountExists, getAccountName, } from "./yaml";
5
4
  export * from "./transformers";
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // Types (journal entries, documents, fiscal years, etc.)
2
+ // Includes all types from @moatless/bookkeeping-types (sync types, domain types, etc.)
2
3
  export * from "./types";
3
- // Sync types (sync stats, errors, results)
4
- export * from "./sync-types";
5
4
  // Accounting logic
6
5
  export * from "./accounting";
7
6
  // YAML utilities (from yaml module - entry helpers, etc.)
@@ -1,17 +1,5 @@
1
- export interface SyncProgress {
2
- current: number;
3
- total: number;
4
- message?: string;
5
- /** Phase of sync: discovering (listing) or fetching (getting details) */
6
- phase?: "discovering" | "fetching";
7
- }
8
- export interface FiscalYearSummary {
9
- id: number;
10
- year: number;
11
- fromDate: string;
12
- toDate: string;
13
- entryCount: number;
14
- }
1
+ import type { SyncProgress, FiscalYearSummary } from "@moatless/bookkeeping-types";
2
+ export type { SyncProgress, FiscalYearSummary };
15
3
  export declare class SyncProgressBar {
16
4
  private bar;
17
5
  private currentPhase;
@@ -1,61 +1 @@
1
- /**
2
- * Result stats for sync operations
3
- */
4
- export interface SyncStats {
5
- fetched: number;
6
- written: number;
7
- skipped: number;
8
- errors: number;
9
- }
10
- /**
11
- * Stats per fiscal year (more detailed)
12
- */
13
- export interface FiscalYearStats {
14
- year: number;
15
- fetched: number;
16
- created: number;
17
- updated: number;
18
- unchanged: number;
19
- failed: number;
20
- }
21
- /**
22
- * Error entry for tracking issues
23
- */
24
- export interface SyncError {
25
- year: number;
26
- externalId: string;
27
- message: string;
28
- }
29
- /**
30
- * Complete sync result
31
- */
32
- export interface SyncJournalResult {
33
- success: boolean;
34
- fiscalYears: FiscalYearStats[];
35
- totals: {
36
- fetched: number;
37
- created: number;
38
- updated: number;
39
- unchanged: number;
40
- failed: number;
41
- };
42
- errors: SyncError[];
43
- durationMs: number;
44
- }
45
- /**
46
- * Fiscal year info for sync operations
47
- */
48
- export interface FiscalYearInfo {
49
- /** External ID from provider (used for API calls) */
50
- externalId: number;
51
- /** Calendar year (used for file paths) */
52
- year: number;
53
- }
54
- /**
55
- * Fiscal year with dates (from database)
56
- */
57
- export interface FiscalYearWithDates {
58
- name: string | null;
59
- start_date: string;
60
- end_date: string;
61
- }
1
+ export type { SyncStats, FiscalYearStats, SyncError, SyncJournalResult, FiscalYearInfo, FiscalYearWithDates, SyncProgress, } from "@moatless/bookkeeping-types";
@@ -1,10 +1,5 @@
1
+ export * from "@moatless/bookkeeping-types";
1
2
  export type ApiResponse = {
2
3
  message: string;
3
4
  success: true;
4
5
  };
5
- export * from "./journal-entry";
6
- export * from "./document";
7
- export type { Document as InboxDocument } from "./document";
8
- export * from "./fiscal-year";
9
- export * from "./ledger-account";
10
- export * from "./discarded-item";
@@ -1,10 +1,2 @@
1
- // Journal entry types (used across Git YAML, API, and client)
2
- export * from "./journal-entry";
3
- // Document types
4
- export * from "./document";
5
- // Fiscal year
6
- export * from "./fiscal-year";
7
- // Ledger account
8
- export * from "./ledger-account";
9
- // Discarded items
10
- export * from "./discarded-item";
1
+ // Re-export all types from @moatless/bookkeeping-types
2
+ export * from "@moatless/bookkeeping-types";
package/dist/utils/git.js CHANGED
@@ -5,8 +5,9 @@ const DEFAULT_GITIGNORE = `# Environment files with secrets
5
5
  .env
6
6
  .env.*
7
7
 
8
- # CLI cache
9
- .kvitton/
8
+ # Tokens and cache (sensitive data)
9
+ .ledgit/tokens/
10
+ .ledgit/cache/
10
11
 
11
12
  # OS files
12
13
  .DS_Store
@@ -3,4 +3,4 @@ export { parseYaml, toYaml } from "./yaml";
3
3
  export { withRetry, isRateLimitError, type RetryOptions } from "./retry";
4
4
  export { initGitRepo, commitAll } from "./git";
5
5
  export { getAgentsTemplate, renderAgentsTemplate, type RenderAgentsTemplateOptions, } from "./templates";
6
- export { KVITTON_DIR, getKvittonDir, getTokensDir, getCacheDir, } from "./paths";
6
+ export { LEDGIT_DIR, getLedgitDir, getTokensDir, getCacheDir, } from "./paths";
@@ -3,4 +3,4 @@ export { parseYaml, toYaml } from "./yaml";
3
3
  export { withRetry, isRateLimitError } from "./retry";
4
4
  export { initGitRepo, commitAll } from "./git";
5
5
  export { getAgentsTemplate, renderAgentsTemplate, } from "./templates";
6
- export { KVITTON_DIR, getKvittonDir, getTokensDir, getCacheDir, } from "./paths";
6
+ export { LEDGIT_DIR, getLedgitDir, getTokensDir, getCacheDir, } from "./paths";
@@ -2,11 +2,11 @@
2
2
  * The hidden directory used for internal storage (tokens, cache, etc.)
3
3
  * inside the accounting repository.
4
4
  */
5
- export declare const KVITTON_DIR = ".kvitton";
5
+ export declare const LEDGIT_DIR = ".ledgit";
6
6
  /**
7
- * Get the base .kvitton directory path for a given working directory.
7
+ * Get the base .ledgit directory path for a given working directory.
8
8
  */
9
- export declare function getKvittonDir(cwd: string): string;
9
+ export declare function getLedgitDir(cwd: string): string;
10
10
  /**
11
11
  * Get the tokens directory path for storing OAuth tokens.
12
12
  */
@@ -3,22 +3,22 @@ import * as path from "node:path";
3
3
  * The hidden directory used for internal storage (tokens, cache, etc.)
4
4
  * inside the accounting repository.
5
5
  */
6
- export const KVITTON_DIR = ".kvitton";
6
+ export const LEDGIT_DIR = ".ledgit";
7
7
  /**
8
- * Get the base .kvitton directory path for a given working directory.
8
+ * Get the base .ledgit directory path for a given working directory.
9
9
  */
10
- export function getKvittonDir(cwd) {
11
- return path.join(cwd, KVITTON_DIR);
10
+ export function getLedgitDir(cwd) {
11
+ return path.join(cwd, LEDGIT_DIR);
12
12
  }
13
13
  /**
14
14
  * Get the tokens directory path for storing OAuth tokens.
15
15
  */
16
16
  export function getTokensDir(cwd) {
17
- return path.join(cwd, KVITTON_DIR, "tokens");
17
+ return path.join(cwd, LEDGIT_DIR, "tokens");
18
18
  }
19
19
  /**
20
20
  * Get the cache directory path for storing cached data (e.g., currency rates).
21
21
  */
22
22
  export function getCacheDir(cwd) {
23
- return path.join(cwd, KVITTON_DIR, "cache");
23
+ return path.join(cwd, LEDGIT_DIR, "cache");
24
24
  }
@@ -15,14 +15,9 @@ This is an accounting data repository for {{COMPANY_NAME}} (Swedish company), st
15
15
  │ ├── entry.yaml
16
16
  │ └── *.pdf # Supporting documents
17
17
 
18
- ├── inbox/ # Raw incoming documents (no entry yet)
19
- └── [DATE]-[NAME]/
20
- │ ├── documents.yaml
21
- │ └── *.pdf
22
- └── drafts/ # Documents with entries, ready to post
23
- └── [DATE]-[NAME]/ # e.g., "2025-12-24-anthropic"
18
+ └── inbox/ # Raw incoming documents (no entry yet)
19
+ └── [DATE]-[NAME]/
24
20
  ├── documents.yaml
25
- ├── entry.yaml
26
21
  └── *.pdf
27
22
  \`\`\`
28
23
 
@@ -36,10 +31,10 @@ npx ledgit-cli sync-journal # Sync journal entries from accounting prov
36
31
  npx ledgit-cli sync-inbox # Download inbox files from accounting provider
37
32
  npx ledgit-cli company-info # Display company information
38
33
 
39
- # Create journal entries
34
+ # Create journal entries (entry number auto-detected, git branch created if on main)
40
35
  npx ledgit-cli create-entry --path <inbox-dir-or-file> \\
41
36
  --tax-code <code> --base-account <acc> --balancing-account <acc> \\
42
- --series <A-K> --entry-number <num>
37
+ --series <A-K>
43
38
 
44
39
  # For raw files (not from inbox), also provide:
45
40
  # --description "Vendor name" --document-date 2025-01-15 --amount 1234.50
@@ -70,26 +65,36 @@ npx ledgit-cli update # Update AGENTS.md to latest version
70
65
  ## Workflow Overview
71
66
 
72
67
  \`\`\`
73
- inbox/ → (create-entry) → drafts/ → (user confirms) journal-entries/ → (sync-journal) → Provider
68
+ inbox/ → (create-entry) → journal-entries/ → (git review & commit) → (sync-journal) → Provider
74
69
  \`\`\`
75
70
 
76
- ## Bookkeeping New Entries
71
+ When you run \`create-entry\` on main branch, a new git branch is automatically created (e.g., \`book/V-189-2025-10-15-anthropic\`). Review changes with \`git diff\`, commit, and sync to provider.
77
72
 
78
- **Important:** Always ask the user for clarification if anything is unclear about the transaction before creating an entry.
73
+ ## Bookkeeping New Entries
79
74
 
80
75
  To create a journal entry from an inbox document:
81
76
 
82
- 1. **Read the document** - Use \`npx ledgit-cli parse-pdf\` for PDFs to understand the content
83
- 2. **Update documents.yaml** - After parsing, populate missing metadata in \`documents.yaml\`:
77
+ 1. **Read the document** - You can read PDFs directly, or use \`npx ledgit-cli parse-pdf\` to extract text
78
+ 2. **Update documents.yaml** - Populate missing metadata in \`documents.yaml\`:
84
79
  - \`documentDate\`: Invoice/receipt date (YYYY-MM-DD)
85
80
  - \`description\`: Vendor or document name
86
81
  - \`totalAmount\`: With \`amount\` (string) and \`currency\` (e.g., EUR, SEK)
87
- 3. **Look for similar entries** - Search \`journal-entries/\` and \`drafts/\` for entries from the same vendor or similar transaction types to learn which accounts and tax codes were used previously
82
+ 3. **Look for similar entries** - Search \`journal-entries/\` for entries from the same vendor or similar transaction types to learn which accounts and tax codes were used previously
88
83
  4. **Check accounts** - Review \`accounts.yaml\` for appropriate expense/revenue accounts
89
- 5. **Determine tax code** - Based on vendor location and transaction type (see Tax Codes below). Explain your reasoning and describe the tax code meaning to the user
90
- 6. **Ask for confirmation** - Before creating, confirm with the user: vendor, amount, accounts (with names), and tax code (with description)
91
- 7. **Create entry** - Run \`npx ledgit-cli create-entry\` with the appropriate parameters
92
- 8. **Files moved to drafts** - The inbox item is automatically moved to \`drafts/\` with \`entry.yaml\`
84
+ 5. **Determine tax code** - Based on vendor location and transaction type (see Tax Codes below)
85
+ 6. **Create entry** - Run \`npx ledgit-cli create-entry\` with the appropriate parameters
86
+ 7. **Review and sync** - The entry is created in \`journal-entries/\`. Review with \`git diff\`, commit, and run \`npx ledgit-cli sync-journal\` to post to {{PROVIDER}}
87
+
88
+ ### GitHub Actions / CI Mode
89
+
90
+ When running in GitHub Actions (automated bookkeeping):
91
+ - **Always proceed** with creating entries using your best judgment
92
+ - **Always end** by creating or updating a PR
93
+ - If anything is unclear, include your questions in the PR description
94
+ - The user will respond via PR comments, which triggers another workflow run
95
+ - Group related entries (same vendor, same week) in one PR when sensible
96
+
97
+ **Never stop and wait for confirmation in CI mode** - the user can't respond until you create a PR.
93
98
 
94
99
  To discard unwanted inbox items:
95
100
  \`\`\`bash
@@ -97,22 +102,6 @@ npx ledgit-cli discard inbox/2025-01-15-spam-document
97
102
  \`\`\`
98
103
  This removes the directory and marks the item for deletion from the provider on next sync.
99
104
 
100
- ## Posting Entries
101
-
102
- **Important:** Moving entries to \`journal-entries/\` posts them to the real ledger. Always confirm with the user before posting.
103
-
104
- When the user confirms draft entries are ready to post:
105
-
106
- 1. **Determine entry number** - Check existing entries in \`journal-entries/FY-YYYY/\` for the series to find the next available number
107
- 2. **Move to journal-entries** - Rename the draft directory to the proper format:
108
- \`\`\`
109
- drafts/2025-01-15-anthropic/
110
- → journal-entries/FY-2025/A-042-2025-01-15-anthropic/
111
- \`\`\`
112
- Format: \`{SERIES}-{NUM}-{DATE}-{DESC}/\`
113
- 3. **Update entry.yaml** - Ensure \`entryNumber\` matches the directory and \`status\` is set appropriately
114
- 4. **Sync to provider** - Run \`npx ledgit-cli sync-journal\` to post the entries to {{PROVIDER}}
115
-
116
105
  ## Tax Codes
117
106
 
118
107
  ### Common Tax Code Patterns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moatless/bookkeeping",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@moatless/api-client": "^0.1.2",
26
+ "@moatless/bookkeeping-types": "workspace:*",
26
27
  "@moatless/fortnox-client": "^0.1.2",
27
28
  "@moatless/bokio-client": "^0.1.2",
28
29
  "cli-progress": "^3.12.0",