[{"data":1,"prerenderedAt":2600},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report":2565,"ancestors:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report":2574,"children:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report":2579,"related:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report":2580},{"id":4,"title":5,"body":6,"dateModified":2529,"datePublished":2529,"description":2530,"extension":2531,"faq":2532,"keyword":2541,"kind":2542,"meta":2543,"navigation":268,"path":2553,"seo":2554,"slug":2557,"stem":2558,"topics":2559,"type":2563,"__hash__":2564},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report\u002Findex.md","Build a Bank Reconciliation Report with Python and Excel",{"type":7,"value":8,"toc":2515},"minimark",[9,19,187,192,223,233,237,240,670,686,690,693,861,864,868,881,1176,1188,1306,1310,1325,1574,1577,1581,1584,1875,2077,2080,2084,2087,2389,2392,2396,2436,2440,2443,2447,2450,2454,2460,2466,2472,2478,2482,2511],[10,11,12,13,18],"p",{},"A bank reconciliation proves that the cash book and the bank agree, and explains exactly why their balances differ. The differences are normally timing — cheques written but not yet cashed, receipts banked on the last day that the bank will show tomorrow — plus a few items the bank recorded first, such as charges and interest. Doing it by hand means ticking lines on two printouts. Doing it in Python means matching in passes, from the most certain to the least, and making sure no line is matched twice. This page is part of ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002F","Building Finance and Operations Reports",".",[20,21,29,30,29,34,29,38,29,46,29,56,29,67,29,74,29,79,29,83,29,89,29,94,29,97,29,103,29,107,29,110,29,113,29,117,29,120,29,123,29,129,29,133,29,136,29,139,29,143,29,146,29,149,29,155,29,160,29,164,29,168,29,171,29,174,29,180,29,184],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"4 0 752 142","img",[25,26],"rec-flow-t","rec-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"Matching in passes, from certain to likely",[35,36,37],"desc",{"id":26},"Five steps. Extract references such as cheque numbers and invoice numbers from both sides. Pass one matches on reference and amount. Pass two matches the remaining lines on amount within five days, one to one. Classify what is left as outstanding cheques, deposits in transit or bank-only items. Prove the adjusted balances agree.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"4","0","752","142","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"20.0","16.0","115.2","110.5","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[57,58,60],"g",{"transform":59},"translate(65.6,29.0) scale(1.000)",[61,62],"path",{"d":63,"fill":64,"stroke":65,"style":66},"M11 4a7 7 0 1 0 .01 0z M16 16l5 5","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[68,69,73],"text",{"x":70,"y":71,"style":72},"77.6","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","References",[68,75,78],{"x":70,"y":76,"style":77},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","regex on both",[68,80,82],{"x":70,"y":81,"style":77},"112.5","sides",[84,85],"line",{"x1":86,"y1":87,"x2":88,"y2":87,"stroke":54,"style":55},"140.2","71.2","162.2",[90,91],"polygon",{"points":92,"fill":93},"166.2,71.2 157.2,76.2 157.2,66.2","#5b5cf0",[39,95],{"x":96,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"171.2",[57,98,100],{"transform":99},"translate(216.8,29.0) scale(1.000)",[61,101],{"d":102,"fill":64,"stroke":65,"style":66},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1",[68,104,106],{"x":105,"y":71,"style":72},"228.8","Pass 1",[68,108,109],{"x":105,"y":76,"style":77},"reference +",[68,111,112],{"x":105,"y":81,"style":77},"amount",[84,114],{"x1":115,"y1":87,"x2":116,"y2":87,"stroke":54,"style":55},"291.4","313.4",[90,118],{"points":119,"fill":93},"317.4,71.2 308.4,76.2 308.4,66.2",[39,121],{"x":122,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"322.4",[57,124,126],{"transform":125},"translate(368.0,29.0) scale(1.000)",[61,127],{"d":128,"fill":64,"stroke":65,"style":66},"M4 5h16v15H4z M4 10h16 M8 3v4 M16 3v4",[68,130,132],{"x":131,"y":71,"style":72},"380.0","Pass 2",[68,134,135],{"x":131,"y":76,"style":77},"amount, ±5",[68,137,138],{"x":131,"y":81,"style":77},"days",[84,140],{"x1":141,"y1":87,"x2":142,"y2":87,"stroke":54,"style":55},"442.6","464.6",[90,144],{"points":145,"fill":93},"468.6,71.2 459.6,76.2 459.6,66.2",[39,147],{"x":148,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"473.6",[57,150,152],{"transform":151},"translate(519.2,37.8) scale(1.000)",[61,153],{"d":154,"fill":64,"stroke":65,"style":66},"M3 5h18l-7 8v6l-4 2v-8z",[68,156,159],{"x":157,"y":158,"style":72},"531.2","84.2","Classify",[68,161,163],{"x":157,"y":162,"style":77},"103.8","what is left",[84,165],{"x1":166,"y1":87,"x2":167,"y2":87,"stroke":54,"style":55},"593.8","615.8",[90,169],{"points":170,"fill":93},"619.8,71.2 610.8,76.2 610.8,66.2",[39,172],{"x":173,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"624.8",[57,175,177],{"transform":176},"translate(670.4,37.8) scale(1.000)",[61,178],{"d":179,"fill":64,"stroke":65,"style":66},"M5 12.5l4.5 4.5L19 7",[68,181,183],{"x":182,"y":158,"style":72},"682.4","Prove",[68,185,186],{"x":182,"y":162,"style":77},"balances agree",[188,189,191],"h2",{"id":190},"prerequisites","Prerequisites",[193,194,199],"pre",{"className":195,"code":196,"language":197,"meta":198,"style":198},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl xlsxwriter\n","bash","",[200,201,202],"code",{"__ignoreMap":198},[203,204,206,210,214,217,220],"span",{"class":84,"line":205},1,[203,207,209],{"class":208},"sMTad","pip",[203,211,213],{"class":212},"srMev"," install",[203,215,216],{"class":212}," pandas",[203,218,219],{"class":212}," openpyxl",[203,221,222],{"class":212}," xlsxwriter\n",[10,224,225,228,229,232],{},[200,226,227],{},"cash_book.xlsx"," has the month's eight ledger entries and ",[200,230,231],{},"bank_statement.xlsx"," the seven statement lines. Both started the month at 20,000.00 and were reconciled at the end of August.",[188,234,236],{"id":235},"step-1-load-both-sides-and-extract-references","Step 1 — Load both sides and extract references",[10,238,239],{},"Statement narratives and ledger descriptions rarely match as text — \"CHQ 100231\" against \"Chq 100231 Northline\" — but they often share a reference. Pull cheque numbers and invoice numbers out of both with the same regular expressions:",[193,241,245],{"className":242,"code":243,"language":244,"meta":198,"style":198},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nbook = pd.read_excel(\"cash_book.xlsx\", parse_dates=[\"Date\"])\nbank = pd.read_excel(\"bank_statement.xlsx\", parse_dates=[\"Date\"])\nbank.insert(0, \"Line\", [f\"B{i}\" for i in range(1, len(bank) + 1)])\n\ndef reference(text):\n    s = text.str.upper()\n    return s.str.extract(r\"CHQ\\s*(\\d{6})\", expand=False).radd(\"CHQ\").fillna(\n        s.str.extract(r\"(INV-\\d+)\", expand=False))\n\nbook[\"Key\"] = reference(book[\"Description\"])\nbank[\"Key\"] = reference(bank[\"Narrative\"])\nfor df in (book, bank):\n    df[\"Cents\"] = (df[\"Amount\"] * 100).round().astype(\"int64\")\nprint(book[[\"Ref\", \"Description\", \"Key\"]].to_string(index=False))\nprint(bank[[\"Line\", \"Narrative\", \"Key\"]].to_string(index=False))\n","python",[200,246,247,263,270,303,328,399,404,417,428,479,514,519,541,561,575,610,642],{"__ignoreMap":198},[203,248,249,253,257,260],{"class":84,"line":205},[203,250,252],{"class":251},"s-kum","import",[203,254,256],{"class":255},"skGVy"," pandas ",[203,258,259],{"class":251},"as",[203,261,262],{"class":255}," pd\n",[203,264,266],{"class":84,"line":265},2,[203,267,269],{"emptyLinePlaceholder":268},true,"\n",[203,271,273,276,279,282,285,288,292,294,297,300],{"class":84,"line":272},3,[203,274,275],{"class":255},"book ",[203,277,278],{"class":251},"=",[203,280,281],{"class":255}," pd.read_excel(",[203,283,284],{"class":212},"\"cash_book.xlsx\"",[203,286,287],{"class":255},", ",[203,289,291],{"class":290},"sa561","parse_dates",[203,293,278],{"class":251},[203,295,296],{"class":255},"[",[203,298,299],{"class":212},"\"Date\"",[203,301,302],{"class":255},"])\n",[203,304,306,309,311,313,316,318,320,322,324,326],{"class":84,"line":305},4,[203,307,308],{"class":255},"bank ",[203,310,278],{"class":251},[203,312,281],{"class":255},[203,314,315],{"class":212},"\"bank_statement.xlsx\"",[203,317,287],{"class":255},[203,319,291],{"class":290},[203,321,278],{"class":251},[203,323,296],{"class":255},[203,325,299],{"class":212},[203,327,302],{"class":255},[203,329,331,334,337,339,342,345,348,351,355,358,361,364,367,370,373,376,379,382,384,387,390,393,396],{"class":84,"line":330},5,[203,332,333],{"class":255},"bank.insert(",[203,335,42],{"class":336},"sP0c6",[203,338,287],{"class":255},[203,340,341],{"class":212},"\"Line\"",[203,343,344],{"class":255},", [",[203,346,347],{"class":251},"f",[203,349,350],{"class":212},"\"B",[203,352,354],{"class":353},"sSjpA","{",[203,356,357],{"class":255},"i",[203,359,360],{"class":353},"}",[203,362,363],{"class":212},"\"",[203,365,366],{"class":251}," for",[203,368,369],{"class":255}," i ",[203,371,372],{"class":251},"in",[203,374,375],{"class":336}," range",[203,377,378],{"class":255},"(",[203,380,381],{"class":336},"1",[203,383,287],{"class":255},[203,385,386],{"class":336},"len",[203,388,389],{"class":255},"(bank) ",[203,391,392],{"class":251},"+",[203,394,395],{"class":336}," 1",[203,397,398],{"class":255},")])\n",[203,400,402],{"class":84,"line":401},6,[203,403,269],{"emptyLinePlaceholder":268},[203,405,407,410,414],{"class":84,"line":406},7,[203,408,409],{"class":251},"def",[203,411,413],{"class":412},"s_Opv"," reference",[203,415,416],{"class":255},"(text):\n",[203,418,420,423,425],{"class":84,"line":419},8,[203,421,422],{"class":255},"    s ",[203,424,278],{"class":251},[203,426,427],{"class":255}," text.str.upper()\n",[203,429,431,434,437,440,443,446,449,452,455,458,460,462,465,467,470,473,476],{"class":84,"line":430},9,[203,432,433],{"class":251},"    return",[203,435,436],{"class":255}," s.str.extract(",[203,438,439],{"class":251},"r",[203,441,442],{"class":212},"\"CHQ",[203,444,445],{"class":336},"\\s",[203,447,448],{"class":251},"*",[203,450,451],{"class":336},"(\\d",[203,453,454],{"class":251},"{6}",[203,456,457],{"class":336},")",[203,459,363],{"class":212},[203,461,287],{"class":255},[203,463,464],{"class":290},"expand",[203,466,278],{"class":251},[203,468,469],{"class":336},"False",[203,471,472],{"class":255},").radd(",[203,474,475],{"class":212},"\"CHQ\"",[203,477,478],{"class":255},").fillna(\n",[203,480,482,485,487,489,491,494,497,499,501,503,505,507,509,511],{"class":84,"line":481},10,[203,483,484],{"class":255},"        s.str.extract(",[203,486,439],{"class":251},[203,488,363],{"class":212},[203,490,378],{"class":336},[203,492,493],{"class":212},"INV-",[203,495,496],{"class":336},"\\d",[203,498,392],{"class":251},[203,500,457],{"class":336},[203,502,363],{"class":212},[203,504,287],{"class":255},[203,506,464],{"class":290},[203,508,278],{"class":251},[203,510,469],{"class":336},[203,512,513],{"class":255},"))\n",[203,515,517],{"class":84,"line":516},11,[203,518,269],{"emptyLinePlaceholder":268},[203,520,522,525,528,531,533,536,539],{"class":84,"line":521},12,[203,523,524],{"class":255},"book[",[203,526,527],{"class":212},"\"Key\"",[203,529,530],{"class":255},"] ",[203,532,278],{"class":251},[203,534,535],{"class":255}," reference(book[",[203,537,538],{"class":212},"\"Description\"",[203,540,302],{"class":255},[203,542,544,547,549,551,553,556,559],{"class":84,"line":543},13,[203,545,546],{"class":255},"bank[",[203,548,527],{"class":212},[203,550,530],{"class":255},[203,552,278],{"class":251},[203,554,555],{"class":255}," reference(bank[",[203,557,558],{"class":212},"\"Narrative\"",[203,560,302],{"class":255},[203,562,564,567,570,572],{"class":84,"line":563},14,[203,565,566],{"class":251},"for",[203,568,569],{"class":255}," df ",[203,571,372],{"class":251},[203,573,574],{"class":255}," (book, bank):\n",[203,576,578,581,584,586,588,591,594,596,598,601,604,607],{"class":84,"line":577},15,[203,579,580],{"class":255},"    df[",[203,582,583],{"class":212},"\"Cents\"",[203,585,530],{"class":255},[203,587,278],{"class":251},[203,589,590],{"class":255}," (df[",[203,592,593],{"class":212},"\"Amount\"",[203,595,530],{"class":255},[203,597,448],{"class":251},[203,599,600],{"class":336}," 100",[203,602,603],{"class":255},").round().astype(",[203,605,606],{"class":212},"\"int64\"",[203,608,609],{"class":255},")\n",[203,611,613,616,619,622,624,626,628,630,633,636,638,640],{"class":84,"line":612},16,[203,614,615],{"class":336},"print",[203,617,618],{"class":255},"(book[[",[203,620,621],{"class":212},"\"Ref\"",[203,623,287],{"class":255},[203,625,538],{"class":212},[203,627,287],{"class":255},[203,629,527],{"class":212},[203,631,632],{"class":255},"]].to_string(",[203,634,635],{"class":290},"index",[203,637,278],{"class":251},[203,639,469],{"class":336},[203,641,513],{"class":255},[203,643,645,647,650,652,654,656,658,660,662,664,666,668],{"class":84,"line":644},17,[203,646,615],{"class":336},[203,648,649],{"class":255},"(bank[[",[203,651,341],{"class":212},[203,653,287],{"class":255},[203,655,558],{"class":212},[203,657,287],{"class":255},[203,659,527],{"class":212},[203,661,632],{"class":255},[203,663,635],{"class":290},[203,665,278],{"class":251},[203,667,469],{"class":336},[203,669,513],{"class":255},[10,671,672,673,676,677,680,681,685],{},"Upper-casing first makes the patterns case-insensitive, and prefixing cheque numbers with ",[200,674,675],{},"CHQ"," stops a cheque number colliding with a number from some other reference. Lines without a recognisable reference get no key and go to the second pass. The ",[200,678,679],{},"Cents"," column is the amount as a whole number of cents: matching on floats is fragile — 0.1 + 0.2 is not exactly 0.3 — and the two files may even load with different types, integers on one side and floats on the other, which pandas warns about when merging. ",[14,682,684],{"href":683},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Fextract-numbers-and-codes-from-text-with-pandas\u002F","Extract Numbers and Codes from Text with pandas"," covers extraction patterns in detail.",[188,687,689],{"id":688},"step-2-pass-one-match-on-reference-and-amount","Step 2 — Pass one: match on reference and amount",[10,691,692],{},"A shared reference and an identical amount is as certain as a match gets. Merge on both and record the pairs:",[193,694,696],{"className":242,"code":695,"language":244,"meta":198,"style":198},"pass1 = book.dropna(subset=[\"Key\"]).merge(bank.dropna(subset=[\"Key\"]), on=[\"Key\", \"Cents\"], suffixes=(\"_book\", \"_bank\"))\nassert not pass1[\"Ref\"].duplicated().any() and not pass1[\"Line\"].duplicated().any(), \"a reference matched twice\"\nmatches = pass1[[\"Ref\", \"Line\", \"Amount_bank\"]].rename(columns={\"Amount_bank\": \"Amount\"}).assign(Rule=\"reference\")\nprint(matches.to_string(index=False))\n",[200,697,698,764,795,846],{"__ignoreMap":198},[203,699,700,703,705,708,711,713,715,717,720,722,724,726,728,731,734,736,738,740,742,744,747,750,752,754,757,759,762],{"class":84,"line":205},[203,701,702],{"class":255},"pass1 ",[203,704,278],{"class":251},[203,706,707],{"class":255}," book.dropna(",[203,709,710],{"class":290},"subset",[203,712,278],{"class":251},[203,714,296],{"class":255},[203,716,527],{"class":212},[203,718,719],{"class":255},"]).merge(bank.dropna(",[203,721,710],{"class":290},[203,723,278],{"class":251},[203,725,296],{"class":255},[203,727,527],{"class":212},[203,729,730],{"class":255},"]), ",[203,732,733],{"class":290},"on",[203,735,278],{"class":251},[203,737,296],{"class":255},[203,739,527],{"class":212},[203,741,287],{"class":255},[203,743,583],{"class":212},[203,745,746],{"class":255},"], ",[203,748,749],{"class":290},"suffixes",[203,751,278],{"class":251},[203,753,378],{"class":255},[203,755,756],{"class":212},"\"_book\"",[203,758,287],{"class":255},[203,760,761],{"class":212},"\"_bank\"",[203,763,513],{"class":255},[203,765,766,769,772,775,777,780,783,785,787,789,792],{"class":84,"line":265},[203,767,768],{"class":251},"assert",[203,770,771],{"class":251}," not",[203,773,774],{"class":255}," pass1[",[203,776,621],{"class":212},[203,778,779],{"class":255},"].duplicated().any() ",[203,781,782],{"class":251},"and",[203,784,771],{"class":251},[203,786,774],{"class":255},[203,788,341],{"class":212},[203,790,791],{"class":255},"].duplicated().any(), ",[203,793,794],{"class":212},"\"a reference matched twice\"\n",[203,796,797,800,802,805,807,809,811,813,816,819,822,824,826,828,831,833,836,839,841,844],{"class":84,"line":272},[203,798,799],{"class":255},"matches ",[203,801,278],{"class":251},[203,803,804],{"class":255}," pass1[[",[203,806,621],{"class":212},[203,808,287],{"class":255},[203,810,341],{"class":212},[203,812,287],{"class":255},[203,814,815],{"class":212},"\"Amount_bank\"",[203,817,818],{"class":255},"]].rename(",[203,820,821],{"class":290},"columns",[203,823,278],{"class":251},[203,825,354],{"class":255},[203,827,815],{"class":212},[203,829,830],{"class":255},": ",[203,832,593],{"class":212},[203,834,835],{"class":255},"}).assign(",[203,837,838],{"class":290},"Rule",[203,840,278],{"class":251},[203,842,843],{"class":212},"\"reference\"",[203,845,609],{"class":255},[203,847,848,850,853,855,857,859],{"class":84,"line":305},[203,849,615],{"class":336},[203,851,852],{"class":255},"(matches.to_string(",[203,854,635],{"class":290},[203,856,278],{"class":251},[203,858,469],{"class":336},[203,860,513],{"class":255},[10,862,863],{},"Three pairs: the Acme receipt by invoice number and two cheques by number. The assertion guards against a reference appearing twice on one side, which would pair lines many-to-many; if it fires, the duplicate needs a person to look at it. Cheque 100233 has a reference but is not on the statement yet — it stays unmatched.",[188,865,867],{"id":866},"step-3-pass-two-match-on-amount-within-a-few-days-one-to-one","Step 3 — Pass two: match on amount within a few days, one to one",[10,869,870,871,875,876,880],{},"For lines still unmatched, pair entries with equal amounts whose dates are within a tolerance — here five days, enough for normal clearing. This must be ",[872,873,874],"strong",{},"one to one",": a plain merge on amount would pair the single Brightside bank credit of 4,100 with ",[877,878,879],"em",{},"both"," ledger receipts of 4,100. Build the candidate pairs, sort them by date gap, and accept each pair only if neither line has been used:",[193,882,884],{"className":242,"code":883,"language":244,"meta":198,"style":198},"left = book[~book[\"Ref\"].isin(matches[\"Ref\"])]\nright = bank[~bank[\"Line\"].isin(matches[\"Line\"])]\n\ncandidates = left.merge(right, on=\"Cents\", suffixes=(\"_book\", \"_bank\"))\ncandidates[\"Gap\"] = (candidates[\"Date_bank\"] - candidates[\"Date_book\"]).abs().dt.days\ncandidates = candidates[candidates[\"Gap\"] \u003C= 5].sort_values([\"Gap\", \"Date_book\"])\n\nused_book, used_bank, pass2 = set(), set(), []\nfor c in candidates.itertuples():\n    if c.Ref not in used_book and c.Line not in used_bank:\n        used_book.add(c.Ref); used_bank.add(c.Line)\n        pass2.append({\"Ref\": c.Ref, \"Line\": c.Line, \"Amount\": c.Amount_bank, \"Rule\": f\"amount, {c.Gap} day(s)\"})\nmatches = pd.concat([matches, pd.DataFrame(pass2)], ignore_index=True)\nprint(matches.to_string(index=False))\n",[200,885,886,911,933,937,969,1001,1031,1035,1054,1066,1095,1100,1143,1162],{"__ignoreMap":198},[203,887,888,891,893,896,899,901,903,906,908],{"class":84,"line":205},[203,889,890],{"class":255},"left ",[203,892,278],{"class":251},[203,894,895],{"class":255}," book[",[203,897,898],{"class":251},"~",[203,900,524],{"class":255},[203,902,621],{"class":212},[203,904,905],{"class":255},"].isin(matches[",[203,907,621],{"class":212},[203,909,910],{"class":255},"])]\n",[203,912,913,916,918,921,923,925,927,929,931],{"class":84,"line":265},[203,914,915],{"class":255},"right ",[203,917,278],{"class":251},[203,919,920],{"class":255}," bank[",[203,922,898],{"class":251},[203,924,546],{"class":255},[203,926,341],{"class":212},[203,928,905],{"class":255},[203,930,341],{"class":212},[203,932,910],{"class":255},[203,934,935],{"class":84,"line":272},[203,936,269],{"emptyLinePlaceholder":268},[203,938,939,942,944,947,949,951,953,955,957,959,961,963,965,967],{"class":84,"line":305},[203,940,941],{"class":255},"candidates ",[203,943,278],{"class":251},[203,945,946],{"class":255}," left.merge(right, ",[203,948,733],{"class":290},[203,950,278],{"class":251},[203,952,583],{"class":212},[203,954,287],{"class":255},[203,956,749],{"class":290},[203,958,278],{"class":251},[203,960,378],{"class":255},[203,962,756],{"class":212},[203,964,287],{"class":255},[203,966,761],{"class":212},[203,968,513],{"class":255},[203,970,971,974,977,979,981,984,987,989,992,995,998],{"class":84,"line":330},[203,972,973],{"class":255},"candidates[",[203,975,976],{"class":212},"\"Gap\"",[203,978,530],{"class":255},[203,980,278],{"class":251},[203,982,983],{"class":255}," (candidates[",[203,985,986],{"class":212},"\"Date_bank\"",[203,988,530],{"class":255},[203,990,991],{"class":251},"-",[203,993,994],{"class":255}," candidates[",[203,996,997],{"class":212},"\"Date_book\"",[203,999,1000],{"class":255},"]).abs().dt.days\n",[203,1002,1003,1005,1007,1010,1012,1014,1017,1020,1023,1025,1027,1029],{"class":84,"line":401},[203,1004,941],{"class":255},[203,1006,278],{"class":251},[203,1008,1009],{"class":255}," candidates[candidates[",[203,1011,976],{"class":212},[203,1013,530],{"class":255},[203,1015,1016],{"class":251},"\u003C=",[203,1018,1019],{"class":336}," 5",[203,1021,1022],{"class":255},"].sort_values([",[203,1024,976],{"class":212},[203,1026,287],{"class":255},[203,1028,997],{"class":212},[203,1030,302],{"class":255},[203,1032,1033],{"class":84,"line":406},[203,1034,269],{"emptyLinePlaceholder":268},[203,1036,1037,1040,1042,1045,1048,1051],{"class":84,"line":419},[203,1038,1039],{"class":255},"used_book, used_bank, pass2 ",[203,1041,278],{"class":251},[203,1043,1044],{"class":336}," set",[203,1046,1047],{"class":255},"(), ",[203,1049,1050],{"class":336},"set",[203,1052,1053],{"class":255},"(), []\n",[203,1055,1056,1058,1061,1063],{"class":84,"line":430},[203,1057,566],{"class":251},[203,1059,1060],{"class":255}," c ",[203,1062,372],{"class":251},[203,1064,1065],{"class":255}," candidates.itertuples():\n",[203,1067,1068,1071,1074,1077,1080,1083,1085,1088,1090,1092],{"class":84,"line":481},[203,1069,1070],{"class":251},"    if",[203,1072,1073],{"class":255}," c.Ref ",[203,1075,1076],{"class":251},"not",[203,1078,1079],{"class":251}," in",[203,1081,1082],{"class":255}," used_book ",[203,1084,782],{"class":251},[203,1086,1087],{"class":255}," c.Line ",[203,1089,1076],{"class":251},[203,1091,1079],{"class":251},[203,1093,1094],{"class":255}," used_bank:\n",[203,1096,1097],{"class":84,"line":516},[203,1098,1099],{"class":255},"        used_book.add(c.Ref); used_bank.add(c.Line)\n",[203,1101,1102,1105,1107,1110,1112,1115,1117,1120,1123,1125,1127,1130,1132,1135,1137,1140],{"class":84,"line":521},[203,1103,1104],{"class":255},"        pass2.append({",[203,1106,621],{"class":212},[203,1108,1109],{"class":255},": c.Ref, ",[203,1111,341],{"class":212},[203,1113,1114],{"class":255},": c.Line, ",[203,1116,593],{"class":212},[203,1118,1119],{"class":255},": c.Amount_bank, ",[203,1121,1122],{"class":212},"\"Rule\"",[203,1124,830],{"class":255},[203,1126,347],{"class":251},[203,1128,1129],{"class":212},"\"amount, ",[203,1131,354],{"class":353},[203,1133,1134],{"class":255},"c.Gap",[203,1136,360],{"class":353},[203,1138,1139],{"class":212}," day(s)\"",[203,1141,1142],{"class":255},"})\n",[203,1144,1145,1147,1149,1152,1155,1157,1160],{"class":84,"line":543},[203,1146,799],{"class":255},[203,1148,278],{"class":251},[203,1150,1151],{"class":255}," pd.concat([matches, pd.DataFrame(pass2)], ",[203,1153,1154],{"class":290},"ignore_index",[203,1156,278],{"class":251},[203,1158,1159],{"class":336},"True",[203,1161,609],{"class":255},[203,1163,1164,1166,1168,1170,1172,1174],{"class":84,"line":563},[203,1165,615],{"class":336},[203,1167,852],{"class":255},[203,1169,635],{"class":290},[203,1171,278],{"class":251},[203,1173,469],{"class":336},[203,1175,513],{"class":255},[10,1177,1178,1179,1183,1184,1187],{},"The Brightside credit on 11 September pairs with the receipt recorded on the 10th — one day apart — and the INV-2170 receipt from the 20th, nine days away, is outside the tolerance and stays unmatched. The rent pairs on the same day. Sorting by gap before accepting pairs is what makes the closest match win; ",[14,1180,1182],{"href":1181},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmatch-rows-by-nearest-date-with-merge-asof\u002F","Match Rows by Nearest Date with merge_asof"," covers a related technique, though ",[200,1185,1186],{},"merge_asof"," alone does not prevent one line being matched twice.",[20,1189,29,1194,29,1197,29,1200,29,1203,29,1210,29,1218,29,1224,29,1230,29,1236,29,1243,29,1247,29,1252,29,1256,29,1261,29,1266,29,1272,29,1277,29,1281,29,1285,29,1290,29,1293,29,1298,29,1301],{"viewBox":1190,"role":23,"ariaLabelledBy":1191,"xmlns":27,"style":28},"4 0 752 207",[1192,1193],"rec-one-to-one-t","rec-one-to-one-d",[31,1195,1196],{"id":1192},"Why the second pass must pair lines only once",[35,1198,1199],{"id":1193},"Two panels. A plain merge on amount pairs the one Brightside bank credit of 4,100 with both ledger receipts of 4,100, hiding that one receipt has not reached the bank. One-to-one matching by closest date pairs the credit with the 10 September receipt and leaves the 20 September receipt unmatched, where it is flagged.",[39,1201],{"x":41,"y":42,"width":43,"height":1202,"fill":45},"207",[39,1204],{"x":48,"y":49,"width":1205,"height":1206,"rx":1207,"fill":1208,"stroke":1209,"style":55},"351.0","175.0","14","#fee8f2","var(--accent,#f43f8f)",[57,1211,1213],{"transform":1212},"translate(182.5,29.0) scale(1.083)",[61,1214],{"d":1215,"fill":64,"stroke":1216,"style":1217},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--accent-ink,#be185d)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[68,1219,1223],{"x":1220,"y":1221,"style":1222},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Plain merge on amount",[84,1225],{"x1":1226,"y1":1227,"x2":1228,"y2":1227,"stroke":1209,"style":1229},"36.0","91.0","355.0","stroke-width:1px",[68,1231,1235],{"x":1232,"y":1233,"style":1234},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","B3 ↔ L3 (4,100)",[57,1237,1239],{"transform":1238},"translate(38.5,99.5) scale(0.625)",[61,1240],{"d":1241,"fill":64,"stroke":1209,"style":1242},"M6 6l12 12 M18 6L6 18","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[68,1244,1246],{"x":1232,"y":1245,"style":1234},"137.0","B3 ↔ L5 (4,100)",[57,1248,1250],{"transform":1249},"translate(38.5,125.5) scale(0.625)",[61,1251],{"d":1241,"fill":64,"stroke":1209,"style":1242},[68,1253,1255],{"x":1232,"y":1254,"style":1234},"163.0","a missing receipt is hidden",[57,1257,1259],{"transform":1258},"translate(38.5,151.5) scale(0.625)",[61,1260],{"d":1241,"fill":64,"stroke":1209,"style":1242},[39,1262],{"x":1263,"y":49,"width":1205,"height":1206,"rx":1207,"fill":1264,"stroke":1265,"style":55},"389.0","#d9f4f1","var(--teal,#0f9488)",[57,1267,1269],{"transform":1268},"translate(551.5,29.0) scale(1.083)",[61,1270],{"d":179,"fill":64,"stroke":1271,"style":1217},"var(--teal-ink,#0b6157)",[68,1273,1276],{"x":1274,"y":1221,"style":1275},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","One to one, closest date",[84,1278],{"x1":1279,"y1":1227,"x2":1280,"y2":1227,"stroke":1265,"style":1229},"405.0","724.0",[68,1282,1284],{"x":1283,"y":1233,"style":1234},"429.0","B3 ↔ L3, 1 day apart",[57,1286,1288],{"transform":1287},"translate(407.5,99.5) scale(0.625)",[61,1289],{"d":179,"fill":64,"stroke":1265,"style":1242},[68,1291,1292],{"x":1283,"y":1245,"style":1234},"L5 left unmatched",[57,1294,1296],{"transform":1295},"translate(407.5,125.5) scale(0.625)",[61,1297],{"d":179,"fill":64,"stroke":1265,"style":1242},[68,1299,1300],{"x":1283,"y":1254,"style":1234},"flagged for follow-up",[57,1302,1304],{"transform":1303},"translate(407.5,151.5) scale(0.625)",[61,1305],{"d":179,"fill":64,"stroke":1265,"style":1242},[188,1307,1309],{"id":1308},"step-4-classify-what-is-left","Step 4 — Classify what is left",[10,1311,1312,1313,1316,1317,1320,1321,1324],{},"Everything unmatched now falls into one of the standard categories. Ledger payments not on the statement are ",[872,1314,1315],{},"outstanding cheques","; ledger receipts not on it are ",[872,1318,1319],{},"deposits in transit","; statement lines not in the ledger are ",[872,1322,1323],{},"bank-only items",", which usually need a journal. Items older than the clearing time deserve a closer look:",[193,1326,1328],{"className":242,"code":1327,"language":244,"meta":198,"style":198},"period_end = pd.Timestamp(\"2026-09-30\")\nbook_open = book[~book[\"Ref\"].isin(matches[\"Ref\"])].copy()\nbank_only = bank[~bank[\"Line\"].isin(matches[\"Line\"])].copy()\n\nbook_open[\"Category\"] = book_open[\"Amount\"].map(lambda a: \"deposit in transit\" if a > 0 else \"outstanding cheque\")\nbook_open[\"Age\"] = (period_end - book_open[\"Date\"]).dt.days\nbook_open[\"Investigate\"] = book_open[\"Age\"] > 5\nbank_only[\"Category\"] = \"bank only — post to ledger\"\nprint(book_open[[\"Ref\", \"Date\", \"Description\", \"Amount\", \"Category\", \"Age\", \"Investigate\"]].to_string(index=False))\nprint(bank_only[[\"Line\", \"Narrative\", \"Amount\", \"Category\"]].to_string(index=False))\n",[200,1329,1330,1345,1367,1388,1392,1441,1464,1486,1500,1543],{"__ignoreMap":198},[203,1331,1332,1335,1337,1340,1343],{"class":84,"line":205},[203,1333,1334],{"class":255},"period_end ",[203,1336,278],{"class":251},[203,1338,1339],{"class":255}," pd.Timestamp(",[203,1341,1342],{"class":212},"\"2026-09-30\"",[203,1344,609],{"class":255},[203,1346,1347,1350,1352,1354,1356,1358,1360,1362,1364],{"class":84,"line":265},[203,1348,1349],{"class":255},"book_open ",[203,1351,278],{"class":251},[203,1353,895],{"class":255},[203,1355,898],{"class":251},[203,1357,524],{"class":255},[203,1359,621],{"class":212},[203,1361,905],{"class":255},[203,1363,621],{"class":212},[203,1365,1366],{"class":255},"])].copy()\n",[203,1368,1369,1372,1374,1376,1378,1380,1382,1384,1386],{"class":84,"line":272},[203,1370,1371],{"class":255},"bank_only ",[203,1373,278],{"class":251},[203,1375,920],{"class":255},[203,1377,898],{"class":251},[203,1379,546],{"class":255},[203,1381,341],{"class":212},[203,1383,905],{"class":255},[203,1385,341],{"class":212},[203,1387,1366],{"class":255},[203,1389,1390],{"class":84,"line":305},[203,1391,269],{"emptyLinePlaceholder":268},[203,1393,1394,1397,1400,1402,1404,1407,1409,1412,1415,1418,1421,1424,1427,1430,1433,1436,1439],{"class":84,"line":330},[203,1395,1396],{"class":255},"book_open[",[203,1398,1399],{"class":212},"\"Category\"",[203,1401,530],{"class":255},[203,1403,278],{"class":251},[203,1405,1406],{"class":255}," book_open[",[203,1408,593],{"class":212},[203,1410,1411],{"class":255},"].map(",[203,1413,1414],{"class":251},"lambda",[203,1416,1417],{"class":255}," a: ",[203,1419,1420],{"class":212},"\"deposit in transit\"",[203,1422,1423],{"class":251}," if",[203,1425,1426],{"class":255}," a ",[203,1428,1429],{"class":251},">",[203,1431,1432],{"class":336}," 0",[203,1434,1435],{"class":251}," else",[203,1437,1438],{"class":212}," \"outstanding cheque\"",[203,1440,609],{"class":255},[203,1442,1443,1445,1448,1450,1452,1455,1457,1459,1461],{"class":84,"line":401},[203,1444,1396],{"class":255},[203,1446,1447],{"class":212},"\"Age\"",[203,1449,530],{"class":255},[203,1451,278],{"class":251},[203,1453,1454],{"class":255}," (period_end ",[203,1456,991],{"class":251},[203,1458,1406],{"class":255},[203,1460,299],{"class":212},[203,1462,1463],{"class":255},"]).dt.days\n",[203,1465,1466,1468,1471,1473,1475,1477,1479,1481,1483],{"class":84,"line":406},[203,1467,1396],{"class":255},[203,1469,1470],{"class":212},"\"Investigate\"",[203,1472,530],{"class":255},[203,1474,278],{"class":251},[203,1476,1406],{"class":255},[203,1478,1447],{"class":212},[203,1480,530],{"class":255},[203,1482,1429],{"class":251},[203,1484,1485],{"class":336}," 5\n",[203,1487,1488,1491,1493,1495,1497],{"class":84,"line":419},[203,1489,1490],{"class":255},"bank_only[",[203,1492,1399],{"class":212},[203,1494,530],{"class":255},[203,1496,278],{"class":251},[203,1498,1499],{"class":212}," \"bank only — post to ledger\"\n",[203,1501,1502,1504,1507,1509,1511,1513,1515,1517,1519,1521,1523,1525,1527,1529,1531,1533,1535,1537,1539,1541],{"class":84,"line":430},[203,1503,615],{"class":336},[203,1505,1506],{"class":255},"(book_open[[",[203,1508,621],{"class":212},[203,1510,287],{"class":255},[203,1512,299],{"class":212},[203,1514,287],{"class":255},[203,1516,538],{"class":212},[203,1518,287],{"class":255},[203,1520,593],{"class":212},[203,1522,287],{"class":255},[203,1524,1399],{"class":212},[203,1526,287],{"class":255},[203,1528,1447],{"class":212},[203,1530,287],{"class":255},[203,1532,1470],{"class":212},[203,1534,632],{"class":255},[203,1536,635],{"class":290},[203,1538,278],{"class":251},[203,1540,469],{"class":336},[203,1542,513],{"class":255},[203,1544,1545,1547,1550,1552,1554,1556,1558,1560,1562,1564,1566,1568,1570,1572],{"class":84,"line":481},[203,1546,615],{"class":336},[203,1548,1549],{"class":255},"(bank_only[[",[203,1551,341],{"class":212},[203,1553,287],{"class":255},[203,1555,558],{"class":212},[203,1557,287],{"class":255},[203,1559,593],{"class":212},[203,1561,287],{"class":255},[203,1563,1399],{"class":212},[203,1565,632],{"class":255},[203,1567,635],{"class":290},[203,1569,278],{"class":251},[203,1571,469],{"class":336},[203,1573,513],{"class":255},[10,1575,1576],{},"The receipt for INV-2201 on 29 September and cheque 100233 on the 30th are ordinary month-end timing differences. The INV-2170 receipt is ten days old and still not in the bank — it may have been recorded before the money arrived, or banked into the wrong account — so it is flagged. The bank charges and interest are on the statement only and need posting to the cash book.",[188,1578,1580],{"id":1579},"step-5-prove-the-balances-agree","Step 5 — Prove the balances agree",[10,1582,1583],{},"The reconciliation statement adjusts each side for the items the other has not yet recorded. If every line was classified correctly, the two adjusted balances are equal:",[193,1585,1587],{"className":242,"code":1586,"language":244,"meta":198,"style":198},"opening = 20000.00\nbook_balance = opening + book[\"Amount\"].sum()\nbank_balance = opening + bank[\"Amount\"].sum()\n\ndeposits = book_open.loc[book_open[\"Amount\"] > 0, \"Amount\"].sum()\ncheques = -book_open.loc[book_open[\"Amount\"] \u003C 0, \"Amount\"].sum()\nbank_items = bank_only[\"Amount\"].sum()\n\nadjusted_bank = bank_balance + deposits - cheques\nadjusted_book = book_balance + bank_items\nstatement = pd.DataFrame({\"Item\": [\"Balance per bank\", \"Add: deposits in transit\", \"Less: outstanding cheques\", \"Adjusted bank balance\",\n                                   \"Balance per cash book\", \"Add\u002Fless: bank-only items\", \"Adjusted book balance\", \"Difference\"],\n                          \"Amount\": [bank_balance, deposits, -cheques, adjusted_bank, book_balance, bank_items, adjusted_book,\n                                     round(adjusted_bank - adjusted_book, 2)]})\nprint(statement.to_string(index=False))\nassert round(adjusted_bank - adjusted_book, 2) == 0\n",[200,1588,1589,1599,1618,1635,1639,1663,1691,1705,1709,1729,1744,1781,1804,1817,1836,1851],{"__ignoreMap":198},[203,1590,1591,1594,1596],{"class":84,"line":205},[203,1592,1593],{"class":255},"opening ",[203,1595,278],{"class":251},[203,1597,1598],{"class":336}," 20000.00\n",[203,1600,1601,1604,1606,1609,1611,1613,1615],{"class":84,"line":265},[203,1602,1603],{"class":255},"book_balance ",[203,1605,278],{"class":251},[203,1607,1608],{"class":255}," opening ",[203,1610,392],{"class":251},[203,1612,895],{"class":255},[203,1614,593],{"class":212},[203,1616,1617],{"class":255},"].sum()\n",[203,1619,1620,1623,1625,1627,1629,1631,1633],{"class":84,"line":272},[203,1621,1622],{"class":255},"bank_balance ",[203,1624,278],{"class":251},[203,1626,1608],{"class":255},[203,1628,392],{"class":251},[203,1630,920],{"class":255},[203,1632,593],{"class":212},[203,1634,1617],{"class":255},[203,1636,1637],{"class":84,"line":305},[203,1638,269],{"emptyLinePlaceholder":268},[203,1640,1641,1644,1646,1649,1651,1653,1655,1657,1659,1661],{"class":84,"line":330},[203,1642,1643],{"class":255},"deposits ",[203,1645,278],{"class":251},[203,1647,1648],{"class":255}," book_open.loc[book_open[",[203,1650,593],{"class":212},[203,1652,530],{"class":255},[203,1654,1429],{"class":251},[203,1656,1432],{"class":336},[203,1658,287],{"class":255},[203,1660,593],{"class":212},[203,1662,1617],{"class":255},[203,1664,1665,1668,1670,1673,1676,1678,1680,1683,1685,1687,1689],{"class":84,"line":401},[203,1666,1667],{"class":255},"cheques ",[203,1669,278],{"class":251},[203,1671,1672],{"class":251}," -",[203,1674,1675],{"class":255},"book_open.loc[book_open[",[203,1677,593],{"class":212},[203,1679,530],{"class":255},[203,1681,1682],{"class":251},"\u003C",[203,1684,1432],{"class":336},[203,1686,287],{"class":255},[203,1688,593],{"class":212},[203,1690,1617],{"class":255},[203,1692,1693,1696,1698,1701,1703],{"class":84,"line":406},[203,1694,1695],{"class":255},"bank_items ",[203,1697,278],{"class":251},[203,1699,1700],{"class":255}," bank_only[",[203,1702,593],{"class":212},[203,1704,1617],{"class":255},[203,1706,1707],{"class":84,"line":419},[203,1708,269],{"emptyLinePlaceholder":268},[203,1710,1711,1714,1716,1719,1721,1724,1726],{"class":84,"line":430},[203,1712,1713],{"class":255},"adjusted_bank ",[203,1715,278],{"class":251},[203,1717,1718],{"class":255}," bank_balance ",[203,1720,392],{"class":251},[203,1722,1723],{"class":255}," deposits ",[203,1725,991],{"class":251},[203,1727,1728],{"class":255}," cheques\n",[203,1730,1731,1734,1736,1739,1741],{"class":84,"line":481},[203,1732,1733],{"class":255},"adjusted_book ",[203,1735,278],{"class":251},[203,1737,1738],{"class":255}," book_balance ",[203,1740,392],{"class":251},[203,1742,1743],{"class":255}," bank_items\n",[203,1745,1746,1749,1751,1754,1757,1760,1763,1765,1768,1770,1773,1775,1778],{"class":84,"line":516},[203,1747,1748],{"class":255},"statement ",[203,1750,278],{"class":251},[203,1752,1753],{"class":255}," pd.DataFrame({",[203,1755,1756],{"class":212},"\"Item\"",[203,1758,1759],{"class":255},": [",[203,1761,1762],{"class":212},"\"Balance per bank\"",[203,1764,287],{"class":255},[203,1766,1767],{"class":212},"\"Add: deposits in transit\"",[203,1769,287],{"class":255},[203,1771,1772],{"class":212},"\"Less: outstanding cheques\"",[203,1774,287],{"class":255},[203,1776,1777],{"class":212},"\"Adjusted bank balance\"",[203,1779,1780],{"class":255},",\n",[203,1782,1783,1786,1788,1791,1793,1796,1798,1801],{"class":84,"line":521},[203,1784,1785],{"class":212},"                                   \"Balance per cash book\"",[203,1787,287],{"class":255},[203,1789,1790],{"class":212},"\"Add\u002Fless: bank-only items\"",[203,1792,287],{"class":255},[203,1794,1795],{"class":212},"\"Adjusted book balance\"",[203,1797,287],{"class":255},[203,1799,1800],{"class":212},"\"Difference\"",[203,1802,1803],{"class":255},"],\n",[203,1805,1806,1809,1812,1814],{"class":84,"line":543},[203,1807,1808],{"class":212},"                          \"Amount\"",[203,1810,1811],{"class":255},": [bank_balance, deposits, ",[203,1813,991],{"class":251},[203,1815,1816],{"class":255},"cheques, adjusted_bank, book_balance, bank_items, adjusted_book,\n",[203,1818,1819,1822,1825,1827,1830,1833],{"class":84,"line":563},[203,1820,1821],{"class":336},"                                     round",[203,1823,1824],{"class":255},"(adjusted_bank ",[203,1826,991],{"class":251},[203,1828,1829],{"class":255}," adjusted_book, ",[203,1831,1832],{"class":336},"2",[203,1834,1835],{"class":255},")]})\n",[203,1837,1838,1840,1843,1845,1847,1849],{"class":84,"line":577},[203,1839,615],{"class":336},[203,1841,1842],{"class":255},"(statement.to_string(",[203,1844,635],{"class":290},[203,1846,278],{"class":251},[203,1848,469],{"class":336},[203,1850,513],{"class":255},[203,1852,1853,1855,1858,1860,1862,1864,1866,1869,1872],{"class":84,"line":612},[203,1854,768],{"class":251},[203,1856,1857],{"class":336}," round",[203,1859,1824],{"class":255},[203,1861,991],{"class":251},[203,1863,1829],{"class":255},[203,1865,1832],{"class":336},[203,1867,1868],{"class":255},") ",[203,1870,1871],{"class":251},"==",[203,1873,1874],{"class":336}," 0\n",[20,1876,29,1882,29,1885,29,1888,29,1893,29,1898,29,1906,29,1910,29,1916,29,1920,29,1924,29,1928,29,1931,29,1933,29,1937,29,1939,29,1942,29,1945,29,1948,29,1950,29,1954,29,1956,29,1959,29,1962,29,1966,29,1968,29,1971,29,1973,29,1976,29,1979,29,1981,29,1983,29,1986,29,1988,29,1991,29,1994,29,1998,29,2001,29,2005,29,2007,29,2010,29,2013,29,2017,29,2019,29,2022,29,2024,29,2027,29,2030,29,2034,29,2036,29,2039,29,2041,29,2044,29,2047,29,2051,29,2053,29,2056,29,2058,29,2060,29,2063,29,2067,29,2069,29,2072,29,2074],{"viewBox":1877,"role":23,"ariaLabelledBy":1878,"xmlns":27,"style":1881},"209 0 342 340",[1879,1880],"rec-statement-t","rec-statement-d","width:100%;max-width:342px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1883,1884],{"id":1879},"The bank reconciliation statement at 30 September 2026",[35,1886,1887],{"id":1880},"The reconciliation statement. Balance per bank 8,327.40, add deposits in transit 7,100.00, less outstanding cheques 380.00, adjusted bank balance 15,047.40. Balance per cash book 15,070.00, less bank-only items net 22.60, adjusted book balance 15,047.40. Difference 0.00.",[39,1889],{"x":1890,"y":42,"width":1891,"height":1892,"fill":45},"209","342","340",[68,1894,1897],{"x":131,"y":1895,"style":1896},"31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Bank reconciliation — 30 September 2026",[39,1899],{"x":1900,"y":1901,"width":1902,"height":1903,"rx":42,"fill":1904,"stroke":1905,"style":1229},"225.4","48.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)",[39,1907],{"x":1908,"y":1901,"width":1909,"height":1903,"rx":42,"fill":1904,"stroke":1905,"style":1229},"257.4","190.2",[68,1911,1915],{"x":1912,"y":1913,"style":1914},"352.5","65.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[39,1917],{"x":1918,"y":1901,"width":1919,"height":1903,"rx":42,"fill":1904,"stroke":1905,"style":1229},"447.6","87.0",[68,1921,1923],{"x":1922,"y":1913,"style":1914},"491.1","B",[39,1925],{"x":1900,"y":1926,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"72.0","28.0",[68,1929,381],{"x":1930,"y":1227,"style":1914},"241.4",[39,1932],{"x":1908,"y":1926,"width":1909,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1934,1936],{"x":1912,"y":1227,"style":1935},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Item",[39,1938],{"x":1918,"y":1926,"width":1919,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1940,1941],{"x":1922,"y":1227,"style":1935},"Amount",[39,1943],{"x":1900,"y":1944,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"100.0",[68,1946,1832],{"x":1930,"y":1947,"style":1914},"119.0",[39,1949],{"x":1908,"y":1944,"width":1909,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1951,1953],{"x":1912,"y":1947,"style":1952},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Balance per bank",[39,1955],{"x":1918,"y":1944,"width":1919,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1957,1958],{"x":1922,"y":1947,"style":1952},"8,327.40",[39,1960],{"x":1900,"y":1961,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"128.0",[68,1963,1965],{"x":1930,"y":1964,"style":1914},"147.0","3",[39,1967],{"x":1908,"y":1961,"width":1909,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1969,1970],{"x":1912,"y":1964,"style":1952},"Add: deposits in transit",[39,1972],{"x":1918,"y":1961,"width":1919,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1974,1975],{"x":1922,"y":1964,"style":1952},"7,100.00",[39,1977],{"x":1900,"y":1978,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"156.0",[68,1980,41],{"x":1930,"y":1206,"style":1914},[39,1982],{"x":1908,"y":1978,"width":1909,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1984,1985],{"x":1912,"y":1206,"style":1952},"Less: outstanding cheques",[39,1987],{"x":1918,"y":1978,"width":1919,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,1989,1990],{"x":1922,"y":1206,"style":1952},"(380.00)",[39,1992],{"x":1900,"y":1993,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"184.0",[68,1995,1997],{"x":1930,"y":1996,"style":1914},"203.0","5",[39,1999],{"x":1908,"y":1993,"width":1909,"height":1927,"rx":42,"fill":1264,"stroke":1265,"style":2000},"stroke-width:1.5px",[68,2002,2004],{"x":1912,"y":1996,"style":2003},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Adjusted bank balance",[39,2006],{"x":1918,"y":1993,"width":1919,"height":1927,"rx":42,"fill":1264,"stroke":1265,"style":2000},[68,2008,2009],{"x":1922,"y":1996,"style":2003},"15,047.40",[39,2011],{"x":1900,"y":2012,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"212.0",[68,2014,2016],{"x":1930,"y":2015,"style":1914},"231.0","6",[39,2018],{"x":1908,"y":2012,"width":1909,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,2020,2021],{"x":1912,"y":2015,"style":1952},"Balance per cash book",[39,2023],{"x":1918,"y":2012,"width":1919,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,2025,2026],{"x":1922,"y":2015,"style":1952},"15,070.00",[39,2028],{"x":1900,"y":2029,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"240.0",[68,2031,2033],{"x":1930,"y":2032,"style":1914},"259.0","7",[39,2035],{"x":1908,"y":2029,"width":1909,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,2037,2038],{"x":1912,"y":2032,"style":1952},"Add\u002Fless: bank-only items",[39,2040],{"x":1918,"y":2029,"width":1919,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,2042,2043],{"x":1922,"y":2032,"style":1952},"(22.60)",[39,2045],{"x":1900,"y":2046,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"268.0",[68,2048,2050],{"x":1930,"y":2049,"style":1914},"287.0","8",[39,2052],{"x":1908,"y":2046,"width":1909,"height":1927,"rx":42,"fill":1264,"stroke":1265,"style":2000},[68,2054,2055],{"x":1912,"y":2049,"style":2003},"Adjusted book balance",[39,2057],{"x":1918,"y":2046,"width":1919,"height":1927,"rx":42,"fill":1264,"stroke":1265,"style":2000},[68,2059,2009],{"x":1922,"y":2049,"style":2003},[39,2061],{"x":1900,"y":2062,"width":1902,"height":1927,"rx":42,"fill":1904,"stroke":1905,"style":1229},"296.0",[68,2064,2066],{"x":1930,"y":2065,"style":1914},"315.0","9",[39,2068],{"x":1908,"y":2062,"width":1909,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,2070,2071],{"x":1912,"y":2065,"style":1952},"Difference",[39,2073],{"x":1918,"y":2062,"width":1919,"height":1927,"rx":42,"fill":45,"stroke":1905,"style":1229},[68,2075,2076],{"x":1922,"y":2065,"style":1952},"0.00",[10,2078,2079],{},"Both sides come to 15,047.40. A non-zero difference means a line has been mismatched or misclassified, or a balance is wrong — the report must not be signed off until it is explained.",[188,2081,2083],{"id":2082},"step-6-write-the-reconciliation-workbook","Step 6 — Write the reconciliation workbook",[10,2085,2086],{},"Write the statement, the matched pairs with the rule that matched each one, and the open items, so a reviewer can check any match and follow up every exception:",[193,2088,2090],{"className":242,"code":2089,"language":244,"meta":198,"style":198},"with pd.ExcelWriter(\"bank_rec_2026-09-30.xlsx\", engine=\"xlsxwriter\") as writer:\n    statement.to_excel(writer, sheet_name=\"Reconciliation\", index=False)\n    matches.merge(book[[\"Ref\", \"Date\", \"Description\"]], on=\"Ref\").merge(\n        bank[[\"Line\", \"Date\", \"Narrative\"]], on=\"Line\", suffixes=(\" book\", \" bank\")).to_excel(writer, sheet_name=\"Matched\", index=False)\n    pd.concat([book_open.rename(columns={\"Ref\": \"Item\"}), bank_only.rename(columns={\"Line\": \"Item\", \"Narrative\": \"Description\"})])[\n        [\"Item\", \"Date\", \"Description\", \"Amount\", \"Category\", \"Investigate\"]].to_excel(writer, sheet_name=\"Open items\", index=False)\n    money = writer.book.add_format({\"num_format\": \"#,##0.00;(#,##0.00);-\"})\n    writer.sheets[\"Reconciliation\"].set_column(\"A:A\", 28)\n    writer.sheets[\"Reconciliation\"].set_column(\"B:B\", 14, money)\nprint(pd.ExcelFile(\"bank_rec_2026-09-30.xlsx\").sheet_names)\n",[200,2091,2092,2120,2143,2170,2229,2272,2319,2339,2359,2377],{"__ignoreMap":198},[203,2093,2094,2097,2100,2103,2105,2108,2110,2113,2115,2117],{"class":84,"line":205},[203,2095,2096],{"class":251},"with",[203,2098,2099],{"class":255}," pd.ExcelWriter(",[203,2101,2102],{"class":212},"\"bank_rec_2026-09-30.xlsx\"",[203,2104,287],{"class":255},[203,2106,2107],{"class":290},"engine",[203,2109,278],{"class":251},[203,2111,2112],{"class":212},"\"xlsxwriter\"",[203,2114,1868],{"class":255},[203,2116,259],{"class":251},[203,2118,2119],{"class":255}," writer:\n",[203,2121,2122,2125,2128,2130,2133,2135,2137,2139,2141],{"class":84,"line":265},[203,2123,2124],{"class":255},"    statement.to_excel(writer, ",[203,2126,2127],{"class":290},"sheet_name",[203,2129,278],{"class":251},[203,2131,2132],{"class":212},"\"Reconciliation\"",[203,2134,287],{"class":255},[203,2136,635],{"class":290},[203,2138,278],{"class":251},[203,2140,469],{"class":336},[203,2142,609],{"class":255},[203,2144,2145,2148,2150,2152,2154,2156,2158,2161,2163,2165,2167],{"class":84,"line":272},[203,2146,2147],{"class":255},"    matches.merge(book[[",[203,2149,621],{"class":212},[203,2151,287],{"class":255},[203,2153,299],{"class":212},[203,2155,287],{"class":255},[203,2157,538],{"class":212},[203,2159,2160],{"class":255},"]], ",[203,2162,733],{"class":290},[203,2164,278],{"class":251},[203,2166,621],{"class":212},[203,2168,2169],{"class":255},").merge(\n",[203,2171,2172,2175,2177,2179,2181,2183,2185,2187,2189,2191,2193,2195,2197,2199,2201,2204,2206,2209,2212,2214,2216,2219,2221,2223,2225,2227],{"class":84,"line":305},[203,2173,2174],{"class":255},"        bank[[",[203,2176,341],{"class":212},[203,2178,287],{"class":255},[203,2180,299],{"class":212},[203,2182,287],{"class":255},[203,2184,558],{"class":212},[203,2186,2160],{"class":255},[203,2188,733],{"class":290},[203,2190,278],{"class":251},[203,2192,341],{"class":212},[203,2194,287],{"class":255},[203,2196,749],{"class":290},[203,2198,278],{"class":251},[203,2200,378],{"class":255},[203,2202,2203],{"class":212},"\" book\"",[203,2205,287],{"class":255},[203,2207,2208],{"class":212},"\" bank\"",[203,2210,2211],{"class":255},")).to_excel(writer, ",[203,2213,2127],{"class":290},[203,2215,278],{"class":251},[203,2217,2218],{"class":212},"\"Matched\"",[203,2220,287],{"class":255},[203,2222,635],{"class":290},[203,2224,278],{"class":251},[203,2226,469],{"class":336},[203,2228,609],{"class":255},[203,2230,2231,2234,2236,2238,2240,2242,2244,2246,2249,2251,2253,2255,2257,2259,2261,2263,2265,2267,2269],{"class":84,"line":330},[203,2232,2233],{"class":255},"    pd.concat([book_open.rename(",[203,2235,821],{"class":290},[203,2237,278],{"class":251},[203,2239,354],{"class":255},[203,2241,621],{"class":212},[203,2243,830],{"class":255},[203,2245,1756],{"class":212},[203,2247,2248],{"class":255},"}), bank_only.rename(",[203,2250,821],{"class":290},[203,2252,278],{"class":251},[203,2254,354],{"class":255},[203,2256,341],{"class":212},[203,2258,830],{"class":255},[203,2260,1756],{"class":212},[203,2262,287],{"class":255},[203,2264,558],{"class":212},[203,2266,830],{"class":255},[203,2268,538],{"class":212},[203,2270,2271],{"class":255},"})])[\n",[203,2273,2274,2277,2279,2281,2283,2285,2287,2289,2291,2293,2295,2297,2299,2302,2304,2306,2309,2311,2313,2315,2317],{"class":84,"line":401},[203,2275,2276],{"class":255},"        [",[203,2278,1756],{"class":212},[203,2280,287],{"class":255},[203,2282,299],{"class":212},[203,2284,287],{"class":255},[203,2286,538],{"class":212},[203,2288,287],{"class":255},[203,2290,593],{"class":212},[203,2292,287],{"class":255},[203,2294,1399],{"class":212},[203,2296,287],{"class":255},[203,2298,1470],{"class":212},[203,2300,2301],{"class":255},"]].to_excel(writer, ",[203,2303,2127],{"class":290},[203,2305,278],{"class":251},[203,2307,2308],{"class":212},"\"Open items\"",[203,2310,287],{"class":255},[203,2312,635],{"class":290},[203,2314,278],{"class":251},[203,2316,469],{"class":336},[203,2318,609],{"class":255},[203,2320,2321,2324,2326,2329,2332,2334,2337],{"class":84,"line":406},[203,2322,2323],{"class":255},"    money ",[203,2325,278],{"class":251},[203,2327,2328],{"class":255}," writer.book.add_format({",[203,2330,2331],{"class":212},"\"num_format\"",[203,2333,830],{"class":255},[203,2335,2336],{"class":212},"\"#,##0.00;(#,##0.00);-\"",[203,2338,1142],{"class":255},[203,2340,2341,2344,2346,2349,2352,2354,2357],{"class":84,"line":419},[203,2342,2343],{"class":255},"    writer.sheets[",[203,2345,2132],{"class":212},[203,2347,2348],{"class":255},"].set_column(",[203,2350,2351],{"class":212},"\"A:A\"",[203,2353,287],{"class":255},[203,2355,2356],{"class":336},"28",[203,2358,609],{"class":255},[203,2360,2361,2363,2365,2367,2370,2372,2374],{"class":84,"line":430},[203,2362,2343],{"class":255},[203,2364,2132],{"class":212},[203,2366,2348],{"class":255},[203,2368,2369],{"class":212},"\"B:B\"",[203,2371,287],{"class":255},[203,2373,1207],{"class":336},[203,2375,2376],{"class":255},", money)\n",[203,2378,2379,2381,2384,2386],{"class":84,"line":481},[203,2380,615],{"class":336},[203,2382,2383],{"class":255},"(pd.ExcelFile(",[203,2385,2102],{"class":212},[203,2387,2388],{"class":255},").sheet_names)\n",[10,2390,2391],{},"Next month, carry the open items forward: outstanding cheques and deposits in transit from September should clear in October, and anything still open after that is an exception in its own right.",[188,2393,2395],{"id":2394},"common-pitfalls","Common pitfalls",[2397,2398,2399,2406,2412,2418,2424,2430],"ul",{},[2400,2401,2402,2405],"li",{},[872,2403,2404],{},"Many-to-many matches."," Merging on amount alone pairs lines repeatedly; accept each pair once.",[2400,2407,2408,2411],{},[872,2409,2410],{},"Loose tolerances."," A wide date window matches unrelated payments of the same amount; keep it tight and let people handle the rest.",[2400,2413,2414,2417],{},[872,2415,2416],{},"Matching on float amounts."," Convert to integer cents before merging.",[2400,2419,2420,2423],{},[872,2421,2422],{},"Sign conventions."," Some banks export debits as positive with a separate type column; normalise to one signed amount first.",[2400,2425,2426,2429],{},[872,2427,2428],{},"Ignoring old open items."," A deposit in transit for three weeks is a problem, not timing.",[2400,2431,2432,2435],{},[872,2433,2434],{},"Forcing a zero difference."," Never add a balancing figure; find the line that explains it.",[188,2437,2439],{"id":2438},"performance-notes","Performance notes",[10,2441,2442],{},"The candidate merge in pass two grows with the number of lines sharing each amount, which stays small for real statements. For tens of thousands of lines per month, restrict candidates by date window before merging, or match within each day's lines.",[188,2444,2446],{"id":2445},"conclusion","Conclusion",[10,2448,2449],{},"Extract cheque and invoice references from both sides, match on reference and amount first, then on amount within a few days, accepting each pair only once and closest date first. Classify what remains as outstanding cheques, deposits in transit and bank-only items, flag anything older than the clearing time, and prove that the adjusted bank and book balances agree before writing the statement, the matches and the open items.",[188,2451,2453],{"id":2452},"frequently-asked-questions","Frequently asked questions",[10,2455,2456,2459],{},[872,2457,2458],{},"How do I match bank transactions to the ledger in Python?","\nIn passes. First match on something both sides share — a cheque number or invoice reference extracted with a regular expression — plus the amount. Then match what is left on equal amount within a few days, pairing each line at most once and preferring the closest date.",[10,2461,2462,2465],{},[872,2463,2464],{},"Why must bank matching be one to one?","\nA merge on amount pairs every bank line with every ledger entry of the same amount. Two receipts of 4,100 would both match one bank credit, hiding the fact that one of them has not reached the bank. Pair each line once, closest date first, and leave the rest unmatched.",[10,2467,2468,2471],{},[872,2469,2470],{},"What goes on a bank reconciliation statement?","\nThe bank balance, plus deposits in transit, less outstanding cheques, gives the adjusted bank balance. The ledger balance, plus or minus items only the bank has recorded — interest, charges — gives the adjusted book balance. The two must be equal.",[10,2473,2474,2477],{},[872,2475,2476],{},"Which unmatched items need attention?","\nItems that have been unmatched for longer than the normal clearing time — a deposit recorded a week ago that the bank has not received, a cheque months old. Recent items at month end are expected; old ones may be errors, duplicates or lost payments.",[188,2479,2481],{"id":2480},"related","Related",[2397,2483,2484,2490,2497,2504],{},[2400,2485,2486,2487,2489],{},"Up one level: ",[14,2488,17],{"href":16}," — the pipeline these reports share.",[2400,2491,2492,2496],{},[14,2493,2495],{"href":2494},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-accounts-receivable-aging-report\u002F","Build an Accounts Receivable Aging Report"," — the receivables side.",[2400,2498,2499,2503],{},[14,2500,2502],{"href":2501},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcompare-two-excel-files-for-differences-with-python\u002F","Compare Two Excel Files for Differences with Python"," — general-purpose comparison.",[2400,2505,2506,2510],{},[14,2507,2509],{"href":2508},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Freconcile-excel-totals-with-control-totals\u002F","Reconcile Excel Totals with Control Totals"," — tie-outs in general.",[2512,2513,2514],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":198,"searchDepth":265,"depth":265,"links":2516},[2517,2518,2519,2520,2521,2522,2523,2524,2525,2526,2527,2528],{"id":190,"depth":265,"text":191},{"id":235,"depth":265,"text":236},{"id":688,"depth":265,"text":689},{"id":866,"depth":265,"text":867},{"id":1308,"depth":265,"text":1309},{"id":1579,"depth":265,"text":1580},{"id":2082,"depth":265,"text":2083},{"id":2394,"depth":265,"text":2395},{"id":2438,"depth":265,"text":2439},{"id":2445,"depth":265,"text":2446},{"id":2452,"depth":265,"text":2453},{"id":2480,"depth":265,"text":2481},"2026-09-25","Match a month of bank statement lines to the cash book: first on cheque numbers and invoice references, then on amount within a few days, one to one. List what is left — outstanding cheques, deposits in transit, bank charges and interest — flag old unmatched items, and prove the adjusted bank and book balances agree.","md",[2533,2535,2537,2539],{"q":2458,"a":2534},"In passes. First match on something both sides share — a cheque number or invoice reference extracted with a regular expression — plus the amount. Then match what is left on equal amount within a few days, pairing each line at most once and preferring the closest date.",{"q":2464,"a":2536},"A merge on amount pairs every bank line with every ledger entry of the same amount. Two receipts of 4,100 would both match one bank credit, hiding the fact that one of them has not reached the bank. Pair each line once, closest date first, and leave the rest unmatched.",{"q":2470,"a":2538},"The bank balance, plus deposits in transit, less outstanding cheques, gives the adjusted bank balance. The ledger balance, plus or minus items only the bank has recorded — interest, charges — gives the adjusted book balance. The two must be equal.",{"q":2476,"a":2540},"Items that have been unmatched for longer than the normal clearing time — a deposit recorded a week ago that the bank has not received, a cheque months old. Recent items at month end are expected; old ones may be errors, duplicates or lost payments.","build a bank reconciliation report",null,{"breadcrumb":2544},[2545,2548,2551],{"name":2546,"item":2547},"Home","\u002F",{"name":2549,"item":2550},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":2552,"item":16},"Building Finance and Operations Reports in Excel with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report",{"title":2555,"description":2556},"Bank Reconciliation in Excel with Python (Automated Matching)","Automate a bank reconciliation with pandas: extract cheque and invoice references with regex, match on reference then amount and date tolerance one to one, outstanding cheques, deposits in transit, bank-only items, a reconciliation statement and stale-item flags.","build-a-bank-reconciliation-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report\u002Findex",[2560,2561,2562],"report-layouts","joins-and-lookups","pandas","how-to","HpJKNZFRJhJrG1WNKnty6S5dg6YLQz7mU-F8EdclX5M",[2566,2570],{"title":2567,"path":2568,"stem":2569,"children":-1},"Allocate Shared Costs Across Departments with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fallocate-shared-costs-across-departments","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fallocate-shared-costs-across-departments\u002Findex",{"title":2571,"path":2572,"stem":2573,"children":-1},"Build a Bill of Materials Cost Rollup","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bill-of-materials-cost-rollup","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bill-of-materials-cost-rollup\u002Findex",[2575,2577],{"path":2576,"title":2552},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports",{"path":2578,"title":2549},"\u002Fautomating-reporting-workflows",[],[2581,2585,2588,2591,2594,2597],{"path":2582,"title":2583,"kind":2584},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards","Building Multi-Sheet Excel Dashboards","guide",{"path":2586,"title":2587,"kind":2563},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-data-dictionary-sheet-to-an-excel-report","Add a Data Dictionary Sheet to an Excel Report",{"path":2589,"title":2590,"kind":2563},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-kpi-summary-block-to-an-excel-dashboard","Add a KPI Summary Block to an Excel Dashboard",{"path":2592,"title":2593,"kind":2563},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel","Add a Table of Contents Sheet with Hyperlinks in Excel",{"path":2595,"title":2596,"kind":2563},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard","Add a Dropdown Filter That Drives an Excel Dashboard",{"path":2598,"title":2599,"kind":2563},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python","Add a Summary Sheet to an Excel Report with Python",1790972119214]