fix(bookkeeping): name 1249/1259/1269 after their BAS 2026 free heads, drop retired 12xx sub-accounts (#2419)
* fix(bookkeeping): name 1249/1259/1269 after their BAS 2026 free heads, drop retired 12xx sub-accounts A sole trader booking a tractor activated 1240 and 1249 from the account picker and got a machinery head labelled "(Fritt konto för Maskiner och andra tekniska anläggningar)" next to a contra account labelled "Ack. avskrivningar på bilar och andra transportmedel". Why it occurred: BAS 2026 restructured kontogrupp 12. Bilar and datorer moved under 1210 (för produktion) and 1220 (ej för produktion), and 1230/1240/1250/1260 became free heads. The catalog in lib/bookkeeping/bas-data/ followed for the heads (#463) but kept seven sub-accounts the official chart no longer has (1241, 1242, 1249, 1251, 1259, 1261, 1269) with their pre-2026 names. Every picker activation of a 12xx contra account therefore produced the contradiction; prod carries the 1240/1249 pair in 173 charts, 1250/1259 in 153 and 1260/1269 in 78. What was removed instead of patched: 1241, 1242, 1251 and 1261 leave the catalog entirely (bas.se BAS 2026 v2 has no such accounts; the SIE mapper already self-maps unknown sub-accounts by number). 1249/1259/1269 stay because the asset module's vehicle and computer defaults and 31 live assets in prod depend on them; they are renamed after their heads so the pair reads as one thing. Why this and not the proposal: the reporter asked for 1249 to be renamed to "ack. avskr. maskiner", which fixes one number and leaves 1259/1269 and the four retired asset accounts contradicting their heads. Dropping 1249/1259/1269 and moving the asset defaults to BAS 2026 (1226/1224 on 1229) is the right long-term shape but changes what a new vehicle or computer asset books to; that decision is the founder's and is tracked in #2414. The migration renames a contra account only when its name is byte-identical to one of the two catalog literals AND the company's head carries the BAS 2026 free label, so old-BAS imports (1240 "Bilar och andra transportmedel") and every user rename stay untouched. Applied and pg-tested on staging. Fixes #2413 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NXSuVejFCvRDyNXF1otEPd * fix(bookkeeping): skip 12xx contra accounts with journal lines in the label backfill Skeptic refutation: lib/import/account-sync.ts creates missing accounts with the catalog name when the SIE #KONTO names are not carried, so an old-BAS vehicle chart can hold the exact free-head + bilar-contra pair with years of depreciation booked on 1249 (8 such charts in prod). The backfill now also requires that the contra account has no journal lines: a label with history is the user's to change. Migration re-issued under a fresh version, applied and pg-tested on staging. Refs #2413 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NXSuVejFCvRDyNXF1otEPd * fix(bookkeeping): lock journal_entry_lines while the 12xx label backfill checks history CodeRabbit (Major): under READ COMMITTED a posting could commit between the NOT EXISTS history check and the rename. A SHARE lock on journal_entry_lines for the migration transaction makes the two atomic; inserts wait milliseconds, reads are unaffected. Migration re-issued under a fresh version, applied and pg-tested on staging. Refs #2413 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NXSuVejFCvRDyNXF1otEPd --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -1662,3 +1662,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-09-08] #2391 skeptic pass: orgNumberKey only strips hyphens and spaces and only unprefixes 12-digit values behind 16/18/19/20. Reason: 26 prod supplier rows hold a VAT number (orgnr + 01, prefixes 55/52/87) in org_number, and 'last 10 of any 12 digits' would have rewritten them to another company's identity; letters stay because BE0123456789 is not the Swedish 0123456789. The matcher scans live suppliers only (archived_at IS NULL), the list and v1 search compare without separators, the CSV import and the provider migration orchestrator key and write through the same rule.
|
||||
[2026-09-08] correctEntry re-points the original entry's transaction_voucher_links rows to the corrected entry (lib/core/bookkeeping/storno-service.ts relinkTransactionsToEntry) instead of deleting them as issue #2364 proposed. Why: for a samlingsverifikat (bulk-book N>1) the junction is the row's only anchor, so deleting it would push rows the corrected verifikat still explains back into Att bokföra; the pointer column already follows the correction and the junction now follows it the same way, so every reader (is_transaction_booked, fetchJunctionLinkedTxIds, the bulk_book RPC) sees one live anchor. Rejected: a relink_entry_anchors RPC moving pointer and junction atomically (a migration plus pg test for a path that is already best-effort across five other statements; revisit if a partial failure ever shows up in the surfaced transactionRelinkError). Prod repair (planned, runs after merge on the founder's go; completion gets its own dated entry): the 7 stale links (3 companies) all sit on rows whose pointer names a posted entry (4 on a correction chain, 3 from a June 2026 samlingsverifikat storno that predates the junction cleanup and were re-booked 1:1); they will be re-pointed to the pointer's entry, the same rule the fix applies, rather than deleted.
|
||||
[2026-09-08] delete_last_voucher returns a correction's bank anchors (transactions.journal_entry_id and transaction_voucher_links rows) to correction_of_id before the row is deleted (migration 20260908095907). Why: the #2364 skeptic showed that once the junction follows the correction, the two-step undo (delete the correction, then the storno) cascaded the links away and restored an original that explains bank rows nobody points at, so the rows surfaced as bookable again; before, the links had stayed on the original by accident. Chosen over releasing the rows (the restored original would still explain them, same trap) and over a TS pre-step in the DELETE route (not atomic with the RPC's own guards: a refused delete would leave anchors on a reversed entry). A duplicate of a link the original already holds is dropped, not re-pointed (UNIQUE (transaction_id, journal_entry_id)).
|
||||
[2026-09-08] Issue #2413 BAS 2026 kontogrupp 12: kept 1249/1259/1269 in the catalog renamed after their free heads and dropped only 1241/1242/1251/1261, instead of removing all seven retired sub-accounts and moving the asset module's vehicle/computer defaults to BAS 2026 (1226/1224 on 1229): the asset module's DEFAULT_ACCOUNTS_BY_CATEGORY still books vehicles on 1240/1249 and computers on 1250/1259 (31 live assets in prod, guard test requires the triple in BAS_REFERENCE), so dropping the contra accounts would have forced a depreciation-default change into a label fix; that change is the founder's call and lives in #2414. The prod backfill renames only the exact catalog literal next to a free-labelled head, so old-BAS imports (1240 Bilar + 1249 Ack. avskr. bilar) and user renames stay untouched.
|
||||
|
||||
@@ -82,6 +82,38 @@ describe('Non-standard accounts removed', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// BAS 2026 restructured kontogrupp 12: bilar/datorer moved under 1210 (för
|
||||
// produktion) and 1220 (ej för produktion), and 1230/1240/1250/1260 became
|
||||
// free heads. The pre-2026 sub-accounts under those heads are gone from the
|
||||
// official chart (bas.se BAS 2026 v2), so the catalog must not hand them out
|
||||
// with their old names next to a head that says "fritt konto" (#2413).
|
||||
describe('BAS 2026 kontogrupp 12: free heads and their contra accounts agree', () => {
|
||||
const retired = ['1241', '1242', '1251', '1261']
|
||||
for (const num of retired) {
|
||||
it(`${num} (retired in BAS 2026) is not in the catalog`, () => {
|
||||
expect(isStandardBASAccount(num)).toBe(false)
|
||||
})
|
||||
}
|
||||
|
||||
const pairs: Array<[head: string, contra: string]> = [
|
||||
['1240', '1249'],
|
||||
['1250', '1259'],
|
||||
['1260', '1269'],
|
||||
]
|
||||
for (const [head, contra] of pairs) {
|
||||
it(`${contra} is named after the free head ${head}, not a retired bilar/datorer account`, () => {
|
||||
const headName = getBASReference(head)?.account_name ?? ''
|
||||
const contraName = getBASReference(contra)?.account_name ?? ''
|
||||
expect(headName.startsWith('(Fritt konto för ')).toBe(true)
|
||||
// "(Fritt konto för X)" on the head; "... (fritt konto för X)" on the contra.
|
||||
const subject = headName.slice('(Fritt konto för '.length, -1)
|
||||
expect(contraName).toContain(`(fritt konto för ${subject})`)
|
||||
expect(contraName).toMatch(/^Ackumulerade avskrivningar/)
|
||||
expect(contraName.toLowerCase()).not.toMatch(/bilar|datorer|inventarier och verktyg$/)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('Class 2 account_type correctness', () => {
|
||||
it('20xx accounts are equity', () => {
|
||||
const group20 = BAS_REFERENCE.filter((a) => a.account_group === '20')
|
||||
|
||||
@@ -11,109 +11,109 @@ export const BAS_ACCOUNT_NUMBERS: readonly string[] = [
|
||||
'1081', '1088', '1090', '1092', '1098', '1099', '1110', '1111', '1112', '1118', '1119', '1120',
|
||||
'1129', '1130', '1140', '1150', '1158', '1159', '1180', '1181', '1188', '1210', '1211', '1212',
|
||||
'1214', '1216', '1217', '1218', '1219', '1220', '1221', '1222', '1224', '1226', '1227', '1228',
|
||||
'1229', '1230', '1240', '1241', '1242', '1249', '1250', '1251', '1259', '1260', '1261', '1269',
|
||||
'1280', '1281', '1288', '1290', '1291', '1292', '1298', '1299', '1310', '1311', '1312', '1313',
|
||||
'1314', '1316', '1317', '1318', '1320', '1321', '1322', '1323', '1328', '1330', '1331', '1332',
|
||||
'1333', '1334', '1336', '1337', '1340', '1341', '1342', '1343', '1344', '1346', '1347', '1350',
|
||||
'1351', '1352', '1353', '1354', '1356', '1357', '1358', '1360', '1369', '1370', '1380', '1381',
|
||||
'1382', '1383', '1384', '1385', '1387', '1388', '1389', '1410', '1419', '1420', '1429', '1440',
|
||||
'1449', '1450', '1459', '1460', '1465', '1466', '1467', '1469', '1470', '1471', '1478', '1479',
|
||||
'1480', '1481', '1489', '1490', '1491', '1492', '1493', '1510', '1511', '1512', '1513', '1516',
|
||||
'1518', '1519', '1520', '1525', '1529', '1530', '1531', '1532', '1536', '1539', '1550', '1560',
|
||||
'1561', '1562', '1563', '1568', '1569', '1570', '1571', '1572', '1573', '1610', '1611', '1612',
|
||||
'1613', '1614', '1619', '1620', '1630', '1640', '1650', '1660', '1661', '1662', '1663', '1670',
|
||||
'1671', '1672', '1673', '1680', '1681', '1682', '1683', '1684', '1685', '1686', '1687', '1688',
|
||||
'1689', '1690', '1710', '1720', '1730', '1740', '1750', '1760', '1770', '1780', '1790', '1810',
|
||||
'1820', '1830', '1860', '1880', '1886', '1889', '1890', '1910', '1911', '1912', '1913', '1920',
|
||||
'1930', '1940', '1950', '1960', '1970', '1972', '1973', '1974', '1979', '1980', '1990', '2010',
|
||||
'2011', '2013', '2017', '2018', '2019', '2020', '2021', '2023', '2027', '2028', '2029', '2030',
|
||||
'2031', '2033', '2037', '2038', '2039', '2040', '2041', '2043', '2047', '2048', '2049', '2050',
|
||||
'2060', '2061', '2064', '2065', '2066', '2067', '2068', '2069', '2070', '2071', '2072', '2080',
|
||||
'2081', '2082', '2083', '2084', '2085', '2086', '2087', '2088', '2089', '2090', '2091', '2092',
|
||||
'2093', '2094', '2095', '2096', '2097', '2098', '2099', '2110', '2120', '2121', '2122', '2123',
|
||||
'2124', '2125', '2126', '2127', '2129', '2130', '2131', '2132', '2133', '2134', '2135', '2136',
|
||||
'2137', '2139', '2150', '2151', '2152', '2153', '2160', '2161', '2162', '2164', '2190', '2196',
|
||||
'2199', '2210', '2220', '2230', '2240', '2250', '2252', '2253', '2290', '2310', '2320', '2321',
|
||||
'2322', '2323', '2324', '2330', '2340', '2350', '2351', '2355', '2359', '2360', '2361', '2362',
|
||||
'2363', '2370', '2371', '2372', '2373', '2390', '2391', '2392', '2393', '2394', '2395', '2396',
|
||||
'2397', '2399', '2410', '2411', '2412', '2417', '2419', '2420', '2421', '2429', '2430', '2431',
|
||||
'2438', '2439', '2440', '2441', '2443', '2445', '2448', '2450', '2460', '2461', '2462', '2463',
|
||||
'2470', '2471', '2472', '2473', '2480', '2490', '2491', '2492', '2499', '2510', '2512', '2513',
|
||||
'2514', '2515', '2517', '2518', '2610', '2611', '2612', '2613', '2614', '2615', '2616', '2618',
|
||||
'2620', '2621', '2622', '2623', '2624', '2625', '2626', '2628', '2630', '2631', '2632', '2633',
|
||||
'2634', '2635', '2636', '2638', '2640', '2641', '2642', '2645', '2646', '2647', '2648', '2649',
|
||||
'2650', '2660', '2670', '2710', '2730', '2731', '2732', '2740', '2750', '2760', '2761', '2762',
|
||||
'2790', '2791', '2792', '2793', '2794', '2795', '2799', '2810', '2811', '2812', '2820', '2821',
|
||||
'2822', '2823', '2829', '2830', '2840', '2841', '2849', '2850', '2852', '2860', '2861', '2862',
|
||||
'2863', '2870', '2871', '2872', '2873', '2880', '2890', '2891', '2892', '2893', '2895', '2897',
|
||||
'2898', '2899', '2910', '2911', '2912', '2919', '2920', '2930', '2931', '2940', '2941', '2942',
|
||||
'2943', '2944', '2950', '2951', '2959', '2960', '2970', '2971', '2972', '2979', '2980', '2990',
|
||||
'2991', '2992', '2993', '2995', '2998', '2999', '3000', '3001', '3002', '3003', '3004', '3100',
|
||||
'3105', '3106', '3108', '3200', '3211', '3212', '3231', '3300', '3305', '3308', '3400', '3401',
|
||||
'3402', '3403', '3404', '3500', '3510', '3511', '3518', '3520', '3521', '3522', '3530', '3540',
|
||||
'3541', '3542', '3550', '3560', '3561', '3562', '3563', '3570', '3590', '3600', '3610', '3611',
|
||||
'3612', '3613', '3619', '3620', '3630', '3670', '3671', '3672', '3679', '3680', '3690', '3700',
|
||||
'3710', '3730', '3731', '3732', '3740', '3750', '3751', '3752', '3790', '3800', '3840', '3850',
|
||||
'3870', '3900', '3910', '3911', '3912', '3913', '3914', '3920', '3921', '3922', '3925', '3940',
|
||||
'3950', '3960', '3970', '3971', '3972', '3973', '3980', '3981', '3985', '3987', '3988', '3989',
|
||||
'3990', '3991', '3992', '3993', '3994', '3995', '3996', '3997', '3998', '3999', '4000', '4010',
|
||||
'4060', '4065', '4066', '4067', '4070', '4075', '4076', '4077', '4078', '4080', '4085', '4086',
|
||||
'4087', '4090', '4091', '4092', '4099', '4200', '4210', '4211', '4212', '4300', '4310', '4400',
|
||||
'4410', '4415', '4416', '4417', '4420', '4425', '4426', '4427', '4500', '4510', '4515', '4516',
|
||||
'4517', '4518', '4530', '4531', '4532', '4533', '4535', '4536', '4537', '4538', '4540', '4545',
|
||||
'4546', '4547', '4598', '4600', '4610', '4670', '4700', '4730', '4731', '4732', '4739', '4800',
|
||||
'4810', '4820', '4830', '4840', '4890', '4900', '4910', '4920', '4940', '4944', '4945', '4947',
|
||||
'4950', '4960', '4970', '4974', '4975', '4977', '4980', '4981', '4987', '4988', '5000', '5010',
|
||||
'5011', '5012', '5013', '5019', '5020', '5030', '5040', '5050', '5060', '5061', '5062', '5064',
|
||||
'5065', '5069', '5070', '5090', '5100', '5110', '5120', '5130', '5131', '5132', '5139', '5140',
|
||||
'5160', '5161', '5162', '5164', '5165', '5169', '5170', '5190', '5191', '5192', '5193', '5198',
|
||||
'5200', '5210', '5220', '5250', '5290', '5300', '5310', '5320', '5330', '5340', '5350', '5360',
|
||||
'5370', '5380', '5390', '5400', '5410', '5411', '5412', '5420', '5430', '5440', '5460', '5480',
|
||||
'5500', '5510', '5520', '5530', '5550', '5580', '5590', '5600', '5610', '5611', '5612', '5613',
|
||||
'5615', '5616', '5619', '5620', '5621', '5622', '5623', '5625', '5626', '5629', '5630', '5631',
|
||||
'5632', '5633', '5635', '5639', '5640', '5641', '5642', '5643', '5645', '5646', '5649', '5650',
|
||||
'5651', '5652', '5653', '5655', '5656', '5659', '5670', '5671', '5672', '5673', '5675', '5679',
|
||||
'5680', '5681', '5682', '5683', '5685', '5689', '5690', '5691', '5692', '5693', '5695', '5696',
|
||||
'5699', '5700', '5710', '5711', '5712', '5720', '5721', '5722', '5729', '5730', '5790', '5800',
|
||||
'5810', '5820', '5830', '5831', '5832', '5890', '5900', '5910', '5920', '5930', '5940', '5950',
|
||||
'5960', '5970', '5980', '5981', '5982', '5990', '6000', '6010', '6020', '6030', '6040', '6050',
|
||||
'6055', '6059', '6060', '6061', '6062', '6063', '6064', '6069', '6070', '6071', '6072', '6080',
|
||||
'6090', '6100', '6110', '6150', '6200', '6210', '6211', '6212', '6219', '6230', '6250', '6290',
|
||||
'6300', '6310', '6320', '6330', '6340', '6341', '6342', '6350', '6351', '6352', '6360', '6361',
|
||||
'6362', '6370', '6380', '6390', '6391', '6392', '6400', '6420', '6421', '6422', '6423', '6424',
|
||||
'6430', '6440', '6450', '6490', '6500', '6510', '6520', '6530', '6540', '6550', '6551', '6552',
|
||||
'6553', '6554', '6555', '6556', '6559', '6560', '6570', '6580', '6590', '6700', '6710', '6800',
|
||||
'6810', '6820', '6830', '6840', '6850', '6860', '6870', '6880', '6890', '6900', '6910', '6920',
|
||||
'6930', '6940', '6950', '6970', '6980', '6981', '6982', '6990', '6991', '6992', '6993', '6996',
|
||||
'6997', '6998', '6999', '7000', '7010', '7011', '7012', '7013', '7017', '7018', '7019', '7030',
|
||||
'7031', '7032', '7037', '7038', '7039', '7080', '7081', '7082', '7083', '7089', '7090', '7200',
|
||||
'7210', '7211', '7212', '7213', '7217', '7218', '7219', '7220', '7221', '7222', '7227', '7228',
|
||||
'7229', '7230', '7231', '7232', '7237', '7238', '7239', '7240', '7280', '7281', '7282', '7283',
|
||||
'7284', '7285', '7286', '7288', '7289', '7290', '7291', '7292', '7300', '7310', '7311', '7312',
|
||||
'7313', '7314', '7315', '7316', '7317', '7318', '7319', '7320', '7321', '7322', '7323', '7324',
|
||||
'7330', '7331', '7332', '7333', '7350', '7370', '7380', '7381', '7382', '7383', '7384', '7385',
|
||||
'7386', '7387', '7388', '7389', '7390', '7391', '7392', '7400', '7410', '7411', '7412', '7420',
|
||||
'7430', '7440', '7441', '7448', '7460', '7461', '7462', '7463', '7470', '7490', '7500', '7510',
|
||||
'7511', '7512', '7515', '7516', '7518', '7519', '7530', '7531', '7532', '7533', '7550', '7551',
|
||||
'7552', '7553', '7554', '7570', '7571', '7572', '7580', '7581', '7582', '7583', '7589', '7590',
|
||||
'7600', '7610', '7620', '7621', '7622', '7623', '7630', '7631', '7632', '7650', '7670', '7671',
|
||||
'7678', '7690', '7691', '7692', '7693', '7699', '7710', '7720', '7730', '7731', '7732', '7733',
|
||||
'7740', '7760', '7770', '7780', '7781', '7782', '7783', '7790', '7810', '7811', '7812', '7813',
|
||||
'7814', '7815', '7816', '7817', '7819', '7820', '7821', '7824', '7829', '7830', '7831', '7832',
|
||||
'7836', '7839', '7840', '7940', '7960', '7970', '7971', '7972', '7973', '7990', '8010', '8012',
|
||||
'8016', '8020', '8030', '8070', '8072', '8076', '8077', '8080', '8082', '8086', '8087', '8110',
|
||||
'8111', '8112', '8113', '8116', '8117', '8118', '8120', '8121', '8122', '8123', '8130', '8131',
|
||||
'8132', '8133', '8170', '8171', '8172', '8173', '8174', '8176', '8177', '8180', '8181', '8182',
|
||||
'8183', '8184', '8186', '8187', '8210', '8212', '8216', '8220', '8221', '8222', '8223', '8230',
|
||||
'8231', '8236', '8240', '8250', '8251', '8252', '8254', '8255', '8260', '8261', '8262', '8263',
|
||||
'8270', '8271', '8272', '8273', '8280', '8281', '8282', '8283', '8290', '8291', '8295', '8310',
|
||||
'8311', '8312', '8313', '8314', '8317', '8319', '8320', '8321', '8325', '8330', '8331', '8336',
|
||||
'8340', '8350', '8360', '8361', '8362', '8363', '8370', '8380', '8390', '8400', '8410', '8411',
|
||||
'8412', '8413', '8415', '8417', '8418', '8419', '8420', '8421', '8422', '8423', '8424', '8429',
|
||||
'8430', '8431', '8436', '8440', '8450', '8451', '8455', '8460', '8461', '8462', '8463', '8480',
|
||||
'8490', '8491', '8810', '8811', '8819', '8820', '8830', '8840', '8850', '8851', '8852', '8853',
|
||||
'8860', '8861', '8862', '8864', '8865', '8866', '8869', '8890', '8892', '8896', '8899', '8910',
|
||||
'8920', '8930', '8940', '8980', '8990', '8999',
|
||||
'1229', '1230', '1240', '1249', '1250', '1259', '1260', '1269', '1280', '1281', '1288', '1290',
|
||||
'1291', '1292', '1298', '1299', '1310', '1311', '1312', '1313', '1314', '1316', '1317', '1318',
|
||||
'1320', '1321', '1322', '1323', '1328', '1330', '1331', '1332', '1333', '1334', '1336', '1337',
|
||||
'1340', '1341', '1342', '1343', '1344', '1346', '1347', '1350', '1351', '1352', '1353', '1354',
|
||||
'1356', '1357', '1358', '1360', '1369', '1370', '1380', '1381', '1382', '1383', '1384', '1385',
|
||||
'1387', '1388', '1389', '1410', '1419', '1420', '1429', '1440', '1449', '1450', '1459', '1460',
|
||||
'1465', '1466', '1467', '1469', '1470', '1471', '1478', '1479', '1480', '1481', '1489', '1490',
|
||||
'1491', '1492', '1493', '1510', '1511', '1512', '1513', '1516', '1518', '1519', '1520', '1525',
|
||||
'1529', '1530', '1531', '1532', '1536', '1539', '1550', '1560', '1561', '1562', '1563', '1568',
|
||||
'1569', '1570', '1571', '1572', '1573', '1610', '1611', '1612', '1613', '1614', '1619', '1620',
|
||||
'1630', '1640', '1650', '1660', '1661', '1662', '1663', '1670', '1671', '1672', '1673', '1680',
|
||||
'1681', '1682', '1683', '1684', '1685', '1686', '1687', '1688', '1689', '1690', '1710', '1720',
|
||||
'1730', '1740', '1750', '1760', '1770', '1780', '1790', '1810', '1820', '1830', '1860', '1880',
|
||||
'1886', '1889', '1890', '1910', '1911', '1912', '1913', '1920', '1930', '1940', '1950', '1960',
|
||||
'1970', '1972', '1973', '1974', '1979', '1980', '1990', '2010', '2011', '2013', '2017', '2018',
|
||||
'2019', '2020', '2021', '2023', '2027', '2028', '2029', '2030', '2031', '2033', '2037', '2038',
|
||||
'2039', '2040', '2041', '2043', '2047', '2048', '2049', '2050', '2060', '2061', '2064', '2065',
|
||||
'2066', '2067', '2068', '2069', '2070', '2071', '2072', '2080', '2081', '2082', '2083', '2084',
|
||||
'2085', '2086', '2087', '2088', '2089', '2090', '2091', '2092', '2093', '2094', '2095', '2096',
|
||||
'2097', '2098', '2099', '2110', '2120', '2121', '2122', '2123', '2124', '2125', '2126', '2127',
|
||||
'2129', '2130', '2131', '2132', '2133', '2134', '2135', '2136', '2137', '2139', '2150', '2151',
|
||||
'2152', '2153', '2160', '2161', '2162', '2164', '2190', '2196', '2199', '2210', '2220', '2230',
|
||||
'2240', '2250', '2252', '2253', '2290', '2310', '2320', '2321', '2322', '2323', '2324', '2330',
|
||||
'2340', '2350', '2351', '2355', '2359', '2360', '2361', '2362', '2363', '2370', '2371', '2372',
|
||||
'2373', '2390', '2391', '2392', '2393', '2394', '2395', '2396', '2397', '2399', '2410', '2411',
|
||||
'2412', '2417', '2419', '2420', '2421', '2429', '2430', '2431', '2438', '2439', '2440', '2441',
|
||||
'2443', '2445', '2448', '2450', '2460', '2461', '2462', '2463', '2470', '2471', '2472', '2473',
|
||||
'2480', '2490', '2491', '2492', '2499', '2510', '2512', '2513', '2514', '2515', '2517', '2518',
|
||||
'2610', '2611', '2612', '2613', '2614', '2615', '2616', '2618', '2620', '2621', '2622', '2623',
|
||||
'2624', '2625', '2626', '2628', '2630', '2631', '2632', '2633', '2634', '2635', '2636', '2638',
|
||||
'2640', '2641', '2642', '2645', '2646', '2647', '2648', '2649', '2650', '2660', '2670', '2710',
|
||||
'2730', '2731', '2732', '2740', '2750', '2760', '2761', '2762', '2790', '2791', '2792', '2793',
|
||||
'2794', '2795', '2799', '2810', '2811', '2812', '2820', '2821', '2822', '2823', '2829', '2830',
|
||||
'2840', '2841', '2849', '2850', '2852', '2860', '2861', '2862', '2863', '2870', '2871', '2872',
|
||||
'2873', '2880', '2890', '2891', '2892', '2893', '2895', '2897', '2898', '2899', '2910', '2911',
|
||||
'2912', '2919', '2920', '2930', '2931', '2940', '2941', '2942', '2943', '2944', '2950', '2951',
|
||||
'2959', '2960', '2970', '2971', '2972', '2979', '2980', '2990', '2991', '2992', '2993', '2995',
|
||||
'2998', '2999', '3000', '3001', '3002', '3003', '3004', '3100', '3105', '3106', '3108', '3200',
|
||||
'3211', '3212', '3231', '3300', '3305', '3308', '3400', '3401', '3402', '3403', '3404', '3500',
|
||||
'3510', '3511', '3518', '3520', '3521', '3522', '3530', '3540', '3541', '3542', '3550', '3560',
|
||||
'3561', '3562', '3563', '3570', '3590', '3600', '3610', '3611', '3612', '3613', '3619', '3620',
|
||||
'3630', '3670', '3671', '3672', '3679', '3680', '3690', '3700', '3710', '3730', '3731', '3732',
|
||||
'3740', '3750', '3751', '3752', '3790', '3800', '3840', '3850', '3870', '3900', '3910', '3911',
|
||||
'3912', '3913', '3914', '3920', '3921', '3922', '3925', '3940', '3950', '3960', '3970', '3971',
|
||||
'3972', '3973', '3980', '3981', '3985', '3987', '3988', '3989', '3990', '3991', '3992', '3993',
|
||||
'3994', '3995', '3996', '3997', '3998', '3999', '4000', '4010', '4060', '4065', '4066', '4067',
|
||||
'4070', '4075', '4076', '4077', '4078', '4080', '4085', '4086', '4087', '4090', '4091', '4092',
|
||||
'4099', '4200', '4210', '4211', '4212', '4300', '4310', '4400', '4410', '4415', '4416', '4417',
|
||||
'4420', '4425', '4426', '4427', '4500', '4510', '4515', '4516', '4517', '4518', '4530', '4531',
|
||||
'4532', '4533', '4535', '4536', '4537', '4538', '4540', '4545', '4546', '4547', '4598', '4600',
|
||||
'4610', '4670', '4700', '4730', '4731', '4732', '4739', '4800', '4810', '4820', '4830', '4840',
|
||||
'4890', '4900', '4910', '4920', '4940', '4944', '4945', '4947', '4950', '4960', '4970', '4974',
|
||||
'4975', '4977', '4980', '4981', '4987', '4988', '5000', '5010', '5011', '5012', '5013', '5019',
|
||||
'5020', '5030', '5040', '5050', '5060', '5061', '5062', '5064', '5065', '5069', '5070', '5090',
|
||||
'5100', '5110', '5120', '5130', '5131', '5132', '5139', '5140', '5160', '5161', '5162', '5164',
|
||||
'5165', '5169', '5170', '5190', '5191', '5192', '5193', '5198', '5200', '5210', '5220', '5250',
|
||||
'5290', '5300', '5310', '5320', '5330', '5340', '5350', '5360', '5370', '5380', '5390', '5400',
|
||||
'5410', '5411', '5412', '5420', '5430', '5440', '5460', '5480', '5500', '5510', '5520', '5530',
|
||||
'5550', '5580', '5590', '5600', '5610', '5611', '5612', '5613', '5615', '5616', '5619', '5620',
|
||||
'5621', '5622', '5623', '5625', '5626', '5629', '5630', '5631', '5632', '5633', '5635', '5639',
|
||||
'5640', '5641', '5642', '5643', '5645', '5646', '5649', '5650', '5651', '5652', '5653', '5655',
|
||||
'5656', '5659', '5670', '5671', '5672', '5673', '5675', '5679', '5680', '5681', '5682', '5683',
|
||||
'5685', '5689', '5690', '5691', '5692', '5693', '5695', '5696', '5699', '5700', '5710', '5711',
|
||||
'5712', '5720', '5721', '5722', '5729', '5730', '5790', '5800', '5810', '5820', '5830', '5831',
|
||||
'5832', '5890', '5900', '5910', '5920', '5930', '5940', '5950', '5960', '5970', '5980', '5981',
|
||||
'5982', '5990', '6000', '6010', '6020', '6030', '6040', '6050', '6055', '6059', '6060', '6061',
|
||||
'6062', '6063', '6064', '6069', '6070', '6071', '6072', '6080', '6090', '6100', '6110', '6150',
|
||||
'6200', '6210', '6211', '6212', '6219', '6230', '6250', '6290', '6300', '6310', '6320', '6330',
|
||||
'6340', '6341', '6342', '6350', '6351', '6352', '6360', '6361', '6362', '6370', '6380', '6390',
|
||||
'6391', '6392', '6400', '6420', '6421', '6422', '6423', '6424', '6430', '6440', '6450', '6490',
|
||||
'6500', '6510', '6520', '6530', '6540', '6550', '6551', '6552', '6553', '6554', '6555', '6556',
|
||||
'6559', '6560', '6570', '6580', '6590', '6700', '6710', '6800', '6810', '6820', '6830', '6840',
|
||||
'6850', '6860', '6870', '6880', '6890', '6900', '6910', '6920', '6930', '6940', '6950', '6970',
|
||||
'6980', '6981', '6982', '6990', '6991', '6992', '6993', '6996', '6997', '6998', '6999', '7000',
|
||||
'7010', '7011', '7012', '7013', '7017', '7018', '7019', '7030', '7031', '7032', '7037', '7038',
|
||||
'7039', '7080', '7081', '7082', '7083', '7089', '7090', '7200', '7210', '7211', '7212', '7213',
|
||||
'7217', '7218', '7219', '7220', '7221', '7222', '7227', '7228', '7229', '7230', '7231', '7232',
|
||||
'7237', '7238', '7239', '7240', '7280', '7281', '7282', '7283', '7284', '7285', '7286', '7288',
|
||||
'7289', '7290', '7291', '7292', '7300', '7310', '7311', '7312', '7313', '7314', '7315', '7316',
|
||||
'7317', '7318', '7319', '7320', '7321', '7322', '7323', '7324', '7330', '7331', '7332', '7333',
|
||||
'7350', '7370', '7380', '7381', '7382', '7383', '7384', '7385', '7386', '7387', '7388', '7389',
|
||||
'7390', '7391', '7392', '7400', '7410', '7411', '7412', '7420', '7430', '7440', '7441', '7448',
|
||||
'7460', '7461', '7462', '7463', '7470', '7490', '7500', '7510', '7511', '7512', '7515', '7516',
|
||||
'7518', '7519', '7530', '7531', '7532', '7533', '7550', '7551', '7552', '7553', '7554', '7570',
|
||||
'7571', '7572', '7580', '7581', '7582', '7583', '7589', '7590', '7600', '7610', '7620', '7621',
|
||||
'7622', '7623', '7630', '7631', '7632', '7650', '7670', '7671', '7678', '7690', '7691', '7692',
|
||||
'7693', '7699', '7710', '7720', '7730', '7731', '7732', '7733', '7740', '7760', '7770', '7780',
|
||||
'7781', '7782', '7783', '7790', '7810', '7811', '7812', '7813', '7814', '7815', '7816', '7817',
|
||||
'7819', '7820', '7821', '7824', '7829', '7830', '7831', '7832', '7836', '7839', '7840', '7940',
|
||||
'7960', '7970', '7971', '7972', '7973', '7990', '8010', '8012', '8016', '8020', '8030', '8070',
|
||||
'8072', '8076', '8077', '8080', '8082', '8086', '8087', '8110', '8111', '8112', '8113', '8116',
|
||||
'8117', '8118', '8120', '8121', '8122', '8123', '8130', '8131', '8132', '8133', '8170', '8171',
|
||||
'8172', '8173', '8174', '8176', '8177', '8180', '8181', '8182', '8183', '8184', '8186', '8187',
|
||||
'8210', '8212', '8216', '8220', '8221', '8222', '8223', '8230', '8231', '8236', '8240', '8250',
|
||||
'8251', '8252', '8254', '8255', '8260', '8261', '8262', '8263', '8270', '8271', '8272', '8273',
|
||||
'8280', '8281', '8282', '8283', '8290', '8291', '8295', '8310', '8311', '8312', '8313', '8314',
|
||||
'8317', '8319', '8320', '8321', '8325', '8330', '8331', '8336', '8340', '8350', '8360', '8361',
|
||||
'8362', '8363', '8370', '8380', '8390', '8400', '8410', '8411', '8412', '8413', '8415', '8417',
|
||||
'8418', '8419', '8420', '8421', '8422', '8423', '8424', '8429', '8430', '8431', '8436', '8440',
|
||||
'8450', '8451', '8455', '8460', '8461', '8462', '8463', '8480', '8490', '8491', '8810', '8811',
|
||||
'8819', '8820', '8830', '8840', '8850', '8851', '8852', '8853', '8860', '8861', '8862', '8864',
|
||||
'8865', '8866', '8869', '8890', '8892', '8896', '8899', '8910', '8920', '8930', '8940', '8980',
|
||||
'8990', '8999',
|
||||
]
|
||||
|
||||
const BAS_ACCOUNT_NUMBER_SET: ReadonlySet<string> = new Set(BAS_ACCOUNT_NUMBERS)
|
||||
|
||||
@@ -697,36 +697,14 @@ export const CLASS_1_ACCOUNTS: BASReferenceAccount[] = [
|
||||
sru_code: '7202',
|
||||
k2_excluded: false,
|
||||
},
|
||||
{
|
||||
account_number: '1241',
|
||||
account_name: 'Personbilar',
|
||||
account_class: 1,
|
||||
account_group: '12',
|
||||
account_type: 'asset',
|
||||
normal_balance: 'debit',
|
||||
description: 'Personbilar',
|
||||
sru_code: '7202',
|
||||
k2_excluded: false,
|
||||
},
|
||||
{
|
||||
account_number: '1242',
|
||||
account_name: 'Lastbilar och andra transportmedel',
|
||||
account_class: 1,
|
||||
account_group: '12',
|
||||
account_type: 'asset',
|
||||
normal_balance: 'debit',
|
||||
description: 'Lastbilar och andra transportmedel',
|
||||
sru_code: '7202',
|
||||
k2_excluded: false,
|
||||
},
|
||||
{
|
||||
account_number: '1249',
|
||||
account_name: 'Ack. avskrivningar på bilar och andra transportmedel',
|
||||
account_name: 'Ackumulerade avskrivningar (fritt konto för Maskiner och andra tekniska anläggningar)',
|
||||
account_class: 1,
|
||||
account_group: '12',
|
||||
account_type: 'asset',
|
||||
normal_balance: 'credit',
|
||||
description: 'Ackumulerade avskrivningar på bilar och andra transportmedel (kontra-tillgång)',
|
||||
description: 'Ackumulerade avskrivningar på det fria maskinkontot 1240 (kontra-tillgång). BAS 2026 har inga bilkonton i 124x; bilar är 1216 eller 1226.',
|
||||
sru_code: '7202',
|
||||
k2_excluded: false,
|
||||
},
|
||||
@@ -741,25 +719,14 @@ export const CLASS_1_ACCOUNTS: BASReferenceAccount[] = [
|
||||
sru_code: '7215',
|
||||
k2_excluded: false,
|
||||
},
|
||||
{
|
||||
account_number: '1251',
|
||||
account_name: 'Inventarier',
|
||||
account_class: 1,
|
||||
account_group: '12',
|
||||
account_type: 'asset',
|
||||
normal_balance: 'debit',
|
||||
description: 'Inventarier',
|
||||
sru_code: '7215',
|
||||
k2_excluded: false,
|
||||
},
|
||||
{
|
||||
account_number: '1259',
|
||||
account_name: 'Ack. avskrivningar på inventarier och verktyg',
|
||||
account_name: 'Ackumulerade avskrivningar (fritt konto för Inventarier, verktyg och installationer)',
|
||||
account_class: 1,
|
||||
account_group: '12',
|
||||
account_type: 'asset',
|
||||
normal_balance: 'credit',
|
||||
description: 'Ackumulerade avskrivningar på inventarier och verktyg (kontra-tillgång)',
|
||||
description: 'Ackumulerade avskrivningar på det fria inventariekontot 1250 (kontra-tillgång). BAS 2026 har inga datorkonton i 125x; datorer är 1214 eller 1224.',
|
||||
sru_code: '7215',
|
||||
k2_excluded: false,
|
||||
},
|
||||
@@ -774,25 +741,14 @@ export const CLASS_1_ACCOUNTS: BASReferenceAccount[] = [
|
||||
sru_code: '7215',
|
||||
k2_excluded: false,
|
||||
},
|
||||
{
|
||||
account_number: '1261',
|
||||
account_name: 'Datorer, bärbara',
|
||||
account_class: 1,
|
||||
account_group: '12',
|
||||
account_type: 'asset',
|
||||
normal_balance: 'debit',
|
||||
description: 'Datorer, bärbara',
|
||||
sru_code: '7215',
|
||||
k2_excluded: false,
|
||||
},
|
||||
{
|
||||
account_number: '1269',
|
||||
account_name: 'Ack. avskrivningar på datorer',
|
||||
account_name: 'Ackumulerade avskrivningar (fritt konto för Inventarier, verktyg och installationer)',
|
||||
account_class: 1,
|
||||
account_group: '12',
|
||||
account_type: 'asset',
|
||||
normal_balance: 'credit',
|
||||
description: 'Ackumulerade avskrivningar på datorer (kontra-tillgång)',
|
||||
description: 'Ackumulerade avskrivningar på det fria inventariekontot 1260 (kontra-tillgång). BAS 2026 har inga leasingkonton i 126x; leasade tillgångar är 1217 eller 1227.',
|
||||
sru_code: '7215',
|
||||
k2_excluded: false,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
-- Backfill: retire the pre-2026 labels on 1249/1259/1269 where the company's
|
||||
-- head account already carries the BAS 2026 free-account label (#2413).
|
||||
--
|
||||
-- BAS 2026 restructured kontogrupp 12: 1210 (maskiner, för produktion) and
|
||||
-- 1220 (inventarier, ej för produktion) got the bilar/datorer sub-accounts,
|
||||
-- while 1230/1240 became "(Fritt konto för Maskiner och andra tekniska
|
||||
-- anläggningar)" and 1250/1260 "(Fritt konto för Inventarier, verktyg och
|
||||
-- installationer)". The catalog in lib/bookkeeping/bas-data/ followed for the
|
||||
-- heads but kept the retired contra accounts 1249/1259/1269 with their old
|
||||
-- names, so a company that activated 1240 + 1249 from the picker got a free
|
||||
-- machinery account whose only contra account said "bilar". The catalog now
|
||||
-- names those contra accounts after their heads; this backfill does the same
|
||||
-- for charts that already carry the contradictory pair.
|
||||
--
|
||||
-- Safety: a row is renamed only when BOTH hold: its name is byte-identical to
|
||||
-- one of the two catalog literals it could have been seeded with, AND the
|
||||
-- company's head account (1240/1250/1260) carries the BAS 2026 free label.
|
||||
-- A chart imported from an older BAS (1240 "Bilar och andra transportmedel")
|
||||
-- is internally consistent and is left alone, as is every user rename.
|
||||
--
|
||||
-- Third guard: the contra account has no journal lines. A SIE import whose
|
||||
-- #KONTO names were not carried creates BOTH rows from the catalog
|
||||
-- (lib/import/account-sync.ts create pass), so an old-BAS vehicle chart can
|
||||
-- hold exactly the pair above with years of "Avskrivningar bil" postings on
|
||||
-- 1249. A label with history is the user's to change; this backfill only
|
||||
-- corrects the label the picker handed out before anything was booked on it.
|
||||
-- No row is deleted; bookings key on account_number, never on the label.
|
||||
--
|
||||
-- The migration runs in one transaction (Supabase CLI wraps it). The SHARE
|
||||
-- lock below makes the "no journal lines" check and the rename atomic against
|
||||
-- concurrent postings: inserts on journal_entry_lines wait the few
|
||||
-- milliseconds this takes, reads are unaffected.
|
||||
|
||||
LOCK TABLE public.journal_entry_lines IN SHARE MODE;
|
||||
|
||||
UPDATE public.chart_of_accounts a
|
||||
SET account_name = 'Ackumulerade avskrivningar (fritt konto för Maskiner och andra tekniska anläggningar)',
|
||||
updated_at = now()
|
||||
WHERE a.account_number = '1249'
|
||||
AND a.account_name IN (
|
||||
'Ack. avskrivningar på bilar och andra transportmedel',
|
||||
'Ackumulerade avskrivningar på bilar och andra transportmedel'
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.chart_of_accounts h
|
||||
WHERE h.company_id = a.company_id
|
||||
AND h.account_number = '1240'
|
||||
AND h.account_name = '(Fritt konto för Maskiner och andra tekniska anläggningar)'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM public.journal_entry_lines l
|
||||
JOIN public.journal_entries e ON e.id = l.journal_entry_id
|
||||
WHERE e.company_id = a.company_id
|
||||
AND l.account_number = a.account_number
|
||||
);
|
||||
|
||||
UPDATE public.chart_of_accounts a
|
||||
SET account_name = 'Ackumulerade avskrivningar (fritt konto för Inventarier, verktyg och installationer)',
|
||||
updated_at = now()
|
||||
WHERE a.account_number = '1259'
|
||||
AND a.account_name IN (
|
||||
'Ack. avskrivningar på inventarier och verktyg',
|
||||
'Ackumulerade avskrivningar på inventarier och verktyg'
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.chart_of_accounts h
|
||||
WHERE h.company_id = a.company_id
|
||||
AND h.account_number = '1250'
|
||||
AND h.account_name = '(Fritt konto för Inventarier, verktyg och installationer)'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM public.journal_entry_lines l
|
||||
JOIN public.journal_entries e ON e.id = l.journal_entry_id
|
||||
WHERE e.company_id = a.company_id
|
||||
AND l.account_number = a.account_number
|
||||
);
|
||||
|
||||
UPDATE public.chart_of_accounts a
|
||||
SET account_name = 'Ackumulerade avskrivningar (fritt konto för Inventarier, verktyg och installationer)',
|
||||
updated_at = now()
|
||||
WHERE a.account_number = '1269'
|
||||
AND a.account_name IN (
|
||||
'Ack. avskrivningar på datorer',
|
||||
'Ackumulerade avskrivningar på datorer'
|
||||
)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM public.chart_of_accounts h
|
||||
WHERE h.company_id = a.company_id
|
||||
AND h.account_number = '1260'
|
||||
AND h.account_name = '(Fritt konto för Inventarier, verktyg och installationer)'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM public.journal_entry_lines l
|
||||
JOIN public.journal_entries e ON e.id = l.journal_entry_id
|
||||
WHERE e.company_id = a.company_id
|
||||
AND l.account_number = a.account_number
|
||||
);
|
||||
@@ -0,0 +1,171 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { insertPostedJournalEntry, seedCompany } from '@/tests/pg/fixtures'
|
||||
import { getPool } from '@/tests/pg/setup'
|
||||
|
||||
/**
|
||||
* pg-real coverage for 20260908113353_backfill_bas2026_retired_12xx_labels.sql
|
||||
* (#2413).
|
||||
*
|
||||
* The backfill renames 1249/1259/1269 only where the chart carries the
|
||||
* contradictory pair the catalog used to hand out: a BAS 2026 free head
|
||||
* (1240/1250/1260) next to a contra account still named after the retired
|
||||
* bilar/inventarier/datorer accounts. Guards under test:
|
||||
* - the pair is renamed (both catalog literals for the contra name)
|
||||
* - an old-BAS chart (1240 "Bilar och andra transportmedel") is untouched
|
||||
* - a user-renamed contra account is untouched
|
||||
* - a contra account without its head is untouched
|
||||
* - a contra account with journal lines (old-BAS SIE import re-labelled by
|
||||
* the catalog on create) is untouched
|
||||
* - idempotent (re-run is a no-op)
|
||||
*/
|
||||
|
||||
const BACKFILL_SQL = readFileSync(
|
||||
join(process.cwd(), 'supabase/migrations/20260908113353_backfill_bas2026_retired_12xx_labels.sql'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
const FREE_MASKINER = '(Fritt konto för Maskiner och andra tekniska anläggningar)'
|
||||
const FREE_INVENTARIER = '(Fritt konto för Inventarier, verktyg och installationer)'
|
||||
const CONTRA_MASKINER =
|
||||
'Ackumulerade avskrivningar (fritt konto för Maskiner och andra tekniska anläggningar)'
|
||||
const CONTRA_INVENTARIER =
|
||||
'Ackumulerade avskrivningar (fritt konto för Inventarier, verktyg och installationer)'
|
||||
|
||||
async function runBackfill(): Promise<void> {
|
||||
await getPool().query(BACKFILL_SQL)
|
||||
}
|
||||
|
||||
async function insertAccounts(
|
||||
userId: string,
|
||||
companyId: string,
|
||||
rows: Array<[number: string, name: string]>,
|
||||
): Promise<void> {
|
||||
for (const [number, name] of rows) {
|
||||
await getPool().query(
|
||||
`INSERT INTO public.chart_of_accounts
|
||||
(user_id, company_id, account_number, account_name, account_class, account_group,
|
||||
account_type, normal_balance, is_active)
|
||||
VALUES ($1, $2, $3, $4, 1, '12', 'asset', $5, true)`,
|
||||
[userId, companyId, number, name, number.endsWith('9') ? 'credit' : 'debit'],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function names(companyId: string): Promise<Record<string, string>> {
|
||||
const { rows } = await getPool().query<{ account_number: string; account_name: string }>(
|
||||
`SELECT account_number, account_name FROM public.chart_of_accounts WHERE company_id = $1`,
|
||||
[companyId],
|
||||
)
|
||||
return Object.fromEntries(rows.map((r) => [r.account_number, r.account_name]))
|
||||
}
|
||||
|
||||
describe('backfill_bas2026_retired_12xx_labels', () => {
|
||||
it('renames the contra account under a BAS 2026 free head, both catalog literals', async () => {
|
||||
const { userId, companyId } = await seedCompany()
|
||||
await insertAccounts(userId, companyId, [
|
||||
['1240', FREE_MASKINER],
|
||||
['1249', 'Ack. avskrivningar på bilar och andra transportmedel'],
|
||||
['1250', FREE_INVENTARIER],
|
||||
['1259', 'Ackumulerade avskrivningar på inventarier och verktyg'],
|
||||
['1260', FREE_INVENTARIER],
|
||||
['1269', 'Ack. avskrivningar på datorer'],
|
||||
])
|
||||
|
||||
await runBackfill()
|
||||
|
||||
const after = await names(companyId)
|
||||
expect(after['1249']).toBe(CONTRA_MASKINER)
|
||||
expect(after['1259']).toBe(CONTRA_INVENTARIER)
|
||||
expect(after['1269']).toBe(CONTRA_INVENTARIER)
|
||||
expect(after['1240']).toBe(FREE_MASKINER)
|
||||
})
|
||||
|
||||
it('leaves an old-BAS chart alone: 1240 Bilar next to 1249 Ack. avskr. bilar is consistent', async () => {
|
||||
const { userId, companyId } = await seedCompany()
|
||||
await insertAccounts(userId, companyId, [
|
||||
['1240', 'Bilar och andra transportmedel'],
|
||||
['1249', 'Ackumulerade avskrivningar på bilar och andra transportmedel'],
|
||||
['1250', 'Datorer'],
|
||||
['1259', 'Ackumulerade avskrivningar på datorer'],
|
||||
])
|
||||
|
||||
await runBackfill()
|
||||
|
||||
const after = await names(companyId)
|
||||
expect(after['1249']).toBe('Ackumulerade avskrivningar på bilar och andra transportmedel')
|
||||
expect(after['1259']).toBe('Ackumulerade avskrivningar på datorer')
|
||||
})
|
||||
|
||||
it('leaves a user rename alone', async () => {
|
||||
const { userId, companyId } = await seedCompany()
|
||||
await insertAccounts(userId, companyId, [
|
||||
['1240', FREE_MASKINER],
|
||||
['1249', 'Ack. avskrivningar maskiner och andra tekniska anläggningar'],
|
||||
])
|
||||
|
||||
await runBackfill()
|
||||
|
||||
expect((await names(companyId))['1249']).toBe(
|
||||
'Ack. avskrivningar maskiner och andra tekniska anläggningar',
|
||||
)
|
||||
})
|
||||
|
||||
it('leaves a contra account without its free head alone', async () => {
|
||||
const { userId, companyId } = await seedCompany()
|
||||
await insertAccounts(userId, companyId, [
|
||||
['1249', 'Ack. avskrivningar på bilar och andra transportmedel'],
|
||||
])
|
||||
|
||||
await runBackfill()
|
||||
|
||||
expect((await names(companyId))['1249']).toBe(
|
||||
'Ack. avskrivningar på bilar och andra transportmedel',
|
||||
)
|
||||
})
|
||||
|
||||
it('leaves a contra account with journal lines alone, even under a free head', async () => {
|
||||
// lib/import/account-sync.ts creates missing accounts with the catalog
|
||||
// name when the SIE #KONTO names are not carried, so an old-BAS vehicle
|
||||
// chart can hold the exact pair with real depreciation booked on 1249.
|
||||
const { userId, companyId, fiscalPeriodId } = await seedCompany()
|
||||
await insertAccounts(userId, companyId, [
|
||||
['1240', FREE_MASKINER],
|
||||
['1249', 'Ack. avskrivningar på bilar och andra transportmedel'],
|
||||
['7832', 'Avskrivningar på inventarier'],
|
||||
])
|
||||
await insertPostedJournalEntry({
|
||||
userId,
|
||||
companyId,
|
||||
fiscalPeriodId,
|
||||
sourceType: 'import',
|
||||
lines: [
|
||||
{ accountNumber: '7832', debitAmount: 2500, creditAmount: 0 },
|
||||
{ accountNumber: '1249', debitAmount: 0, creditAmount: 2500 },
|
||||
],
|
||||
})
|
||||
|
||||
await runBackfill()
|
||||
|
||||
expect((await names(companyId))['1249']).toBe(
|
||||
'Ack. avskrivningar på bilar och andra transportmedel',
|
||||
)
|
||||
})
|
||||
|
||||
it('is idempotent', async () => {
|
||||
const { userId, companyId } = await seedCompany()
|
||||
await insertAccounts(userId, companyId, [
|
||||
['1260', FREE_INVENTARIER],
|
||||
['1269', 'Ackumulerade avskrivningar på datorer'],
|
||||
])
|
||||
|
||||
await runBackfill()
|
||||
const first = await names(companyId)
|
||||
await runBackfill()
|
||||
const second = await names(companyId)
|
||||
|
||||
expect(first['1269']).toBe(CONTRA_INVENTARIER)
|
||||
expect(second).toEqual(first)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user