@kirjapro/mcp-server 0.10.0 → 0.11.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,62 @@
1
1
  # Changelog
2
2
 
3
- ## [0.10.0] - Unreleased
3
+ ## [0.11.0] - Unreleased
4
+
5
+ Requires the database migrations `20261002070106`–`20261002071619` (ALV report
6
+ audit batch 2), in particular `book_purchase_with_vat`, and
7
+ `20261002120000`–`20261002120001` (batch 3, the OmaVero field map); release it
8
+ only after `db:deploy`. Version 0.10.0 keeps working against that database: its
9
+ standard purchases book plain vouchers without a VAT fact (listed by the ALV
10
+ reconciliation), the database applies the new sales-account rule, and its ALV
11
+ summary passes the new `omavero` key through. Against a database without the
12
+ batch 3 map, 0.11.0 derives the same fields from the older keys.
13
+
14
+ - `kirjapro_book_purchase`: for a VAT-registered company a purchase with a rate
15
+ above 0 is booked with `book_purchase_with_vat`, which writes the voucher and
16
+ its VAT fact together, so the VAT reaches field 307. `reverseCharge` works
17
+ again with the new `reverseChargeCategory` (`eu_goods`, `eu_services`,
18
+ `non_eu_services`, `import`, `construction`, `scrap_metal`) and a Finnish
19
+ rate. A company that is not VAT-registered is refused a rate above 0 or a
20
+ reverse charge and books the purchase gross with `vatRate` `0`; the refusal
21
+ says that the VAT on a service from a foreign seller is still owed and goes
22
+ on a non-deductible Ostojen ALV row in the web app and on a voucher D expense
23
+ / K 4210 (Customs collects such a company's import VAT). New optional
24
+ `supplierName`; the result adds `vat_entry_ids`. The database refuses (P0001)
25
+ a deduction on law-based non-deductible accounts, a foreign rate and VAT rows
26
+ that do not match the 2320/4210 lines.
27
+ - `kirjapro_create_from_template`: a purchase template that still debits 2320
28
+ books its domestic VAT fact through `book_purchase_with_vat`; for a company
29
+ that is not VAT-registered it is refused. `kirjapro_create_from_template_batch`
30
+ refuses purchase templates with VAT before any write (the atomic batch cannot
31
+ write VAT facts). `kirjapro_list_templates` hides `purchase_expense` and
32
+ `service_expense` from a company that is not VAT-registered.
33
+ - `kirjapro_create_transaction(s_batch)` and the server instructions describe
34
+ the net sales-account rule (U-3): a net credit on 5000-5499, a sales line
35
+ with a 4210 or 2320 line and a net 4210 credit are refused; a transfer out of
36
+ a sales account without a VAT line, such as a fixed-asset disposal, is
37
+ allowed. They also say that a free 2320 line does not reach field 307.
38
+ - `kirjapro_get_alv_summary` documents `output_vat_non_eu_services`, the VAT on
39
+ services bought from outside the EU that fields 301-303 now include.
40
+ - `kirjapro_get_alv_summary` returns every official OmaVero field in
41
+ `omavero_fields` (VSRALVKV v1.10: 301-314 and 318-320) from the database's
42
+ `omavero` map: 307 is the total deductible VAT including reverse charge and
43
+ import VAT, 308 = 301+302+303+304+305+306+318 - 307, 309 excludes EU sales,
44
+ and 311 follows AVL 138 a §. The legacy keys `field_307`…`field_313` (and
45
+ their `_base` keys), `field_318`-`field_320`, `total_zero_rated` and
46
+ `reverse_charge_base` are no longer returned: `field_307` was only the
47
+ domestic deductible VAT and the others were purchase categories, not field
48
+ numbers. New `exempt_sales_base` (sales without a deduction right, in no
49
+ field); unclassified legacy reverse-charge invoice lines add a `warning`.
50
+ - `kirjapro://compliance/*`: field 309 excludes EU sales (311, 312) and exempt
51
+ sales; cash-basis VAT needs no OmaVero opt-in and leaves EU and import items
52
+ on the accrual basis; a correction within 45 days carries no late-filing fee
53
+ regardless of the amount; the reverse-charge field list names 301-303 for
54
+ non-EU services, 304/310 for imports and the base fields.
55
+ - Review the fingerprints of `kirjapro_book_purchase`,
56
+ `kirjapro_create_from_template(_batch)`, `kirjapro_list_templates`,
57
+ `kirjapro_create_transaction(s_batch)` and `kirjapro_get_alv_summary`.
58
+
59
+ ## [0.10.0] - 2026-10-02
4
60
 
5
61
  Requires the database migrations `20261001204555`–`20261001204559` (ALV report
6
62
  audit batch 1). Version 0.9.1 keeps working against that database: its sales
package/dist/index.js CHANGED
@@ -35679,7 +35679,7 @@ var StdioServerTransport = class {
35679
35679
  import { createClient } from "@supabase/supabase-js";
35680
35680
 
35681
35681
  // src/version.ts
35682
- var VERSION = "0.10.0";
35682
+ var VERSION = "0.11.0";
35683
35683
 
35684
35684
  // src/auth.ts
35685
35685
  var SUPABASE_PROJECT_URL = "https://tifewzsimmyoiepblhld.supabase.co";
@@ -36882,7 +36882,7 @@ function registerTransactionTools(server2) {
36882
36882
  server2.registerTool(
36883
36883
  "kirjapro_create_transaction",
36884
36884
  {
36885
- description: "Create a transaction (tosite) with journal entries. Entries must balance (debits = credits). Accepts account numbers resolved to UUIDs server-side. Prefer kirjapro_create_from_template for standard patterns. For a VAT-registered (ALV) company the database refuses lines on sales accounts 5000-5499 and a net 4210 credit: sales are invoices.",
36885
+ description: "Create a transaction (tosite) with journal entries. Entries must balance (debits = credits). Accepts account numbers resolved to UUIDs server-side. Prefer kirjapro_create_from_template for standard patterns. For a VAT-registered (ALV) company the database refuses a net credit on sales accounts 5000-5499, a sales-account line together with a 4210 or 2320 line, and a net 4210 credit: sales are invoices. Moving an amount out of a sales account without a VAT line (e.g. a fixed-asset disposal D 5010 / K 1600) is allowed. A purchase with deductible VAT belongs in kirjapro_book_purchase so the VAT reaches field 307; a free 2320 line is listed by the ALV reconciliation.",
36886
36886
  inputSchema: {
36887
36887
  fiscalYearId: external_exports.string().uuid().optional().describe("Fiscal year UUID (auto-resolved if omitted)"),
36888
36888
  transactionDate: ISO_DATE_SCHEMA.describe("Transaction date (YYYY-MM-DD)"),
@@ -37021,7 +37021,7 @@ function registerTransactionTools(server2) {
37021
37021
  server2.registerTool(
37022
37022
  "kirjapro_create_transactions_batch",
37023
37023
  {
37024
- description: "Preview or atomically create multiple transactions in one call (up to 50). dryRun defaults to true. Commit is always all-or-nothing; partial/best-effort writes are intentionally unavailable. For a VAT-registered (ALV) company the database refuses lines on sales accounts 5000-5499 and a net 4210 credit: sales are invoices.",
37024
+ description: "Preview or atomically create multiple transactions in one call (up to 50). dryRun defaults to true. Commit is always all-or-nothing; partial/best-effort writes are intentionally unavailable. For a VAT-registered (ALV) company the database refuses a net credit on sales accounts 5000-5499, a sales-account line together with a 4210 or 2320 line, and a net 4210 credit: sales are invoices. Moving an amount out of a sales account without a VAT line (e.g. a fixed-asset disposal D 5010 / K 1600) is allowed. A purchase with deductible VAT belongs in kirjapro_book_purchase so the VAT reaches field 307; a free 2320 line is listed by the ALV reconciliation.",
37025
37025
  inputSchema: {
37026
37026
  fiscalYearId: external_exports.string().uuid().optional().describe("Fiscal year UUID (auto-resolved if omitted)"),
37027
37027
  dryRun: external_exports.boolean().default(true).describe(
@@ -38439,6 +38439,83 @@ function registerCustomerTools(server2) {
38439
38439
  );
38440
38440
  }
38441
38441
 
38442
+ // src/compliance/omavero-fields.ts
38443
+ var OMAVERO_FIELD_NUMBERS = [
38444
+ "301",
38445
+ "302",
38446
+ "303",
38447
+ "304",
38448
+ "305",
38449
+ "306",
38450
+ "307",
38451
+ "308",
38452
+ "309",
38453
+ "310",
38454
+ "311",
38455
+ "312",
38456
+ "313",
38457
+ "314",
38458
+ "318",
38459
+ "319",
38460
+ "320"
38461
+ ];
38462
+ var LEGACY_ALV_SUMMARY_KEYS = [
38463
+ "field_307",
38464
+ "field_307_base",
38465
+ "field_308",
38466
+ "field_308_base",
38467
+ "field_309",
38468
+ "field_309_base",
38469
+ "field_310",
38470
+ "field_310_base",
38471
+ "field_312",
38472
+ "field_312_base",
38473
+ "field_313",
38474
+ "field_313_base",
38475
+ "field_318",
38476
+ "field_319",
38477
+ "field_320",
38478
+ "total_zero_rated",
38479
+ "reverse_charge_base",
38480
+ "omavero"
38481
+ ];
38482
+ function cents(value) {
38483
+ return Math.round(value * 100) / 100;
38484
+ }
38485
+ function num(value) {
38486
+ const n = typeof value === "number" ? value : Number(value ?? 0);
38487
+ return Number.isFinite(n) ? n : 0;
38488
+ }
38489
+ function omaveroFieldsFromSummary(s) {
38490
+ const fields = {
38491
+ "301": num(s.field_301),
38492
+ "302": num(s.field_302),
38493
+ "303": num(s.field_303),
38494
+ "304": num(s.output_vat_import),
38495
+ "305": num(s.output_vat_eu_goods),
38496
+ "306": num(s.output_vat_eu_services),
38497
+ "307": num(s.total_input_vat),
38498
+ "308": num(s.net_vat_payable),
38499
+ "309": num(s.zero_vat_turnover),
38500
+ "310": num(s.field_312_base),
38501
+ "311": num(s.eu_goods_sales_base),
38502
+ "312": num(s.eu_service_sales_base),
38503
+ "313": num(s.field_308_base),
38504
+ "314": num(s.field_309_base),
38505
+ "318": num(s.output_vat_construction_scrap),
38506
+ "319": num(s.reverse_charge_sales_base),
38507
+ "320": cents(num(s.field_310_base) + num(s.field_313_base))
38508
+ };
38509
+ const map2 = s.omavero;
38510
+ if (map2 && typeof map2 === "object") {
38511
+ const values = map2;
38512
+ for (const field of OMAVERO_FIELD_NUMBERS) {
38513
+ if (values[field] !== void 0 && values[field] !== null) fields[field] = num(values[field]);
38514
+ }
38515
+ }
38516
+ return fields;
38517
+ }
38518
+
38442
38519
  // src/lib/incomeStatementSynthesis.ts
38443
38520
  function synthesizeIncomeStatementLines(lineTypes, currentByLine, previousByLine) {
38444
38521
  const lineAmounts = new Map(currentByLine);
@@ -38593,18 +38670,28 @@ var ALV_SUMMARY_OUTPUT_SHAPE = {
38593
38670
  input_vat: external_exports.unknown().optional(),
38594
38671
  net_vat_payable: external_exports.number().optional(),
38595
38672
  net_vat_payable_formatted: external_exports.string().optional(),
38596
- omavero_fields: external_exports.record(external_exports.string(), external_exports.number()).optional(),
38673
+ // Every official OmaVero field (VSRALVKV v1.10: 301-314, 318-320), ALV
38674
+ // report audit 2026-10-01, T2-11. The only field-numbered output.
38675
+ omavero_fields: external_exports.record(external_exports.string(), external_exports.number()).describe(
38676
+ "Official OmaVero VAT-return fields 301-314 and 318-320. 307 is all deductible VAT; 308 = 301+302+303+304+305+306+318 - 307."
38677
+ ).optional(),
38678
+ // Sales without a deduction right: in no OmaVero field (informational).
38679
+ exempt_sales_base: external_exports.number().optional(),
38597
38680
  zero_vat_turnover: external_exports.number().optional(),
38598
38681
  eu_goods_sales_base: external_exports.number().optional(),
38599
38682
  reverse_charge_sales_base: external_exports.number().optional(),
38600
38683
  eu_service_sales_base: external_exports.number().optional(),
38601
38684
  unclassified_legacy_reverse_charge_base: external_exports.number().optional(),
38685
+ // VAT on services bought from outside the EU, already inside field_301-303
38686
+ // and total_output_vat (ALV report audit 2026-10-01, T2-13).
38687
+ output_vat_non_eu_services: external_exports.number().optional(),
38602
38688
  // get_alv_summary.reconciliation: the report against the 4210/2320 ledger
38603
38689
  // (ALV report audit 2026-10-01, T2-1). Absent on an older database.
38604
38690
  reconciliation: external_exports.object({ status: external_exports.enum(["match", "explained", "mismatch"]) }).passthrough().optional(),
38605
38691
  warning: external_exports.string().optional(),
38606
38692
  message: external_exports.string().optional()
38607
38693
  };
38694
+ var LEGACY_REVERSE_CHARGE_WARNING = "Legacy reverse-charge invoice lines must be reclassified before the fields are complete.";
38608
38695
  function alvReconciliationWarning(reconciliation) {
38609
38696
  if (!reconciliation || typeof reconciliation !== "object") return void 0;
38610
38697
  const r = reconciliation;
@@ -39087,7 +39174,7 @@ function registerReportTools(server2) {
39087
39174
  server2.registerTool(
39088
39175
  "kirjapro_get_alv_summary",
39089
39176
  {
39090
- description: "Get VAT (ALV) summary for a date range. Returns output/input VAT breakdown by rate and OmaVero field codes (301-320), and a reconciliation object comparing the 4210/2320 ledger with the report (status match, explained or mismatch, with the differences and the vouchers, invoices, fact mismatches and manual rows that explain them). A mismatch sets warning. For filing deadlines, read kirjapro://compliance/deadlines.",
39177
+ description: "Get the VAT (ALV) summary for a date range. Returns `omavero_fields`, every official OmaVero VAT-return field (VSRALVKV v1.10: 301-314, 318-320). 307 is the total deductible VAT including reverse charge and import VAT; 308 = (301+302+303+304+305+306+318) - 307. 309 excludes 311/312 sales; exempt sales without a deduction right (`exempt_sales_base`) belong in no field. 311 follows AVL 138 a \xA7 (the delivery month when invoiced on or after the delivery in that month; otherwise, including an advance invoice, the next month). Fields 301-303 include the self-assessed VAT on services bought from outside the EU (output_vat_non_eu_services). Copy fields only from `omavero_fields`. Also returns a reconciliation object comparing the 4210/2320 ledger with the report (status match, explained or mismatch, with the differences and the vouchers, invoices, fact mismatches and manual rows that explain them); a mismatch or unclassified legacy reverse-charge lines set warning. For filing deadlines, read kirjapro://compliance/deadlines.",
39091
39178
  inputSchema: {
39092
39179
  dateFrom: ISO_DATE_SCHEMA.describe("Period start date (YYYY-MM-DD)"),
39093
39180
  dateTo: ISO_DATE_SCHEMA.describe("Period end date (YYYY-MM-DD)")
@@ -39127,26 +39214,21 @@ function registerReportTools(server2) {
39127
39214
  });
39128
39215
  }
39129
39216
  const raw = result;
39130
- const {
39131
- field_318: _legacyField318,
39132
- field_319: _legacyField319,
39133
- field_320: _legacyField320,
39134
- ...safeResult
39135
- } = raw;
39136
- const warning = alvReconciliationWarning(raw.reconciliation);
39217
+ const omaveroFields = omaveroFieldsFromSummary(raw);
39218
+ const legacyKeys = new Set(LEGACY_ALV_SUMMARY_KEYS);
39219
+ const safeResult = Object.fromEntries(
39220
+ Object.entries(raw).filter(([key]) => !legacyKeys.has(key))
39221
+ );
39222
+ const warnings = [
39223
+ alvReconciliationWarning(raw.reconciliation),
39224
+ Number(raw.unclassified_legacy_reverse_charge_base) ? LEGACY_REVERSE_CHARGE_WARNING : void 0
39225
+ ].filter((text) => Boolean(text));
39137
39226
  return toResult({
39138
39227
  ...safeResult,
39139
- ...warning && { warning },
39228
+ ...warnings.length > 0 && { warning: warnings.join(" ") },
39140
39229
  period: { from: params.dateFrom, to: params.dateTo },
39141
39230
  net_vat_payable_formatted: formatCurrency(result.net_vat_payable || 0),
39142
- omavero_fields: {
39143
- "309": Number(raw.zero_vat_turnover) || 0,
39144
- "311": Number(raw.eu_goods_sales_base) || 0,
39145
- "312": Number(raw.eu_service_sales_base) || 0,
39146
- "318": Number(raw.output_vat_construction_scrap) || 0,
39147
- "319": Number(raw.reverse_charge_sales_base) || 0,
39148
- "320": (Number(raw.field_310_base) || 0) + (Number(raw.field_313_base) || 0)
39149
- }
39231
+ omavero_fields: omaveroFields
39150
39232
  });
39151
39233
  }
39152
39234
  );
@@ -39760,7 +39842,21 @@ var ACCOUNTS = {
39760
39842
 
39761
39843
  // src/compliance/vat-guard.ts
39762
39844
  var ALV_REGISTERED_SALE_REFUSAL = "This company is VAT-registered (ALV). A sale booked as a journal voucher never reaches the ALV report (fields 301-303 and 309), so Kirjapro refuses it. Create the sale with kirjapro_create_invoice, deliver it with kirjapro_send_invoice and record the payment with kirjapro_record_payment or bank matching.";
39763
- var ALV_REGISTERED_REVERSE_CHARGE_REFUSAL = "Reverse-charge purchases must be booked as a received invoice in the Kirjapro web app or as a manual Ostojen ALV row so fields 305/306/313/314 are filled. MCP support follows in a later release.";
39845
+ var NOT_VAT_REGISTERED_PURCHASE_REFUSAL = 'This company is not VAT-registered (ALV), so it deducts no VAT on purchases: book the purchase gross with vatRate "0". A service bought from a foreign seller (in another EU country or outside the EU) still makes the company liable for the self-assessed VAT: tell the user to add it as a non-deductible row on the Ostojen ALV page of the Kirjapro web app, to book the same VAT as a voucher debiting the expense account and crediting 4210 (ALV-velka), for example with kirjapro_create_transaction, and to check whether the company must register for VAT. Customs (Tulli) collects the import VAT of such a company. If the company is VAT-registered, update its settings in the Kirjapro web app first.';
39846
+ var REVERSE_CHARGE_CATEGORIES = [
39847
+ "eu_goods",
39848
+ "eu_services",
39849
+ "non_eu_services",
39850
+ "import",
39851
+ "construction",
39852
+ "scrap_metal"
39853
+ ];
39854
+ var REVERSE_CHARGE_CATEGORY_REQUIRED = "reverseChargeCategory is required: eu_goods (fields 305/313), eu_services (306/314), non_eu_services (301-303), import (304/310), construction or scrap_metal (318/320).";
39855
+ var REVERSE_CHARGE_RATE_REQUIRED = "A reverse-charge purchase needs the Finnish rate the buyer self-assesses (25.5, 13.5 or 10).";
39856
+ var PURCHASE_TEMPLATE_NOT_VAT_REGISTERED_REFUSAL = 'This company is not VAT-registered (ALV), so a purchase template with VAT (2320) cannot be used. Book the purchase gross with kirjapro_book_purchase and vatRate "0".';
39857
+ function purchaseTemplateBatchRefusal(indexes) {
39858
+ return `Purchase templates with VAT (index ${indexes.join(", ")}) are booked one at a time with kirjapro_create_from_template or kirjapro_book_purchase so the VAT reaches the ALV report; nothing was written.`;
39859
+ }
39764
39860
  var NOT_VAT_REGISTERED_SETTLEMENT_REFUSAL = "This company is not VAT-registered (ALV), so it has no VAT settlement to book.";
39765
39861
  async function readCompanyVat(supabase2, companyId2) {
39766
39862
  const { data, error: error61 } = await supabase2.from("companies").select("alv_registered, vat_period").eq("id", companyId2).single();
@@ -40034,8 +40130,17 @@ var TEMPLATE_MAP = new Map(TEMPLATES.map((t) => [t.id, t]));
40034
40130
  function isSaleTemplate(templateId) {
40035
40131
  return TEMPLATE_MAP.get(templateId)?.kind === "sale";
40036
40132
  }
40133
+ function inputVatOf(entries) {
40134
+ return roundCents(
40135
+ entries.filter((e) => e.accountNumber === ACCOUNTS.ALV_SAAMISET).reduce((sum, e) => sum + e.debitAmount - e.creditAmount, 0)
40136
+ );
40137
+ }
40138
+ function isVatPurchaseTemplate(templateId) {
40139
+ const template = TEMPLATE_MAP.get(templateId);
40140
+ return template?.kind === "purchase" && inputVatOf(template.expand(100)) !== 0;
40141
+ }
40037
40142
  function availableTemplates(alvRegistered) {
40038
- return alvRegistered ? TEMPLATES.filter((t) => t.kind !== "sale") : TEMPLATES;
40143
+ return alvRegistered ? TEMPLATES.filter((t) => t.kind !== "sale") : TEMPLATES.filter((t) => !isVatPurchaseTemplate(t.id));
40039
40144
  }
40040
40145
  function expandTemplate(templateId, amount, opts) {
40041
40146
  const template = TEMPLATE_MAP.get(templateId);
@@ -40099,7 +40204,7 @@ function toJournalEntries(entries, accountMap) {
40099
40204
  credit_amount: e.creditAmount
40100
40205
  }));
40101
40206
  }
40102
- async function resolveAndCreate(params) {
40207
+ async function preparePosting(params) {
40103
40208
  const supabase2 = getSupabase();
40104
40209
  const companyId2 = getCompanyId();
40105
40210
  let fiscalYearId = params.fiscalYearId;
@@ -40128,16 +40233,41 @@ async function resolveAndCreate(params) {
40128
40233
  `Template produced unbalanced entries: debits ${totalDebit.toFixed(2)} != credits ${totalCredit.toFixed(2)}.`
40129
40234
  );
40130
40235
  }
40131
- const journalEntries = toJournalEntries(params.entries, accountResult);
40132
- const { data: transactionId, error: txError } = await supabase2.rpc(
40236
+ return { fiscalYearId, journalEntries: toJournalEntries(params.entries, accountResult) };
40237
+ }
40238
+ async function resolveAndCreatePurchase(params) {
40239
+ const prepared = await preparePosting(params);
40240
+ if ("isError" in prepared) return prepared;
40241
+ const { data, error: error61 } = await getSupabase().rpc("book_purchase_with_vat", {
40242
+ p_company_id: getCompanyId(),
40243
+ p_fiscal_year_id: prepared.fiscalYearId,
40244
+ p_transaction_date: params.transactionDate,
40245
+ p_description: params.description,
40246
+ p_document_number: params.documentNumber || "",
40247
+ p_entries: prepared.journalEntries,
40248
+ p_vat_rows: params.vatRows,
40249
+ ...params.supplierName ? { p_supplier_name: params.supplierName } : {}
40250
+ });
40251
+ const result = data;
40252
+ if (error61 || !result?.transaction_id) {
40253
+ return toErrorContent(
40254
+ error61 ? sanitizeDbError("Failed to book the purchase", error61) : "Failed to book the purchase: no ID returned."
40255
+ );
40256
+ }
40257
+ return { id: result.transaction_id, vatEntryIds: result.vat_entry_ids ?? [] };
40258
+ }
40259
+ async function resolveAndCreate(params) {
40260
+ const prepared = await preparePosting(params);
40261
+ if ("isError" in prepared) return prepared;
40262
+ const { data: transactionId, error: txError } = await getSupabase().rpc(
40133
40263
  "create_transaction_with_entries",
40134
40264
  {
40135
- p_company_id: companyId2,
40136
- p_fiscal_year_id: fiscalYearId,
40265
+ p_company_id: getCompanyId(),
40266
+ p_fiscal_year_id: prepared.fiscalYearId,
40137
40267
  p_transaction_date: params.transactionDate,
40138
40268
  p_description: params.description,
40139
40269
  p_document_number: params.documentNumber || "",
40140
- p_entries: journalEntries
40270
+ p_entries: prepared.journalEntries
40141
40271
  }
40142
40272
  );
40143
40273
  if (txError || !transactionId) {
@@ -40151,7 +40281,7 @@ function registerTemplateTools(server2) {
40151
40281
  server2.registerTool(
40152
40282
  "kirjapro_list_templates",
40153
40283
  {
40154
- description: "List available transaction templates. Templates handle double-entry balancing and VAT splitting automatically. Sale templates are not listed for a VAT-registered (ALV) company: its sales are invoices.",
40284
+ description: "List available transaction templates. Templates handle double-entry balancing and VAT splitting automatically. Sale templates are not listed for a VAT-registered (ALV) company: its sales are invoices. Purchase templates with VAT are not listed for a company that is not VAT-registered.",
40155
40285
  inputSchema: {},
40156
40286
  outputSchema: {
40157
40287
  templates: external_exports.array(external_exports.object({
@@ -40176,7 +40306,7 @@ function registerTemplateTools(server2) {
40176
40306
  server2.registerTool(
40177
40307
  "kirjapro_create_from_template",
40178
40308
  {
40179
- description: 'Create a transaction from a template. Use kirjapro_list_templates for available templates. accountOverrides swaps default accounts (e.g. {"2320": "2330"}). Sale templates are refused for a VAT-registered (ALV) company: create an invoice instead.',
40309
+ description: 'Create a transaction from a template. Use kirjapro_list_templates for available templates. accountOverrides swaps default accounts (e.g. {"2320": "2330"}). Sale templates are refused for a VAT-registered (ALV) company: create an invoice instead. A purchase template with VAT (2320) books its VAT fact so the VAT reaches field 307; for a company that is not VAT-registered it is refused (book the purchase gross).',
40180
40310
  inputSchema: {
40181
40311
  template: external_exports.string().describe('Template ID (e.g. "cash_sale_25", "purchase_expense")'),
40182
40312
  fiscalYearId: external_exports.string().uuid().optional().describe("Fiscal year UUID (auto-resolved if omitted)"),
@@ -40192,10 +40322,15 @@ function registerTemplateTools(server2) {
40192
40322
  async (params) => {
40193
40323
  const supabase2 = getSupabase();
40194
40324
  const companyId2 = getCompanyId();
40195
- if (isSaleTemplate(params.template)) {
40325
+ const template = TEMPLATE_MAP.get(params.template);
40326
+ let alvRegistered = null;
40327
+ if (template && (template.kind === "sale" || template.kind === "purchase")) {
40196
40328
  const companyVat = await readCompanyVat(supabase2, companyId2);
40197
40329
  if ("isError" in companyVat) return companyVat;
40198
- if (companyVat.alvRegistered) return toErrorContent(ALV_REGISTERED_SALE_REFUSAL);
40330
+ alvRegistered = companyVat.alvRegistered;
40331
+ if (template.kind === "sale" && alvRegistered) {
40332
+ return toErrorContent(ALV_REGISTERED_SALE_REFUSAL);
40333
+ }
40199
40334
  }
40200
40335
  return withIdempotency(
40201
40336
  supabase2,
@@ -40214,7 +40349,27 @@ function registerTemplateTools(server2) {
40214
40349
  if ("error" in result) {
40215
40350
  return toErrorContent(result.error);
40216
40351
  }
40217
- const createResult = await resolveAndCreate({
40352
+ const inputVat = template?.kind === "purchase" ? inputVatOf(result.entries) : 0;
40353
+ if (inputVat !== 0 && alvRegistered === false) {
40354
+ return toErrorContent(PURCHASE_TEMPLATE_NOT_VAT_REGISTERED_REFUSAL);
40355
+ }
40356
+ const createResult = inputVat !== 0 ? await resolveAndCreatePurchase({
40357
+ fiscalYearId: params.fiscalYearId,
40358
+ transactionDate: params.transactionDate,
40359
+ description: params.description,
40360
+ documentNumber: params.documentNumber,
40361
+ entries: result.entries,
40362
+ vatRows: [
40363
+ {
40364
+ category: "domestic",
40365
+ vat_rate: template?.vatRate ?? 0,
40366
+ tax_base: roundCents(
40367
+ result.entries.filter((e) => e.accountNumber !== ACCOUNTS.ALV_SAAMISET).reduce((sum, e) => sum + e.debitAmount, 0)
40368
+ ),
40369
+ vat_amount: inputVat
40370
+ }
40371
+ ]
40372
+ }) : await resolveAndCreate({
40218
40373
  fiscalYearId: params.fiscalYearId,
40219
40374
  transactionDate: params.transactionDate,
40220
40375
  description: params.description,
@@ -40228,7 +40383,8 @@ function registerTemplateTools(server2) {
40228
40383
  message: "Transaction created from template",
40229
40384
  template: params.template,
40230
40385
  transaction_id: createResult.id,
40231
- entries: result.entries
40386
+ entries: result.entries,
40387
+ ..."vatEntryIds" in createResult && { vat_entry_ids: createResult.vatEntryIds }
40232
40388
  });
40233
40389
  }
40234
40390
  );
@@ -40237,7 +40393,7 @@ function registerTemplateTools(server2) {
40237
40393
  server2.registerTool(
40238
40394
  "kirjapro_create_from_template_batch",
40239
40395
  {
40240
- description: "Preview or atomically create multiple template transactions in one call (up to 50). dryRun defaults to true. Commit is always all-or-nothing; partial/best-effort writes are intentionally unavailable. A batch with a sale template is refused for a VAT-registered (ALV) company before any write.",
40396
+ description: "Preview or atomically create multiple template transactions in one call (up to 50). dryRun defaults to true. Commit is always all-or-nothing; partial/best-effort writes are intentionally unavailable. A batch with a sale template is refused for a VAT-registered (ALV) company before any write. Purchase templates with VAT (2320) are refused in a batch: book them one at a time with kirjapro_create_from_template so the VAT reaches the ALV report.",
40241
40397
  inputSchema: {
40242
40398
  fiscalYearId: external_exports.string().uuid().optional().describe("Fiscal year UUID (auto-resolved if omitted)"),
40243
40399
  dryRun: external_exports.boolean().default(true).describe(
@@ -40260,6 +40416,17 @@ function registerTemplateTools(server2) {
40260
40416
  async (params) => {
40261
40417
  const supabase2 = getSupabase();
40262
40418
  const companyId2 = getCompanyId();
40419
+ const vatPurchaseIndexes = params.transactions.map((tx, index) => {
40420
+ const template = TEMPLATE_MAP.get(tx.template);
40421
+ if (template?.kind !== "purchase") return -1;
40422
+ const expanded = expandTemplate(tx.template, tx.amount, {
40423
+ accountOverrides: tx.accountOverrides
40424
+ });
40425
+ return "entries" in expanded && inputVatOf(expanded.entries) !== 0 ? index : -1;
40426
+ }).filter((index) => index >= 0);
40427
+ if (vatPurchaseIndexes.length > 0) {
40428
+ return toErrorContent(purchaseTemplateBatchRefusal(vatPurchaseIndexes));
40429
+ }
40263
40430
  const saleIndexes = params.transactions.map((tx, index) => isSaleTemplate(tx.template) ? index : -1).filter((index) => index >= 0);
40264
40431
  if (saleIndexes.length > 0) {
40265
40432
  const companyVat = await readCompanyVat(supabase2, companyId2);
@@ -40548,7 +40715,7 @@ function registerBookingTools(server2) {
40548
40715
  server2.registerTool(
40549
40716
  "kirjapro_book_purchase",
40550
40717
  {
40551
- description: "Book a purchase (osto) with automatic VAT splitting. Supports reverse charge (AVL 9\xA7) for companies that are not VAT-registered and entertainment (edustus, AVL 114\xA7). For a VAT-registered (ALV) company reverseCharge is refused: book it as a received invoice in the web app. Rate rules: food/restaurants 13.5%, insurance 0% (exempt), alcohol 25.5%.",
40718
+ description: "Book a purchase (osto) with automatic VAT splitting. For a VAT-registered (ALV) company the VAT is recorded as a VAT fact, so it reaches field 307 (and the reverse-charge fields). Reverse charge (reverseCharge=true, with reverseChargeCategory) self-assesses the VAT for EU goods and services, services from outside the EU, import, construction and scrap metal. Entertainment (edustus, AVL 114\xA7) is booked gross. Statutorily non-deductible accounts (wages, insurance, interest, depreciation, taxes, representation) are refused with VAT: book them with vatRate 0. A company that is not VAT-registered books purchases gross with vatRate 0. Rate rules: food/restaurants 13.5%, insurance 0% (exempt), alcohol 25.5%.",
40552
40719
  inputSchema: {
40553
40720
  amount: external_exports.number().finite().positive().max(1e9).describe(
40554
40721
  "EUR amount. Standard purchase: GROSS (VAT-inclusive) \u2014 VAT is split out of this. Reverse charge (reverseCharge=true): NET foreign-invoice amount \u2014 the foreign supplier does not charge Finnish VAT, so the parameter must be net. VAT is self-assessed on top per AVL 9\xA7 producing equal debit to ALV-saamiset and credit to ALV-velka. Entertainment (edustus=true): GROSS (full amount to expense, no VAT deduction per AVL 114\xA7)."
@@ -40557,7 +40724,11 @@ function registerBookingTools(server2) {
40557
40724
  date: ISO_DATE_SCHEMA.describe("Transaction date (YYYY-MM-DD)"),
40558
40725
  vatRate: external_exports.enum(["25.5", "13.5", "10", "0"]).describe("Finnish VAT rate"),
40559
40726
  expenseAccount: external_exports.string().describe('Expense account number (e.g. "8080")'),
40560
- reverseCharge: external_exports.boolean().default(false).describe(`EU reverse charge: D expense + D ${ACCOUNTS.ALV_SAAMISET} ALV-saamiset + C ${ACCOUNTS.ALV_VELKA} ALV-velka + C bank`),
40727
+ reverseCharge: external_exports.boolean().default(false).describe(`Reverse charge (k\xE4\xE4nnetty verovelvollisuus): D expense + D ${ACCOUNTS.ALV_SAAMISET} ALV-saamiset + C ${ACCOUNTS.ALV_VELKA} ALV-velka + C bank, with a VAT fact of reverseChargeCategory`),
40728
+ reverseChargeCategory: external_exports.enum(REVERSE_CHARGE_CATEGORIES).optional().describe(
40729
+ "Required with reverseCharge: eu_goods (fields 305/313), eu_services (306/314), non_eu_services (services from outside the EU, 301-303), import (304/310), construction or scrap_metal (318/320)"
40730
+ ),
40731
+ supplierName: external_exports.string().max(200).optional().describe("Supplier name, stored on the VAT fact"),
40561
40732
  edustus: external_exports.boolean().default(false).describe("Entertainment expense (AVL 114\xA7): 0% VAT deduction, full gross to expense"),
40562
40733
  paymentAccount: external_exports.string().default(ACCOUNTS.PANKKITILI).describe(`Payment account (default "${ACCOUNTS.PANKKITILI}" Pankkitili)`),
40563
40734
  fiscalYearId: external_exports.string().uuid().optional().describe("Fiscal year UUID (auto-resolved if omitted)"),
@@ -40571,10 +40742,15 @@ function registerBookingTools(server2) {
40571
40742
  const rate = Number.parseFloat(params.vatRate);
40572
40743
  const gross = roundCents(params.amount);
40573
40744
  let entries;
40745
+ let vatRows = [];
40746
+ const companyVat = await readCompanyVat(getSupabase(), getCompanyId());
40747
+ if ("isError" in companyVat) return companyVat;
40748
+ if (!params.edustus && !companyVat.alvRegistered && (rate !== 0 || params.reverseCharge)) {
40749
+ return toErrorContent(NOT_VAT_REGISTERED_PURCHASE_REFUSAL);
40750
+ }
40574
40751
  if (params.reverseCharge && !params.edustus) {
40575
- const vat = await readCompanyVat(getSupabase(), getCompanyId());
40576
- if ("isError" in vat) return vat;
40577
- if (vat.alvRegistered) return toErrorContent(ALV_REGISTERED_REVERSE_CHARGE_REFUSAL);
40752
+ if (!params.reverseChargeCategory) return toErrorContent(REVERSE_CHARGE_CATEGORY_REQUIRED);
40753
+ if (rate === 0) return toErrorContent(REVERSE_CHARGE_RATE_REQUIRED);
40578
40754
  }
40579
40755
  if (params.edustus) {
40580
40756
  entries = [
@@ -40591,6 +40767,14 @@ function registerBookingTools(server2) {
40591
40767
  // ALV-velka (output)
40592
40768
  { accountNumber: params.paymentAccount, debitAmount: 0, creditAmount: gross }
40593
40769
  ];
40770
+ vatRows = [
40771
+ {
40772
+ category: params.reverseChargeCategory ?? "eu_services",
40773
+ vat_rate: rate,
40774
+ tax_base: gross,
40775
+ vat_amount: vat
40776
+ }
40777
+ ];
40594
40778
  } else if (rate === 0) {
40595
40779
  entries = [
40596
40780
  { accountNumber: params.expenseAccount, debitAmount: gross, creditAmount: 0 },
@@ -40603,6 +40787,7 @@ function registerBookingTools(server2) {
40603
40787
  { accountNumber: ACCOUNTS.ALV_SAAMISET, debitAmount: vat, creditAmount: 0 },
40604
40788
  { accountNumber: params.paymentAccount, debitAmount: 0, creditAmount: gross }
40605
40789
  ];
40790
+ vatRows = [{ category: "domestic", vat_rate: rate, tax_base: net, vat_amount: vat }];
40606
40791
  }
40607
40792
  const purchaseAccountWarning = expenseAccountWarning(params.expenseAccount);
40608
40793
  if (params.dryRun) {
@@ -40613,6 +40798,7 @@ function registerBookingTools(server2) {
40613
40798
  description: params.description,
40614
40799
  date: params.date,
40615
40800
  entries,
40801
+ ...vatRows.length > 0 && { vat_rows: vatRows },
40616
40802
  ...purchaseAccountWarning && { account_warning: purchaseAccountWarning }
40617
40803
  });
40618
40804
  }
@@ -40623,18 +40809,24 @@ function registerBookingTools(server2) {
40623
40809
  params.idempotencyKey,
40624
40810
  params,
40625
40811
  async () => {
40626
- const result = await resolveAndCreate({
40812
+ const posting = {
40627
40813
  fiscalYearId: params.fiscalYearId,
40628
40814
  transactionDate: params.date,
40629
40815
  description: params.description,
40630
40816
  documentNumber: params.documentNumber,
40631
40817
  entries
40632
- });
40818
+ };
40819
+ const result = vatRows.length > 0 ? await resolveAndCreatePurchase({
40820
+ ...posting,
40821
+ vatRows,
40822
+ supplierName: params.supplierName
40823
+ }) : await resolveAndCreate(posting);
40633
40824
  if ("isError" in result) return result;
40634
40825
  return toResult({
40635
40826
  message: "Purchase booked",
40636
40827
  transaction_id: result.id,
40637
40828
  entries,
40829
+ ..."vatEntryIds" in result && { vat_entry_ids: result.vatEntryIds },
40638
40830
  ...purchaseAccountWarning && { warning: purchaseAccountWarning }
40639
40831
  });
40640
40832
  }
@@ -42308,7 +42500,7 @@ var VAT_COMPLIANCE = {
42308
42500
  ],
42309
42501
  booking: "4 entries: D expense + D ALV-saamiset + C bank + C ALV-velka (k\xE4\xE4nnetty)",
42310
42502
  note: "Net VAT effect is zero but BOTH must be reported on ALV-ilmoitus. Invoice must NOT show VAT rate or VAT amount",
42311
- omavero_fields: "Foreign: 305 (EU goods), 306 (EU services). Domestic construction/scrap: 318 (VAT amount), 319/320 (tax bases)"
42503
+ omavero_fields: "EU goods: 305 (VAT) / 313 (base); EU services: 306 / 314; services from outside the EU: VAT in 301-303 by rate (no base field); imports: 304 / 310; domestic construction/scrap purchases: 318 / 320, sales 319. The deductible part of each is in 307."
42312
42504
  },
42313
42505
  common_mistakes: [
42314
42506
  "Food/coffee at 25.5% \u2014 should be 13.5% (AVL 85\xA7)",
@@ -42387,14 +42579,18 @@ var FILING_DEADLINES = {
42387
42579
  },
42388
42580
  maksuperusteinen_alv: {
42389
42581
  threshold: 5e5,
42390
- note: "Revenue under 500,000 \u20AC: may opt for cash-basis VAT (report when payment received/made, not when invoiced)",
42391
- application: "Opt-in via OmaVero"
42582
+ note: "Turnover up to 500,000 \u20AC per calendar year: domestic sales VAT may be reported when payment is received and purchase VAT deducted when paid (cash basis), consistently for all domestic sales and purchases; imports, exports, intra-EU sales and acquisitions and cross-border reverse-charge services stay on the accrual basis. Kirjapro reports on the accrual basis only.",
42583
+ application: "No application: the business applies the cash basis in its bookkeeping. Since 2025 the VAT return has no cash-basis field.",
42584
+ source: "https://www.vero.fi/yritykset-ja-yhteisot/verot-ja-maksut/arvonlisaverotus/pienyrityksen-maksuperusteinen-alv/",
42585
+ sources_verified_at: "2026-10-02"
42392
42586
  },
42393
42587
  late_penalties: {
42394
- myohastymismaksu: "3 \u20AC/day for days 1-45 (max 135 \u20AC), then +2% of tax amount (max 15,000 \u20AC per tax type)",
42588
+ myohastymismaksu: "3 \u20AC/day for at most 45 days (135 \u20AC); more than 45 days late: 135 \u20AC + 2 % of the declared payable tax (max 15,000 \u20AC per tax type and period); also for returns with no tax payable",
42395
42589
  viivastyskorko_2026: "9.5% for most taxes, 4.5% for inheritance tax",
42396
- correction_grace: "Corrections within 45 days of original deadline: no penalty if change \u2264 500 \u20AC",
42397
- nil_return: "Even when no VAT activity occurred, a nil return must be filed for each verokausi"
42590
+ correction_grace: "A correction return filed within 45 days of the original due date carries no late-filing fee (no amount condition). Later corrections: 2 % of the additional payable tax.",
42591
+ nil_return: "Even when no VAT activity occurred, a nil return must be filed for each verokausi",
42592
+ source: "https://www.vero.fi/syventavat-vero-ohjeet/ohje-hakusivu/48377/seuraamusmaksut-oma-aloitteisessa-verotuksessa5/",
42593
+ sources_verified_at: "2026-10-02"
42398
42594
  },
42399
42595
  payment_deadline: "Same as filing deadline (12th of M+2 for monthly/quarterly, last day of Feb for annual)",
42400
42596
  omavero_field_codes: {
@@ -42412,16 +42608,17 @@ var FILING_DEADLINES = {
42412
42608
  318: "Vero rakentamispalvelun ja metalliromun ostoista (construction/scrap reverse charge VAT)"
42413
42609
  },
42414
42610
  tax_bases: {
42415
- 309: "0-verokannan alainen liikevaihto (zero-rate turnover: exports, EU sales with deduction right)",
42611
+ 309: "0-verokannan alainen liikevaihto (zero-rate turnover, e.g. exports of goods outside the EU, AVL 55-72 h \xA7). Does NOT include EU goods sales (311) or EU service sales (312). Exempt sales without a deduction right (healthcare, education, finance, insurance) are reported in no field.",
42416
42612
  310: "Tavaroiden maahantuonnit EU:n ulkopuolelta (goods imports from outside EU)",
42417
- 311: "Tavaroiden myynnit muihin EU-maihin (goods sales to EU)",
42613
+ 311: "Tavaroiden myynnit muihin EU-maihin (goods sales to EU; reported for the delivery month when invoiced on or after the delivery in that month, otherwise, including an advance invoice, the next month, AVL 138 a \xA7)",
42418
42614
  312: "Palveluiden myynnit muihin EU-maihin (service sales to EU)",
42419
42615
  313: "Tavaraostot muista EU-maista (goods purchases from EU)",
42420
42616
  314: "Palveluostot muista EU-maista (service purchases from EU)",
42421
42617
  319: "Rakentamispalvelun ja metalliromun myynnit (construction/scrap sales)",
42422
42618
  320: "Rakentamispalvelun ja metalliromun ostot (construction/scrap purchases)"
42423
42619
  },
42424
- removed_2025: "315, 316, 317 (alarajahuojennus fields) removed from 1.1.2025"
42620
+ removed_2025: "315, 316, 317 (alarajahuojennus fields) removed from 1.1.2025",
42621
+ sources_verified_at: "2026-10-02"
42425
42622
  }
42426
42623
  };
42427
42624
  var SOCIAL_INSURANCE_2026 = {
@@ -42702,7 +42899,7 @@ Do not invent a "closest match" tool for these tasks. A confidently wrong action
42702
42899
  A bank transaction that is already booked (a receipt or purchase booked as paid from the bank account, a manual invoice payment, a paid purchase invoice, a Kirjapro payment batch) is linked, never posted again: \`kirjapro_list_bank_link_candidates\` \u2192 \`kirjapro_link_bank_transaction\`. Never post a manual voucher for an already-booked or skipped bank transaction; a wrong reconciliation is undone in the Kirjapro app (Pankki \u2192 T\xE4sm\xE4ytys \u2192 T\xE4sm\xE4ytetyt \u2192 Pura t\xE4sm\xE4ytys) or, on the full surface, with \`kirjapro_unreconcile_bank_transaction\`, and then matched again.
42703
42900
 
42704
42901
  ### Reports
42705
- \`kirjapro_get_dashboard\` (overview) | \`kirjapro_get_income_statement\` (tuloslaskelma) | \`kirjapro_get_balance_sheet\` (tase \u2014 \`equity.synthetic_tilikauden_tulos\` is the unclosed P/L residual already included in equity; never add it again. The cumulative report groups current earnings under 3600 and retained earnings under 3500. Report rows can differ from raw ledger account balances. Do not present book equity as an approved distribution limit) | \`kirjapro_get_ar_aging\` (bounded myyntisaamisten aggregate; weighted age is not revenue-based DSO) | \`kirjapro_get_alv_summary\` (ALV-yhteenveto with OmaVero field codes 301-320).
42902
+ \`kirjapro_get_dashboard\` (overview) | \`kirjapro_get_income_statement\` (tuloslaskelma) | \`kirjapro_get_balance_sheet\` (tase \u2014 \`equity.synthetic_tilikauden_tulos\` is the unclosed P/L residual already included in equity; never add it again. The cumulative report groups current earnings under 3600 and retained earnings under 3500. Report rows can differ from raw ledger account balances. Do not present book equity as an approved distribution limit) | \`kirjapro_get_ar_aging\` (bounded myyntisaamisten aggregate; weighted age is not revenue-based DSO) | \`kirjapro_get_alv_summary\` (ALV-yhteenveto; the official OmaVero fields are in \`omavero_fields\`).
42706
42903
 
42707
42904
  ## Safety Rules
42708
42905
 
@@ -42768,7 +42965,7 @@ var INSTRUCTIONS_CORE_ADDENDUM = `
42768
42965
  The following workflows are NOT exposed in the current (core) surface. When a user asks for them, redirect to the web UI rather than reaching for the closest available tool:
42769
42966
 
42770
42967
  - **Recurring invoice templates** \u2014 created and managed in the web app; cron generates the issued invoices
42771
- - **Booking shortcuts** (book_purchase / book_sale / book_vat_settlement) \u2014 for these, you CAN compose the journal entries manually via \`kirjapro_create_transaction\` with explicit debit/credit lines if the user prefers; otherwise redirect. Exception: a VAT-registered (ALV) company books sales only as invoices (\`kirjapro_create_invoice\` + \`kirjapro_send_invoice\`) and reverse-charge purchases in the web app; the database refuses its vouchers with sales accounts 5000-5499 or a net 4210 credit
42968
+ - **Booking shortcuts** (book_purchase / book_sale / book_vat_settlement) \u2014 for these, you CAN compose the journal entries manually via \`kirjapro_create_transaction\` with explicit debit/credit lines if the user prefers; otherwise redirect. Exception: a VAT-registered (ALV) company books sales only as invoices (\`kirjapro_create_invoice\` + \`kirjapro_send_invoice\`) and reverse-charge purchases and purchases with deductible VAT in the web app (a free 2320 line on a voucher has no VAT fact and does not reach field 307); the database refuses its vouchers with a net credit on sales accounts 5000-5499, a sales-account line together with a 4210 or 2320 line, or a net 4210 credit; moving an amount out of a sales account without a VAT line (e.g. a fixed-asset disposal D 5010 / K 1600) is allowed
42772
42969
  - **Payroll (palkanlaskenta)** \u2014 Kirjapro does NOT calculate or book payroll. Direct the user to Palkkaus.fi or Palkka.fi for the calculation, then bring the resulting payslip back and book it via \`kirjapro_create_transaction\` with explicit debit/credit lines
42773
42970
  - **Transaction templates** \u2014 manual journal entries via \`kirjapro_create_transaction\`
42774
42971
  - **Manual offer status changes** \u2014 the customer's email-link click sets status server-side; just wait for the user to confirm acceptance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kirjapro/mcp-server",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "MCP server for Finnish accounting — exposes Kirjapro bookkeeping, invoicing, and reporting tools to Claude, ChatGPT, Mistral, and other MCP clients.",
5
5
  "type": "module",
6
6
  "bin": {
package/server.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@kirjapro/mcp-server",
3
3
  "displayName": "Kirjapro - Finnish Accounting",
4
4
  "description": "Finnish double-entry bookkeeping, invoicing, and financial reporting for growing businesses. Create transactions, generate invoices with VAT calculation, produce income statements and balance sheets, and validate Finnish business identifiers.",
5
- "version": "0.10.0",
5
+ "version": "0.11.0",
6
6
  "homepage": "https://kirjapro.fi",
7
7
  "repository": "https://github.com/Jaollo/kirja-pro",
8
8
  "license": "MIT",
@@ -2,7 +2,7 @@
2
2
  "fingerprints": [
3
3
  {
4
4
  "name": "kirjapro_book_purchase",
5
- "hash": "ec096990916f5112"
5
+ "hash": "e6c7bfe42eedc619"
6
6
  },
7
7
  {
8
8
  "name": "kirjapro_book_sale",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  {
24
24
  "name": "kirjapro_create_from_template",
25
- "hash": "9e774f4f25c6ce93"
25
+ "hash": "d410e01e95e20227"
26
26
  },
27
27
  {
28
28
  "name": "kirjapro_create_from_template_batch",
29
- "hash": "415d1d219c81467f"
29
+ "hash": "ff8fa037077fac9a"
30
30
  },
31
31
  {
32
32
  "name": "kirjapro_create_invoice",
@@ -42,11 +42,11 @@
42
42
  },
43
43
  {
44
44
  "name": "kirjapro_create_transaction",
45
- "hash": "bea9ccf179928813"
45
+ "hash": "8b6a32983de69413"
46
46
  },
47
47
  {
48
48
  "name": "kirjapro_create_transactions_batch",
49
- "hash": "160de9628d56e6be"
49
+ "hash": "a3e71d41bbf437e4"
50
50
  },
51
51
  {
52
52
  "name": "kirjapro_delete_invoice",
@@ -66,7 +66,7 @@
66
66
  },
67
67
  {
68
68
  "name": "kirjapro_get_alv_summary",
69
- "hash": "1c225c5d61c0839f"
69
+ "hash": "a149fdcb6307923a"
70
70
  },
71
71
  {
72
72
  "name": "kirjapro_get_ar_aging",
@@ -162,7 +162,7 @@
162
162
  },
163
163
  {
164
164
  "name": "kirjapro_list_templates",
165
- "hash": "7601b5d1da0d7283"
165
+ "hash": "91c70a7143aa96ac"
166
166
  },
167
167
  {
168
168
  "name": "kirjapro_list_transactions",