apiVersion: connectors.lumnik.io/v1kind: Connectormetadata:name: scenario-cfonb-bconnector_type: csv-filescopes: [treasury-b]spec:transport:kind: localpath: /tmp/lumnik-scenario/cfonb-bank-statement/b/*.txt # the bank's CFONB 120 statementsparser:lines: true # fixed width: 120 characters per record, no separatorafter_process: nonemapping:cell: - regex-extract: # every record: its code, then the account it is aboutcolumn: linerequired: truepattern: "^(\\d{2})(.{5}).{4}(.{5})(.{3})(\\d).(.{11})"groups: { 1: record, 2: bank, 3: branch, 4: currency, 5: decimals, 6: account } - regex-extract: # 01 old balance, 04 movement, 07 new balancecolumn: linepattern: "^0[147].{32}(.{6}).{8}(.{31}).{2}(.{7}).{2}(.{14})"groups: { 1: op_date, 2: label, 3: entry_no, 4: amount_raw } - regex-extract: { column: line, pattern: "^05.{43}(.{3})(.{70})", groups: { 1: qualifier, 2: info } } # 05 complement - regex-extract: { column: line, pattern: "^01.{32}(.{6})", groups: { 1: statement_date } } # 01 opens a statement - regex-extract: { column: line, pattern: "^04.{79}(.{7})", groups: { 1: movement } } # 04 is a movement-clean: { columns: [label, info, entry_no], strip: true }-kotlin: { script: hooks:cfonb-amount-b.kts } # the signed amount, from its last character - parse-number: { column: amount } - parse-date: { column: op_date, formats: ["ddMMyy"] } - parse-date: { column: statement_date, formats: ["ddMMyy"] } - fill-down: { columns: [statement_date] } # the statement, on each of its records - fill-down: { columns: [movement], reset-when: { column: record, in: ["01", "07"] } } # the movement, on its 05 complements
manifests/cfonb-amount.kts
// Operator-provided: a CFONB 120 amount. 13 digits, then one character holding the last digit and the sign —// "{" and A to I for 0 to 9 positive, "}" and J to R for 0 to 9 negative — scaled by the record's decimals.@Suppress("UNCHECKED_CAST")val cells = bindings["cells"] as MutableMap<String, Any?>val raw = (cells["amount_raw"] as String?).orEmpty()if (raw.length == 14) { val positive = "{ABCDEFGHI".indexOf(raw.last()) val negative = "}JKLMNOPQR".indexOf(raw.last()) val digit = if (positive >= 0) positive else negative if (digit >= 0) { val scaled = (raw.dropLast(1) + digit).toBigDecimal().movePointLeft((cells["decimals"] as String).toInt()) cells["amount"] = (if (negative >= 0) scaled.negate() else scaled).toPlainString() }}
$ lm hook scriptsNAME SIZE SHA256
cfonb-amount-b.kts 784 3f94b73a8a923324f6750648e230e3d91026299ed636627a4562b14552d493ba$ lm apply -f 10-statement.yamlApplied connector "scenario-cfonb-b" — 1 endpoint$ lm connector run scenario-cfonb-brun scheduled: bda4d683-4b1b-4f67-86b7-b5ca733b97bc$ lm run get bda4d683-4b1b-4f67-86b7-b5ca733b97bcID CONNECTOR ENDPOINT STATUS IN OUT SKIPPED DEAD-LETTERS STARTED ENDED
bda4d683 scenario-cfonb-b default Completed 11 11 0 0 2026-11-03 16:19 2026-11-03 16:19• script hooks:cfonb-amount-b.kts sha256 3f94b73a8a923324f6750648e230e3d91026299ed636627a4562b14552d493ba