Skip to content

Tally import readiness review

  • Date: 2026-10-21
  • Scope: review only. The importer was not changed; two desktop items found here were fixed alongside the backup work (the import command is now async, and it asks and backs up before importing into a company with books). This looks at what khata-import-tally and the desktop import path do with a real TallyPrime export, before the maintainer imports real company data.
  • Read: crates/khata-import-tally/src/* (lib, parse, apply, text, request, http), its tests and fixtures, apps/desktop/src-tauri/src/export.rs (import_tally), commands.rs (import_tally), phone_files.rs (local_input, read_bytes), apps/desktop/src/screens/company.ts (importFromTally), reports.ts (ImportReportScreen), and khata-core (book.rs, voucher.rs, chart.rs, inventory.rs).
  • How it was checked: reading the code, running cargo test -p khata-import-tally (38 tests pass), and a throwaway program outside the repo. That program fed the importer hand-made Tally-shaped XML for each edge case, and timed synthetic Day Books of 100,000 and 300,000 vouchers. Behaviour marked "verified" was seen in that program. Behaviour marked "not verified" is read from the code, or is an assumption about what Tally writes.
  • Caveat: every fixture in crates/khata-import-tally/tests/fixtures is hand-written ("Hand-written sample in the shape of a TallyPrime … file"). No real TallyPrime export has been run through the importer, so the exact tag layout Tally writes is itself unverified.

Status after the fixes (2026-10-21)

The top five findings and several lower ones were fixed after this review. Rows in the ranked table carry their status in bold. All of it is tested on invented TallyPrime-shaped fixtures (crates/khata-import-tally/tests/migration.rs and crates/khata-core/tests/migration.rs); none of it has met a real TallyPrime export yet.

Rank Status What changed
1 Fixed Voucher.series (core) keeps a custom voucher type's name as its own number series, and the duplicate check is per type, series and period. When Tally's numbers repeat across financial years and the company's numbering never restarts, the importer makes it restart yearly, with a note. A number Tally repeated within one series and year is posted as "12 (2)", with a note. A numbered voucher already in the company from before is skipped as "already in the company". Nothing is skipped silently.
2 Fixed Book::group_is_cash_or_bank counts Bank OD A/c and Bank OCC A/c (and groups under them) as cash or bank for Contra, payment and receipt accounts, bank dates and bank reconciliation. Their stored flag and nature are unchanged, so existing company files replay as before. Other groups are unchanged (tested).
3 Fixed Voucher BILLALLOCATIONS.LIST (New Ref, Agst Ref, Advance, On Account) become Entry.bills, with due dates from BILLCREDITPERIOD (days or a date). Opening bills on ledger masters become Ledger.opening_bills (new in core, gap M5), which outstanding_bills and ageing use; any part of the opening balance they don't cover shows as "Opening Balance". Lines whose bills can't be kept (mixed debits and credits, unknown type, not adding up) post without them, with a note. There is no screen to edit opening bills by hand yet.
4 Fixed GSTIN (validated; a failing one is kept with a note and an override reason), state (to the GST state code), PAN, address, PIN code, phone, email, special economic zone flag and credit period on parties, from the direct tags and from LEDGSTREGDETAILS.LIST / LEDMAILINGDETAILS.LIST. GST duty ledgers get Ledger.tax (CGST, SGST, UTGST, IGST, cess) with the side from the name, else from use on sales or purchases; a ledger used on both is tagged input with a note. HSN/SAC on sales and service ledgers, and rates by HSN/SAC and date from GSTDETAILS.LIST, set in one change (Book::set_tax_rates). Registration type and GST classification masters are not kept; both are reported.
5 Fixed The summary gives the first and last voucher dates and warns when the Day Book starts more than 31 days after its financial year begins, when there are no ledger masters or no opening balances, and when opening balances don't add up. Closing balances are compared with Tally's when the files carry them: a Trial Balance exported as XML (ledger or group rows, DSPACCNAME / DSPACCINFO), or CLOSINGBALANCE on ledger masters. Each difference is listed. The Trial Balance XML layout is read from Tally's public report format and is not verified on a real export.
6 Reported Lines with cost centre allocations are counted in one note. Cost centres themselves wait for M9.
7 Fixed The import summary screen shows warnings first, counts, vouchers by Tally voucher type, bills, GST and party details, checks (dates, opening difference, closing balance comparison), notes and skips. It saves as CSV or text, and as a shareable summary with counts only, no names, amounts, dates or file names.
8 Partly fixed BANKERSDATE becomes Entry.bank_date on cash and bank ledgers (dropped, and counted, when before the voucher date). Instrument numbers are not kept.
9 Fixed Ledger, stock item and group lookups use the importer's maps first. A synthetic import of 10,000 ledgers and 100,000 vouchers took 8.2 s in a release build (tests/large.rs, ignored by default), against 54 s measured above.
11 Fixed Cancelled vouchers are posted as cancelled, keeping their numbers, with their lines when the export has them and without lines when Tally dropped them (core now accepts a cancelled voucher with no lines).
14 Partly fixed Numbered vouchers already in the company are skipped with a clear reason. Vouchers without a number are still posted again.
16 Partly fixed The desktop parses each file and drops its bytes before reading the next, so a large Day Book isn't held as raw bytes and parsed objects at once. Parsing still keeps every voucher before applying (no streaming apply yet).
18 Fixed with a note KhataOS has no post-dated state, so post-dated vouchers are posted on their date with a note: reports up to an earlier date leave them out, undated balances include them.
21 Reason fixed An item whose opening is in a compound unit now says so. Compound units are still not imported.
22 Fixed ACTION="Delete" and ISDELETED vouchers are skipped with the reason "marked as deleted".
24 Fixed Covered by rank 7: the opening difference is on the summary.
25 Fixed The desktop import restores the in-memory company if saving fails, so an unsaved import never lingers.

Still open: ranks 10 (custom primary groups), 12 (stock vouchers), 13 (custom types without their masters), 15 (supplier invoice number), 17 (payroll), 19 (rate in an alternate unit), 20 (foreign currency), 23 (group names that normalize alike), 26 (ASCII exports) and 27 (files from different companies).

Summary: the five findings most likely to bite

  1. Repeated voucher numbers drop real vouchers. KhataOS needs a number to be unique per base kind (Sales, Payment, …) and numbering period. Tally numbers each custom voucher type on its own ("Cash Sales 1", "Credit Sales 1"), and often restarts every year. Every clash after the first is skipped. The skip is reported, but the books then don't match Tally. Verified.
  2. Contra vouchers using a Bank OD or Bank OCC (cash credit) ledger are skipped. KhataOS marks Bank OD A/c and Bank OCC A/c as not cash or bank (chart.rs DEFAULT_GROUPS), so check_voucher rejects the Contra. Tally allows these, and traders with a CC limit use them daily. Verified.
  3. Bills, cost centres, bank dates, GST registration data and supplier references are dropped without a report line. Totals stay right. But outstandings and ageing are empty, cost centre reports are empty, and bank reconciliation must be redone. GSTINs and states are missing, and tax ledgers are not tagged, so KhataOS GST reports can't see imported tax lines. The guide lists most of this, but the result screen says nothing. Verified for bills, cost centres, GSTIN and reference.
  4. A partial Day Book gives silently wrong balances. Ledger opening balances in a Tally masters export are as at the books beginning date. If the Day Book covers only recent years, every closing balance is wrong, yet the trial balance still tallies. Nothing warns about it. Read from the code. Tally's opening semantics are not verified in a live Tally here.
  5. Large charts of accounts are slow, and the window freezes. Every voucher line looks its ledger up with a linear scan (Book::ledger_by_name, called first by resolve_ledger). The desktop command is a plain synchronous Tauri command, so the import runs on the main thread. Measured: 100,000 vouchers took 10.6 s with 3,000 ledgers and 54 s with 10,000 ledgers. Nothing is lost, but a user may kill a frozen app. Nothing is saved until the end, so killing it leaves the company unchanged.

What works well, verified in tests or the probe:

  • UTF-16 LE and BE, with or without BOM, and UTF-8 with or without BOM (text.rs decode).
  •  and other control character references are stripped before parsing (text.rs sanitize). 
 survives into narrations as CR LF.
  • & and the other predefined entities work. Unknown entities are dropped instead of failing (parse.rs predefined). CDATA is read.
  • Amounts use rust_decimal, never floats. These parse: Indian grouping (1,00,000.00), currency symbols, Dr/Cr prefixes and suffixes, parentheses, and the forex form … = -₹ 830.00. Quantities like 10.000 Nos and 10 Nos = 1 Box parse too.
  • Sign follows the sign of AMOUNT, not ISDEEMEDPOSITIVE. That is right for Tally XML, including negative value lines.
  • Masters and Day Book can come in any order and in any file, because import_all parses everything first and applies masters before vouchers. Groups, stock groups and godowns are created in dependency order.
  • A file that is not Tally XML, is truncated, or carries Tally STATUS 0 aborts before the book is touched.

Ranked findings

Ranked by how likely each is to bite on real data, then by severity. "Effect" uses three values:

  • fails loudly: an error, and nothing is imported;
  • skipped and reported: the object is left out, with a line on the result screen;
  • silently wrong: imported wrong or incomplete, with no line on the result screen.
Rank Issue What a real TallyPrime export contains What KhataOS does today Effect Likelihood Suggested fix Effort
1 Repeated voucher numbers (Fixed) Several custom types on one base type, each numbered from 1 ("Cash Sales", "Credit Sales", "Bank Payment", "Cash Payment"). Often a yearly restart. Manual numbers can repeat when Tally's duplicate check is off. Custom types fold into one VoucherKind (apply.rs voucher_kind). check_voucher (book.rs) rejects a number already used for that kind in the same series period. The default series never restarts. Verified: "Cash Sales 1" is skipped after "Sales GST 1", and "Sales GST 1" of the next year too, unless yearly numbering is set before import. Skipped and reported. The trial balance then differs from Tally. High On a clash, post with a type-qualified number (for example "Cash Sales/1") and add a report note. Until then, set yearly restart before import. M
2 Contra with Bank OD or Bank OCC ledgers (Fixed) Contra vouchers between a current account and a CC or OD account, under Tally's Bank OCC A/c or Bank OD A/c. DEFAULT_GROUPS in chart.rs gives Bank OD A/c and Bank OCC A/c is_cash_or_bank = false. check_voucher returns ContraNonCashLedger. Verified for both groups. Skipped and reported. Cash, bank and CC balances are then wrong. High for traders with a CC or OD limit Treat Bank OD A/c and Bank OCC A/c as cash or bank, as Tally does. Check the side effects on bank dates and reports. S
3 Bill-wise detail dropped (Fixed) BILLALLOCATIONS.LIST on party lines of nearly every sales, purchase, receipt and payment. Opening bills under BILLALLOCATIONS.LIST on ledger masters. entries_in (parse.rs) does not descend into nested lists. The ledger reader keeps only OPENINGBALANCE. The opening total is right (verified: ₹1,00,000 kept, its two bills dropped). Core has BillAllocation on Entry, but the importer never fills it. Opening bills on ledgers don't exist in core (gap M5). Silently incomplete. The guide mentions it, the result screen doesn't. Outstandings and ageing are empty. High Map voucher BILLALLOCATIONS.LIST (New Ref, Agst Ref, Advance, On Account) to Entry.bills, and report opening bills until M5 exists. M
4 GST master data and tax ledger tagging (Fixed) PARTYGSTIN, GSTREGISTRATIONTYPE, LEDSTATENAME and PLACEOFSUPPLY on parties. TAXTYPE GST with GSTDUTYHEAD on duty ledgers. GST rates in GSTDETAILS.LIST on items and ledgers. Only the item's HSN code is kept, as tax_class (parse.rs hsn_code, verified). Ledgers get no tax_id, state or tax. The desktop GST code finds tax lines through Ledger.tax (gst.rs around line 204). Imported CGST, SGST and IGST ledgers are not tagged, so GST reports over imported vouchers are likely empty or wrong (not verified end to end). Silently incomplete High for GST-registered companies Map PARTYGSTIN to tax_id, the state to the mailing state, and GSTDUTYHEAD with the ledger's use to LedgerTax. Report items whose GST rate was not kept. M
5 Opening balances against a partial Day Book (Fixed) Ledger OPENINGBALANCE is the balance at the books beginning date. Users often export only the latest year or two of the Day Book to keep files small. No check. The importer does not know the books beginning date and does not report the voucher date range. The trial balance still tallies, so the error is easy to miss. Read from the code. Tally's opening semantics are not verified in a live Tally here. Silently wrong Medium to High Show the first and last voucher dates in the result. Say in the guide that the Day Book must start at the books beginning date. S
6 Cost centre and cost category allocations dropped (Reported, not imported) CATEGORYALLOCATIONS.LIST and COSTCENTREALLOCATIONS.LIST on expense and income lines. COSTCENTRE and COSTCATEGORY masters. Nested lists are not read (parse.rs entries_in, test nested_allocations_are_not_ledger_lines). The masters are not in OBJECTS, so they are ignored. Verified: a payment with a cost centre posts with no report line. Core has no cost centres (gap M9). Silently incomplete. The guide mentions it, the result screen doesn't. Medium to High for mid-size companies Count dropped allocations by kind and show one note per kind now. Import them once M9 exists. S now, L later
7 Result screen has no reconciliation (Fixed) Not about Tally data. It is about spotting the other problems. ImportReportScreen (reports.ts) shows counts and a skip list only. No opening difference, no trial balance totals, no voucher date range, no count of dropped allocations. The report lives only on screen. Makes silently wrong data harder to spot Medium Add opening difference, trial balance totals, date range and dropped-detail counts to ImportSummary. Allow printing or CSV export. S
8 Bank allocation detail dropped (Partly fixed) BANKALLOCATIONS.LIST with instrument number, instrument date and BANKERSDATE (the reconciled date). Not read. Core has Entry.bank_date, but the importer never sets it. Silently incomplete. Bank reconciliation must be redone. Medium Map BANKERSDATE to bank_date. Put the instrument number in the narration. S
9 Slow on large charts, and the UI freezes (Fixed) A mid-size trader can have 5,000 to 10,000 ledgers and 100,000+ vouchers. resolve_ledger (apply.rs) calls Book::ledger_by_name (book.rs, a linear scan) before its own hash map. Measured on synthetic data: 100,000 vouchers took 10.6 s with 3,000 ledgers and 54 s with 10,000. 300,000 vouchers with 3,000 ledgers took 17.4 s, plus 2.8 s to save. The desktop import_tally command was a plain fn on the main thread; it is now async and runs in spawn_blocking (fixed with the backup work), so the window keeps painting, but it still holds the company lock throughout. No data loss. Slow, but the window no longer looks hung. Medium Look in the importer's map first, and make the command async or use spawn_blocking. S
10 Custom primary groups cascade Groups created directly under Primary, for example "Partners Funds". Skipped as CustomPrimaryGroup (apply.rs groups). Every ledger under it is skipped as unknown parent. Every voucher touching those ledgers is skipped as unknown ledger, including its cash or bank side. Verified. Skipped and reported, but one master can remove hundreds of vouchers Medium File such a group under the default primary group of the same nature (from Tally's ISREVENUE, ISDEEMEDPOSITIVE and AFFECTSGROSSPROFIT), with a note. S to M
11 Cancelled vouchers skipped (Fixed) ISCANCELLED Yes. Tally keeps the number, and GSTR-1 Table 13 counts cancelled invoices. Skipped (build_voucher). Core supports cancelled vouchers that keep their number (cancel_voucher, Voucher.cancelled). Skipped and reported. Invoice series get gaps. Medium Post them with cancelled: true. S
12 Stock vouchers skipped Stock Journal, Manufacturing Journal, Physical Stock, Delivery and Receipt Notes, Rejections. Skipped as InventoryVoucherType. Core already has VoucherKind::StockJournal with Voucher.stock moves. Skipped and reported. Closing stock quantities differ from Tally. Medium for manufacturers Import Stock Journal and Manufacturing Journal as StockJournal. M
13 Voucher type masters missing Masters exported without voucher types, or masters and Day Book imported in two separate runs. Custom types are resolved only through VOUCHERTYPE masters. There is no guess from the name (voucher_kind). Unknown types are skipped. Verified by the existing test separate_imports_lose_voucher_types_and_aliases. Skipped and reported Medium Fall back to a base type whose name the custom name starts or ends with, with a note. S
14 Import into a company that already has data, or importing twice (Partly fixed) Users retry after fixing things in Tally. Existing ledgers are kept and Tally's opening balance is not applied. The skip says only "already exists". Numbered vouchers are all skipped as duplicates. Vouchers without a number are posted again under new numbers (verified). The command now takes an automatic backup first when the company already has entries (backup::import_tally calls backup::before_risky). The app now also asks before importing into a company that has ledgers or vouchers, and suggests a new company. Data → Restore is the undo. Partly silently wrong (duplicate unnumbered vouchers), partly reported Medium Warn and ask before importing into a company that has vouchers. Say in the duplicate-ledger reason that the opening balance was not applied. S
15 Supplier invoice number and party details dropped REFERENCE and REFERENCEDATE (the supplier's bill number), PARTYLEDGERNAME, buyer address, e-way bill and IRN fields. Not read. Voucher has no reference field. Verified: REFERENCE is lost. Silently incomplete. Purchase registers and GSTR-2B matching lose the supplier's number. Medium Append "Ref: …" to the narration. S
16 Memory and file size (Partly fixed) A 5-year Day Book in UTF-16 can be several hundred MB to over 1 GB. Whole-file reads (export.rs std::fs::read). The raw bytes are held until the import ends, then copied by decode, then by sanitize. Parsing streams with quick-xml, but every voucher is kept in TallyData before applying. The book then holds each voucher twice: as a struct and as JSON in the audit log. Measured peak memory: 0.86 GB for a 292 MB file (100,000 vouchers) and 1.93 GB for an 877 MB file (300,000 vouchers). The 200 MB cap applies only to phone URIs (phone_files.rs read_bytes). On desktop local_input passes the path through, so there is no cap. Fails loudly over 200 MB on a phone. Fine on a desktop with enough RAM. Low to Medium Drop the raw bytes after parsing each file, and parse from a buffered reader. M
17 Payroll vouchers skipped Payroll vouchers post salary expense and payables to the books. "Payroll" is in UNSUPPORTED_TYPES and is skipped like Attendance, which really is non-accounting. Skipped and reported. The trial balance differs. Low to Medium (payroll users only) Import Payroll as a Journal using its ledger lines. S
18 Post-dated vouchers posted as regular (Fixed with a note) ISPOSTDATED Yes, for example post-dated cheques. Tally keeps them out of the books until their date. ISPOSTDATED is not read. They are posted, and Book::balances(None) counts them. Verified. Silently wrong for reports run before the cheque date Low to Medium Skip them with a report line, or post them and add a note. S
19 Rate in an alternate unit RATE 5,000.00/Box on a line whose ACTUALQTY is "10 Nos = 1 Box". The quantity is the base figure (10 Nos), but tally_rate keeps 5,000 as the rate. Verified: a line of qty 10, rate 5,000, amount 5,000. Amounts and valuation use amount, so totals are right. The rate shows on reprints and in last_rate suggestions. Silently wrong rate Low to Medium When the rate's unit differs from the quantity's unit, use amount divided by quantity. S
20 Foreign currency … @ ₹ 83.00/$ = … amounts on export and import parties. Only the base amount is kept. Core has Entry.forex and foreign-currency ledgers, which are not filled. Silently incomplete (the guide mentions it) Low to Medium Fill Entry.forex and the ledger currency from the forex form. M
21 Compound units (Reason fixed) "Box of 10 Nos" units, and items using them. The unit is skipped, and its items are skipped. Verified: an item whose opening is "5 Box of 10 Nos" is reported as amount "5 Box of 10 Nos" is not a number. That message is misleading, because the problem is the compound unit. Skipped and reported Low to Medium Check the item's unit before parsing its opening quantity, so the reason says "compound unit". S
22 Deleted vouchers posted (Fixed) ACTION="Delete" or ISDELETED Yes (rare in a Day Book export; more likely in sync-style exports). The ACTION attribute and ISDELETED are not read. Verified: both are posted. Silently wrong Low Skip them with a report line. S
23 Group names that normalize alike Two custom groups like "Debtors-North" and "Debtors North". The importer's group map is keyed by normalize (letters and digits only, apply.rs remember_group). Ledgers under the second group resolve to the first. Verified. Silently wrong group Low Key custom groups by lower-cased name, and keep normalize only for predefined names. S
24 Opening stock that can't be kept (Fixed) Items with negative opening quantity, compound units or an unknown stock group. Reported item by item. The effect on the opening difference is not shown. Book::opening_difference includes opening stock value. Skipped and reported Low to Medium Covered by rank 7: show the opening difference. S
25 Save fails after the book was changed (Fixed) Disk full, or the store diverged. export.rs import_tally changes the in-memory book and then saves once. If the save fails, the error is shown, but the in-memory book keeps the import. The next successful save from any other action writes it to disk. Confusing. The user is told it failed, and it may persist later. Low On save failure, reload the company from disk. S
26 "ASCII" export with non-English names The ASCII option, with Hindi, Gujarati or ₹ in names (what Tally writes there is not verified). Bytes that aren't valid UTF-8 become U+FFFD (String::from_utf8_lossy), without warning. Masters and vouchers are mangled the same way, so links still hold. Silently mangled names Low Report when replacement characters appear. S
27 Files from different companies Masters from one company and a Day Book from another, or a multi-company HTTP export. SVCURRENTCOMPANY and COMPANY objects are not read, and the files are not cross-checked. Skipped and reported, as many unknown ledgers Low Read the company name from each file and refuse a mismatch. S

Checked and found fine on the points the brief asked about:

  • Optional vouchers are skipped. Tally keeps them out of the books too.
  • Memorandum and Reversing Journal vouchers are skipped. They don't affect Tally's books either.
  • Invoice view and voucher view both work: read_voucher uses whichever of ALLLEDGERENTRIES.LIST, LEDGERENTRIES.LIST plus inventory allocations, or both, balances.
  • Unbalanced vouchers are rejected by Voucher::validate and reported.
  • Voucher dates outside the financial year are accepted. There is no date check.
  • Long narrations have no length limit.
  • Aliases from LANGUAGENAME.LIST / NAME.LIST are kept. They never shadow a real ledger name.
  • Ledgers that differ only in case can't occur in Tally, and lookups ignore ASCII case.
  • Unknown ledgers in vouchers are reported.

Before importing real data

Superseded by the checklist in Import from Tally, which reflects the fixes above. Kept as written at review time.

  1. Start fresh. Create a new company in KhataOS. Set the country to India and the financial year to start in April.
  2. Set numbering first. If Tally restarts numbering each year, set every voucher type's numbering to restart yearly before the import (Settings, voucher numbering). Verified: this stops next-year duplicates. It does not help two custom types of the same base type that share numbers in one year.
  3. Write down Tally's figures for the last date: trial balance totals, the opening difference, the closing balances of cash, every bank and CC account and the largest parties, and voucher counts by type (Tally's Statistics report).
  4. Export All Masters, including voucher types, as XML.
  5. Export the Day Book from the books beginning date to the last date, not just recent years. Unicode (UTF-16) or UTF-8 both work. Avoid the ASCII option if any name uses Indian scripts.
  6. Import both files in one go. Select both in the file dialog. Use the desktop app, not a phone: phones refuse files over 200 MB.
  7. Expect a pause. On a large file the window may look frozen for a minute or more. Don't force quit. If you do, nothing is saved.
  8. Read the whole skip list, grouped by reason. Look for Contra vouchers on CC or OD accounts, duplicate numbers, unknown voucher types and custom primary groups. These change balances.
  9. Compare with Tally: the trial balance totals, the "Difference in opening balances" row (the trial balance and balance sheet show it when it isn't zero), and the cash, bank, CC and party balances you wrote down.
  10. Know what is missing even when nothing is skipped: bills (outstandings and ageing start empty), cost centres, GSTINs and states, GST tagging on CGST, SGST and IGST ledgers, bank reconciliation dates, supplier bill numbers and foreign amounts. Before running any GST report, set the tax component on each duty ledger.
  11. Don't import the same files twice into one company. To retry, delete the test company and start again. Keep Tally's own data folder backed up and untouched.

Trivial fixes I'd suggest (not applied)

Since applied: 1, 3, 4, 5, 8, 9, 11 and 12. Not yet: 6, 7 and 10.

  1. In apply.rs resolve_ledger, look in self.ledgers first and fall back to Book::ledger_by_name. The map already holds every imported and reused ledger name, so the result is the same and the linear scan goes away.
  2. ~~Make the desktop import_tally command async, or run the work in spawn_blocking, so the window stays responsive.~~ Done with the backup work.
  3. In parse.rs read_voucher, skip vouchers with ACTION="Delete" or ISDELETED Yes, and report ISPOSTDATED Yes, each with a new SkipReason.
  4. In parse.rs read_stock_item, check for a compound unit before parsing OPENINGBALANCE, so the reason is "compound unit", not "not a number".
  5. In apply.rs build_voucher, post cancelled vouchers with cancelled: true instead of skipping them.
  6. In apply.rs remember_group, key custom group names by lower case instead of normalize.
  7. Append REFERENCE (and REFERENCEDATE) to the narration, for example "Ref: INV-0912 dt 03-04-2024".
  8. Map BANKERSDATE in BANKALLOCATIONS.LIST to Entry.bank_date when the ledger is cash or bank.
  9. Add to ImportReport and ImportSummary: the first and last voucher dates, Book::opening_difference(), trial balance totals, and counts of dropped bill, cost centre and bank allocations. Show them on ImportReportScreen.
  10. Change the DuplicateLedger reason to say the Tally opening balance was not applied.
  11. In export.rs import_tally, parse each file and drop its bytes before parsing the next. Today all raw files stay in memory until the import ends.
  12. In docs/guide/import-from-tally.md, say the Day Book must start at the books beginning date. Also say to set yearly numbering before the import when Tally restarts numbers each year, and mention the Contra limit for CC and OD accounts until rank 2 is fixed.