[{"data":1,"prerenderedAt":2268},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references":2234,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references":2243,"children:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references":2248,"related:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references":2249},{"id":4,"title":5,"body":6,"dateModified":2199,"datePublished":2199,"description":2200,"extension":2201,"faq":2202,"keyword":2211,"kind":2212,"meta":2213,"navigation":262,"path":2222,"seo":2223,"slug":2226,"stem":2227,"topics":2228,"type":2232,"__hash__":2233},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references\u002Findex.md","Sum the Same Cell Across Many Sheets with 3D References from Python",{"type":7,"value":8,"toc":2186},"minimark",[9,37,178,183,211,214,218,221,712,719,723,746,1011,1018,1266,1270,1284,1373,1383,1460,1464,1479,1745,1748,1752,1755,1917,1920,1936,2041,2048,2052,2091,2095,2103,2107,2121,2125,2131,2137,2143,2149,2153,2182],[10,11,12,13,17,18,22,23,26,27,30,31,36],"p",{},"Monthly workbooks often follow one pattern: a tab per month with an identical layout, and a summary tab that adds them up. In Excel the summary does not need twelve additions — a ",[14,15,16],"em",{},"3D reference"," such as ",[19,20,21],"code",{},"=SUM(Jan:Dec!B2)"," adds cell B2 across every sheet from ",[19,24,25],{},"Jan"," to ",[19,28,29],{},"Dec"," in the tab order. When Python generates the monthly tabs, it can write the summary formulas too, so the workbook stays live: someone corrects a figure on the March tab and the summary updates in Excel without re-running the script. This guide, part of ",[32,33,35],"a",{"href":34},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002F","Working with Multiple Excel Sheets in Python",", builds that workbook with openpyxl and covers the rules that make 3D references reliable.",[38,39,47,48,47,52,47,56,47,64,47,74,47,85,47,92,47,97,47,105,47,112,47,118,47,123,47,126,47,131,47,134,47,139,47,142,47,146,47,149,47,151,47,154,47,159,47,163,47,167,47,170,47,172],"svg",{"viewBox":40,"role":41,"ariaLabelledBy":42,"xmlns":45,"style":46},"4 0 752 358","img",[43,44],"threed-fan-t","threed-fan-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  ",[49,50,51],"title",{"id":43},"A summary cell that adds the same cell on every month sheet",[53,54,55],"desc",{"id":44},"Three month sheets, Jan, Feb and Mar, each hold a value in cell B2. The summary sheet's B2 contains =SUM(Jan:Mar!B2), which adds the B2 cell of every sheet from Jan to Mar in tab order.",[57,58],"rect",{"x":59,"y":60,"width":61,"height":62,"fill":63},"4","0","752","358","#ffffff",[57,65],{"x":66,"y":67,"width":68,"height":69,"rx":70,"fill":71,"stroke":72,"style":73},"520.0","119.5","220.0","93.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[75,76,78],"g",{"transform":77},"translate(618.0,132.5) scale(1.000)",[79,80],"path",{"d":81,"fill":82,"stroke":83,"style":84},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[86,87,91],"text",{"x":88,"y":89,"style":90},"630.0","179.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Summary!B2",[86,93,96],{"x":88,"y":94,"style":95},"198.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","=SUM(Jan:Mar!B2)",[57,98],{"x":99,"y":100,"width":101,"height":102,"rx":70,"fill":103,"stroke":104,"style":73},"20.0","16.0","250.0","92.0","#e7ebef","var(--line,#cdd5e6)",[75,106,108],{"transform":107},"translate(133.0,29.0) scale(1.000)",[79,109],{"d":110,"fill":82,"stroke":111,"style":84},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","var(--text,#172033)",[86,113,117],{"x":114,"y":115,"style":116},"145.0","75.0","font-size:13px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Jan!B2",[86,119,122],{"x":114,"y":120,"style":121},"94.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","12,400",[79,124],{"d":125,"fill":82,"stroke":72,"style":73},"M274.0,62.0 C394.0,62.0 394.0,166.0 510.0,166.0",[127,128],"polygon",{"points":129,"fill":130},"514.0,166.0 505.0,171.0 505.0,161.0","#5b5cf0",[57,132],{"x":99,"y":133,"width":101,"height":102,"rx":70,"fill":103,"stroke":104,"style":73},"120.0",[75,135,137],{"transform":136},"translate(133.0,133.0) scale(1.000)",[79,138],{"d":110,"fill":82,"stroke":111,"style":84},[86,140,141],{"x":114,"y":89,"style":116},"Feb!B2",[86,143,145],{"x":114,"y":144,"style":121},"198.0","11,950",[79,147],{"d":148,"fill":82,"stroke":72,"style":73},"M274.0,166.0 C394.0,166.0 394.0,166.0 510.0,166.0",[127,150],{"points":129,"fill":130},[57,152],{"x":99,"y":153,"width":101,"height":102,"rx":70,"fill":103,"stroke":104,"style":73},"224.0",[75,155,157],{"transform":156},"translate(133.0,237.0) scale(1.000)",[79,158],{"d":110,"fill":82,"stroke":111,"style":84},[86,160,162],{"x":114,"y":161,"style":116},"283.0","Mar!B2",[86,164,166],{"x":114,"y":165,"style":121},"302.0","13,210",[79,168],{"d":169,"fill":82,"stroke":72,"style":73},"M274.0,270.0 C394.0,270.0 394.0,166.0 510.0,166.0",[127,171],{"points":129,"fill":130},[86,173,177],{"x":174,"y":175,"style":176},"380.0","338.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","the formula names only the first and last sheet — every tab between them is included",[179,180,182],"h2",{"id":181},"prerequisites","Prerequisites",[184,185,190],"pre",{"className":186,"code":187,"language":188,"meta":189,"style":189},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas\n","bash","",[19,191,192],{"__ignoreMap":189},[193,194,197,201,205,208],"span",{"class":195,"line":196},"line",1,[193,198,200],{"class":199},"sMTad","pip",[193,202,204],{"class":203},"srMev"," install",[193,206,207],{"class":203}," openpyxl",[193,209,210],{"class":203}," pandas\n",[10,212,213],{},"LibreOffice is optional; it is used at the end to recalculate the workbook and check the totals without Excel.",[179,215,217],{"id":216},"step-1-write-identical-month-sheets","Step 1 — Write identical month sheets",[10,219,220],{},"3D references only make sense when every sheet has the same layout, so generate the month tabs from one function. Each gets the same labels in column A and that month's figures in column B:",[184,222,226],{"className":223,"code":224,"language":225,"meta":189,"style":189},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook, load_workbook\nfrom openpyxl.styles import Font\n\nLINES = [\"Revenue\", \"Cost of sales\", \"Gross margin\", \"Overheads\", \"Operating profit\"]\nMONTHS = {\n    \"Jan\": [12400, 7100, None, 2900, None],\n    \"Feb\": [11950, 6800, None, 2950, None],\n    \"Mar\": [13210, 7450, None, 3010, None],\n}\n\ndef month_sheet(wb, name, values, index=None):\n    ws = wb.create_sheet(name, index)\n    ws[\"A1\"], ws[\"B1\"] = \"Line\", name\n    ws[\"A1\"].font = ws[\"B1\"].font = Font(bold=True)\n    for row, (label, value) in enumerate(zip(LINES, values), start=2):\n        ws.cell(row, 1, label)\n        ws.cell(row, 2, value)\n    ws[\"B4\"] = \"=B2-B3\"          # gross margin\n    ws[\"B6\"] = \"=B4-B5\"          # operating profit\n    ws.column_dimensions[\"A\"].width = 20\n    return ws\n\nwb = Workbook()\nsummary = wb.active\nsummary.title = \"Summary\"\nfor name, values in MONTHS.items():\n    month_sheet(wb, name, values)\nprint(wb.sheetnames)    # ['Summary', 'Jan', 'Feb', 'Mar']\n","python",[19,227,228,244,257,264,304,315,349,380,411,417,422,443,454,480,516,554,566,576,595,613,630,639,644,655,666,677,694,700],{"__ignoreMap":189},[193,229,230,234,238,241],{"class":195,"line":196},[193,231,233],{"class":232},"s-kum","from",[193,235,237],{"class":236},"skGVy"," openpyxl ",[193,239,240],{"class":232},"import",[193,242,243],{"class":236}," Workbook, load_workbook\n",[193,245,247,249,252,254],{"class":195,"line":246},2,[193,248,233],{"class":232},[193,250,251],{"class":236}," openpyxl.styles ",[193,253,240],{"class":232},[193,255,256],{"class":236}," Font\n",[193,258,260],{"class":195,"line":259},3,[193,261,263],{"emptyLinePlaceholder":262},true,"\n",[193,265,267,271,274,277,280,283,286,288,291,293,296,298,301],{"class":195,"line":266},4,[193,268,270],{"class":269},"sP0c6","LINES",[193,272,273],{"class":232}," =",[193,275,276],{"class":236}," [",[193,278,279],{"class":203},"\"Revenue\"",[193,281,282],{"class":236},", ",[193,284,285],{"class":203},"\"Cost of sales\"",[193,287,282],{"class":236},[193,289,290],{"class":203},"\"Gross margin\"",[193,292,282],{"class":236},[193,294,295],{"class":203},"\"Overheads\"",[193,297,282],{"class":236},[193,299,300],{"class":203},"\"Operating profit\"",[193,302,303],{"class":236},"]\n",[193,305,307,310,312],{"class":195,"line":306},5,[193,308,309],{"class":269},"MONTHS",[193,311,273],{"class":232},[193,313,314],{"class":236}," {\n",[193,316,318,321,324,327,329,332,334,337,339,342,344,346],{"class":195,"line":317},6,[193,319,320],{"class":203},"    \"Jan\"",[193,322,323],{"class":236},": [",[193,325,326],{"class":269},"12400",[193,328,282],{"class":236},[193,330,331],{"class":269},"7100",[193,333,282],{"class":236},[193,335,336],{"class":269},"None",[193,338,282],{"class":236},[193,340,341],{"class":269},"2900",[193,343,282],{"class":236},[193,345,336],{"class":269},[193,347,348],{"class":236},"],\n",[193,350,352,355,357,360,362,365,367,369,371,374,376,378],{"class":195,"line":351},7,[193,353,354],{"class":203},"    \"Feb\"",[193,356,323],{"class":236},[193,358,359],{"class":269},"11950",[193,361,282],{"class":236},[193,363,364],{"class":269},"6800",[193,366,282],{"class":236},[193,368,336],{"class":269},[193,370,282],{"class":236},[193,372,373],{"class":269},"2950",[193,375,282],{"class":236},[193,377,336],{"class":269},[193,379,348],{"class":236},[193,381,383,386,388,391,393,396,398,400,402,405,407,409],{"class":195,"line":382},8,[193,384,385],{"class":203},"    \"Mar\"",[193,387,323],{"class":236},[193,389,390],{"class":269},"13210",[193,392,282],{"class":236},[193,394,395],{"class":269},"7450",[193,397,282],{"class":236},[193,399,336],{"class":269},[193,401,282],{"class":236},[193,403,404],{"class":269},"3010",[193,406,282],{"class":236},[193,408,336],{"class":269},[193,410,348],{"class":236},[193,412,414],{"class":195,"line":413},9,[193,415,416],{"class":236},"}\n",[193,418,420],{"class":195,"line":419},10,[193,421,263],{"emptyLinePlaceholder":262},[193,423,425,428,432,435,438,440],{"class":195,"line":424},11,[193,426,427],{"class":232},"def",[193,429,431],{"class":430},"s_Opv"," month_sheet",[193,433,434],{"class":236},"(wb, name, values, index",[193,436,437],{"class":232},"=",[193,439,336],{"class":269},[193,441,442],{"class":236},"):\n",[193,444,446,449,451],{"class":195,"line":445},12,[193,447,448],{"class":236},"    ws ",[193,450,437],{"class":232},[193,452,453],{"class":236}," wb.create_sheet(name, index)\n",[193,455,457,460,463,466,469,472,474,477],{"class":195,"line":456},13,[193,458,459],{"class":236},"    ws[",[193,461,462],{"class":203},"\"A1\"",[193,464,465],{"class":236},"], ws[",[193,467,468],{"class":203},"\"B1\"",[193,470,471],{"class":236},"] ",[193,473,437],{"class":232},[193,475,476],{"class":203}," \"Line\"",[193,478,479],{"class":236},", name\n",[193,481,483,485,487,490,492,495,497,499,501,504,508,510,513],{"class":195,"line":482},14,[193,484,459],{"class":236},[193,486,462],{"class":203},[193,488,489],{"class":236},"].font ",[193,491,437],{"class":232},[193,493,494],{"class":236}," ws[",[193,496,468],{"class":203},[193,498,489],{"class":236},[193,500,437],{"class":232},[193,502,503],{"class":236}," Font(",[193,505,507],{"class":506},"sa561","bold",[193,509,437],{"class":232},[193,511,512],{"class":269},"True",[193,514,515],{"class":236},")\n",[193,517,519,522,525,528,531,534,537,539,541,544,547,549,552],{"class":195,"line":518},15,[193,520,521],{"class":232},"    for",[193,523,524],{"class":236}," row, (label, value) ",[193,526,527],{"class":232},"in",[193,529,530],{"class":269}," enumerate",[193,532,533],{"class":236},"(",[193,535,536],{"class":269},"zip",[193,538,533],{"class":236},[193,540,270],{"class":269},[193,542,543],{"class":236},", values), ",[193,545,546],{"class":506},"start",[193,548,437],{"class":232},[193,550,551],{"class":269},"2",[193,553,442],{"class":236},[193,555,557,560,563],{"class":195,"line":556},16,[193,558,559],{"class":236},"        ws.cell(row, ",[193,561,562],{"class":269},"1",[193,564,565],{"class":236},", label)\n",[193,567,569,571,573],{"class":195,"line":568},17,[193,570,559],{"class":236},[193,572,551],{"class":269},[193,574,575],{"class":236},", value)\n",[193,577,579,581,584,586,588,591],{"class":195,"line":578},18,[193,580,459],{"class":236},[193,582,583],{"class":203},"\"B4\"",[193,585,471],{"class":236},[193,587,437],{"class":232},[193,589,590],{"class":203}," \"=B2-B3\"",[193,592,594],{"class":593},"s-wDw","          # gross margin\n",[193,596,598,600,603,605,607,610],{"class":195,"line":597},19,[193,599,459],{"class":236},[193,601,602],{"class":203},"\"B6\"",[193,604,471],{"class":236},[193,606,437],{"class":232},[193,608,609],{"class":203}," \"=B4-B5\"",[193,611,612],{"class":593},"          # operating profit\n",[193,614,616,619,622,625,627],{"class":195,"line":615},20,[193,617,618],{"class":236},"    ws.column_dimensions[",[193,620,621],{"class":203},"\"A\"",[193,623,624],{"class":236},"].width ",[193,626,437],{"class":232},[193,628,629],{"class":269}," 20\n",[193,631,633,636],{"class":195,"line":632},21,[193,634,635],{"class":232},"    return",[193,637,638],{"class":236}," ws\n",[193,640,642],{"class":195,"line":641},22,[193,643,263],{"emptyLinePlaceholder":262},[193,645,647,650,652],{"class":195,"line":646},23,[193,648,649],{"class":236},"wb ",[193,651,437],{"class":232},[193,653,654],{"class":236}," Workbook()\n",[193,656,658,661,663],{"class":195,"line":657},24,[193,659,660],{"class":236},"summary ",[193,662,437],{"class":232},[193,664,665],{"class":236}," wb.active\n",[193,667,669,672,674],{"class":195,"line":668},25,[193,670,671],{"class":236},"summary.title ",[193,673,437],{"class":232},[193,675,676],{"class":203}," \"Summary\"\n",[193,678,680,683,686,688,691],{"class":195,"line":679},26,[193,681,682],{"class":232},"for",[193,684,685],{"class":236}," name, values ",[193,687,527],{"class":232},[193,689,690],{"class":269}," MONTHS",[193,692,693],{"class":236},".items():\n",[193,695,697],{"class":195,"line":696},27,[193,698,699],{"class":236},"    month_sheet(wb, name, values)\n",[193,701,703,706,709],{"class":195,"line":702},28,[193,704,705],{"class":269},"print",[193,707,708],{"class":236},"(wb.sheetnames)    ",[193,710,711],{"class":593},"# ['Summary', 'Jan', 'Feb', 'Mar']\n",[10,713,714,715,718],{},"The month sheets contain their own formulas for the derived lines. That keeps each tab correct on its own and means the summary can add ",[14,716,717],{},"any"," line, derived or not, with the same kind of 3D formula.",[179,720,722],{"id":721},"step-2-add-bookend-sheets-and-write-the-3d-formulas","Step 2 — Add bookend sheets and write the 3D formulas",[10,724,725,726,729,730,733,734,737,738,741,742,745],{},"Referring to ",[19,727,728],{},"Jan:Mar"," works until April arrives, and then every summary formula needs editing. The standard fix is a pair of empty ",[14,731,732],{},"bookend"," sheets, ",[19,735,736],{},"Start"," and ",[19,739,740],{},"End",", placed around the months. Formulas refer to ",[19,743,744],{},"Start:End",", and any sheet dragged or inserted between the bookends is included automatically:",[184,747,749],{"className":223,"code":748,"language":225,"meta":189,"style":189},"start = wb.create_sheet(\"Start\", 1)        # right after Summary\nend = wb.create_sheet(\"End\")               # after the last month\nfor bookend in (start, end):\n    bookend[\"A1\"] = \"Months between Start and End are added into the Summary.\"\n    bookend.sheet_properties.tabColor = \"CDD5E6\"\n\nsummary[\"A1\"], summary[\"B1\"] = \"Line\", \"Quarter total\"\nsummary[\"A1\"].font = summary[\"B1\"].font = Font(bold=True)\nfor row, label in enumerate(LINES, start=2):\n    summary.cell(row, 1, label)\n    summary.cell(row, 2, f\"=SUM(Start:End!B{row})\")\nsummary.column_dimensions[\"A\"].width = 20\nwb.save(\"quarter.xlsx\")\n\nprint(wb.sheetnames)                       # ['Summary', 'Start', 'Jan', 'Feb', 'Mar', 'End']\nprint(load_workbook(\"quarter.xlsx\")[\"Summary\"][\"B2\"].value)   # =SUM(Start:End!B2)\n",[19,750,751,774,792,804,818,828,832,855,884,909,918,947,960,970,974,984],{"__ignoreMap":189},[193,752,753,756,758,761,764,766,768,771],{"class":195,"line":196},[193,754,755],{"class":236},"start ",[193,757,437],{"class":232},[193,759,760],{"class":236}," wb.create_sheet(",[193,762,763],{"class":203},"\"Start\"",[193,765,282],{"class":236},[193,767,562],{"class":269},[193,769,770],{"class":236},")        ",[193,772,773],{"class":593},"# right after Summary\n",[193,775,776,779,781,783,786,789],{"class":195,"line":246},[193,777,778],{"class":236},"end ",[193,780,437],{"class":232},[193,782,760],{"class":236},[193,784,785],{"class":203},"\"End\"",[193,787,788],{"class":236},")               ",[193,790,791],{"class":593},"# after the last month\n",[193,793,794,796,799,801],{"class":195,"line":259},[193,795,682],{"class":232},[193,797,798],{"class":236}," bookend ",[193,800,527],{"class":232},[193,802,803],{"class":236}," (start, end):\n",[193,805,806,809,811,813,815],{"class":195,"line":266},[193,807,808],{"class":236},"    bookend[",[193,810,462],{"class":203},[193,812,471],{"class":236},[193,814,437],{"class":232},[193,816,817],{"class":203}," \"Months between Start and End are added into the Summary.\"\n",[193,819,820,823,825],{"class":195,"line":306},[193,821,822],{"class":236},"    bookend.sheet_properties.tabColor ",[193,824,437],{"class":232},[193,826,827],{"class":203}," \"CDD5E6\"\n",[193,829,830],{"class":195,"line":317},[193,831,263],{"emptyLinePlaceholder":262},[193,833,834,837,839,842,844,846,848,850,852],{"class":195,"line":351},[193,835,836],{"class":236},"summary[",[193,838,462],{"class":203},[193,840,841],{"class":236},"], summary[",[193,843,468],{"class":203},[193,845,471],{"class":236},[193,847,437],{"class":232},[193,849,476],{"class":203},[193,851,282],{"class":236},[193,853,854],{"class":203},"\"Quarter total\"\n",[193,856,857,859,861,863,865,868,870,872,874,876,878,880,882],{"class":195,"line":382},[193,858,836],{"class":236},[193,860,462],{"class":203},[193,862,489],{"class":236},[193,864,437],{"class":232},[193,866,867],{"class":236}," summary[",[193,869,468],{"class":203},[193,871,489],{"class":236},[193,873,437],{"class":232},[193,875,503],{"class":236},[193,877,507],{"class":506},[193,879,437],{"class":232},[193,881,512],{"class":269},[193,883,515],{"class":236},[193,885,886,888,891,893,895,897,899,901,903,905,907],{"class":195,"line":413},[193,887,682],{"class":232},[193,889,890],{"class":236}," row, label ",[193,892,527],{"class":232},[193,894,530],{"class":269},[193,896,533],{"class":236},[193,898,270],{"class":269},[193,900,282],{"class":236},[193,902,546],{"class":506},[193,904,437],{"class":232},[193,906,551],{"class":269},[193,908,442],{"class":236},[193,910,911,914,916],{"class":195,"line":419},[193,912,913],{"class":236},"    summary.cell(row, ",[193,915,562],{"class":269},[193,917,565],{"class":236},[193,919,920,922,924,926,929,932,936,939,942,945],{"class":195,"line":424},[193,921,913],{"class":236},[193,923,551],{"class":269},[193,925,282],{"class":236},[193,927,928],{"class":232},"f",[193,930,931],{"class":203},"\"=SUM(Start:End!B",[193,933,935],{"class":934},"sSjpA","{",[193,937,938],{"class":236},"row",[193,940,941],{"class":934},"}",[193,943,944],{"class":203},")\"",[193,946,515],{"class":236},[193,948,949,952,954,956,958],{"class":195,"line":445},[193,950,951],{"class":236},"summary.column_dimensions[",[193,953,621],{"class":203},[193,955,624],{"class":236},[193,957,437],{"class":232},[193,959,629],{"class":269},[193,961,962,965,968],{"class":195,"line":456},[193,963,964],{"class":236},"wb.save(",[193,966,967],{"class":203},"\"quarter.xlsx\"",[193,969,515],{"class":236},[193,971,972],{"class":195,"line":482},[193,973,263],{"emptyLinePlaceholder":262},[193,975,976,978,981],{"class":195,"line":518},[193,977,705],{"class":269},[193,979,980],{"class":236},"(wb.sheetnames)                       ",[193,982,983],{"class":593},"# ['Summary', 'Start', 'Jan', 'Feb', 'Mar', 'End']\n",[193,985,986,988,991,993,996,999,1002,1005,1008],{"class":195,"line":556},[193,987,705],{"class":269},[193,989,990],{"class":236},"(load_workbook(",[193,992,967],{"class":203},[193,994,995],{"class":236},")[",[193,997,998],{"class":203},"\"Summary\"",[193,1000,1001],{"class":236},"][",[193,1003,1004],{"class":203},"\"B2\"",[193,1006,1007],{"class":236},"].value)   ",[193,1009,1010],{"class":593},"# =SUM(Start:End!B2)\n",[10,1012,1013,1014,1017],{},"The bookends hold nothing that a SUM would pick up — a text note in A1 is ignored by ",[19,1015,1016],{},"SUM"," and does not collide with the B cells being added. A grey tab colour makes them easy to spot and hard to delete by accident.",[38,1019,47,1025,47,1028,47,1031,47,1036,47,1043,47,1048,47,1056,47,1062,47,1067,47,1070,47,1076,47,1081,47,1086,47,1089,47,1095,47,1099,47,1103,47,1106,47,1110,47,1112,47,1116,47,1118,47,1121,47,1124,47,1126,47,1128,47,1132,47,1136,47,1140,47,1143,47,1147,47,1149,47,1152,47,1154,47,1157,47,1160,47,1163,47,1165,47,1168,47,1170,47,1173,47,1176,47,1180,47,1182,47,1185,47,1187,47,1190,47,1193,47,1197,47,1199,47,1202,47,1204,47,1207,47,1212,47,1218,47,1224,47,1228,47,1232,47,1236,47,1239,47,1243,47,1247,47,1251,47,1255,47,1259,47,1262],{"viewBox":1020,"role":41,"ariaLabelledBy":1021,"xmlns":45,"style":1024},"54 0 651 332",[1022,1023],"threed-tabs-t","threed-tabs-d","width:100%;max-width:651px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[49,1026,1027],{"id":1022},"Bookend sheets around the month tabs",[53,1029,1030],{"id":1023},"The tab bar shows Summary, then an empty Start sheet, the Jan, Feb and Mar sheets, and an empty End sheet. The summary formula SUM of Start to End B2 includes every sheet positioned between the two bookends.",[57,1032],{"x":1033,"y":60,"width":1034,"height":1035,"fill":63},"54","651","332",[57,1037],{"x":1038,"y":100,"width":1039,"height":1040,"rx":1041,"fill":103,"stroke":104,"style":1042},"251.9","256.3","30.0","8","stroke-width:1.5px",[57,1044],{"x":1038,"y":1045,"width":1039,"height":1046,"rx":60,"fill":103,"stroke":104,"style":1047},"38.0","8.0","stroke-width:0px",[75,1049,1051],{"transform":1050},"translate(261.9,23.0) scale(0.667)",[79,1052],{"d":1053,"fill":82,"stroke":1054,"style":1055},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","var(--teal-ink,#0b6157)","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[86,1057,1061],{"x":1058,"y":1059,"style":1060},"283.9","36.0","font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:start","quarter.xlsx",[57,1063],{"x":1038,"y":1064,"width":1039,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},"46.0","28.0","stroke-width:1px",[57,1068],{"x":1038,"y":1064,"width":1069,"height":1065,"rx":60,"fill":103,"stroke":104,"style":1066},"40.0",[86,1071,1075],{"x":1072,"y":1073,"style":1074},"271.9","65.0","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","fx",[86,1077,1080],{"x":1078,"y":1073,"style":1079},"301.9","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","=SUM(Start:End!B2)",[57,1082],{"x":1038,"y":1083,"width":1084,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"74.0","32.0","24.0",[57,1087],{"x":1058,"y":1083,"width":1088,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"122.3",[86,1090,1094],{"x":1091,"y":1092,"style":1093},"345.0","91.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[57,1096],{"x":1097,"y":1083,"width":1098,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"406.1","102.0",[86,1100,1102],{"x":1101,"y":1092,"style":1093},"457.1","B",[57,1104],{"x":1038,"y":1105,"width":1084,"height":1065,"rx":60,"fill":103,"stroke":104,"style":1066},"98.0",[86,1107,562],{"x":1108,"y":1109,"style":1093},"267.9","117.0",[57,1111],{"x":1058,"y":1105,"width":1088,"height":1065,"rx":60,"fill":71,"stroke":72,"style":1042},[86,1113,1115],{"x":1091,"y":1109,"style":1114},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Line",[57,1117],{"x":1097,"y":1105,"width":1098,"height":1065,"rx":60,"fill":71,"stroke":72,"style":1042},[86,1119,1120],{"x":1101,"y":1109,"style":1114},"Quarter total",[57,1122],{"x":1038,"y":1123,"width":1084,"height":1065,"rx":60,"fill":103,"stroke":104,"style":1066},"126.0",[86,1125,551],{"x":1108,"y":114,"style":1093},[57,1127],{"x":1058,"y":1123,"width":1088,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1129,1131],{"x":1091,"y":114,"style":1130},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Revenue",[57,1133],{"x":1097,"y":1123,"width":1098,"height":1065,"rx":60,"fill":1134,"stroke":1135,"style":1042},"#d9f4f1","var(--teal,#0f9488)",[86,1137,1139],{"x":1101,"y":114,"style":1138},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","37,560",[57,1141],{"x":1038,"y":1142,"width":1084,"height":1065,"rx":60,"fill":103,"stroke":104,"style":1066},"154.0",[86,1144,1146],{"x":1108,"y":1145,"style":1093},"173.0","3",[57,1148],{"x":1058,"y":1142,"width":1088,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1150,1151],{"x":1091,"y":1145,"style":1130},"Cost of sales",[57,1153],{"x":1097,"y":1142,"width":1098,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1155,1156],{"x":1101,"y":1145,"style":1130},"21,350",[57,1158],{"x":1038,"y":1159,"width":1084,"height":1065,"rx":60,"fill":103,"stroke":104,"style":1066},"182.0",[86,1161,59],{"x":1108,"y":1162,"style":1093},"201.0",[57,1164],{"x":1058,"y":1159,"width":1088,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1166,1167],{"x":1091,"y":1162,"style":1130},"Gross margin",[57,1169],{"x":1097,"y":1159,"width":1098,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1171,1172],{"x":1101,"y":1162,"style":1130},"16,210",[57,1174],{"x":1038,"y":1175,"width":1084,"height":1065,"rx":60,"fill":103,"stroke":104,"style":1066},"210.0",[86,1177,1179],{"x":1108,"y":1178,"style":1093},"229.0","5",[57,1181],{"x":1058,"y":1175,"width":1088,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1183,1184],{"x":1091,"y":1178,"style":1130},"Overheads",[57,1186],{"x":1097,"y":1175,"width":1098,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1188,1189],{"x":1101,"y":1178,"style":1130},"8,860",[57,1191],{"x":1038,"y":1192,"width":1084,"height":1065,"rx":60,"fill":103,"stroke":104,"style":1066},"238.0",[86,1194,1196],{"x":1108,"y":1195,"style":1093},"257.0","6",[57,1198],{"x":1058,"y":1192,"width":1088,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1200,1201],{"x":1091,"y":1195,"style":1130},"Operating profit",[57,1203],{"x":1097,"y":1192,"width":1098,"height":1065,"rx":60,"fill":63,"stroke":104,"style":1066},[86,1205,1206],{"x":1101,"y":1195,"style":1130},"7,350",[57,1208],{"x":1209,"y":1210,"width":1211,"height":1085,"rx":60,"fill":63,"stroke":104,"style":1066},"259.9","266.0","80.7",[195,1213],{"x1":1214,"y1":1215,"x2":1216,"y2":1215,"stroke":1135,"style":1217},"261.9","289.0","338.5","stroke-width:3px",[86,1219,1223],{"x":1220,"y":1221,"style":1222},"300.2","282.5","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Summary",[57,1225],{"x":1226,"y":1210,"width":1227,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"342.5","51.0",[86,1229,736],{"x":1230,"y":1221,"style":1231},"368.0","font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle",[57,1233],{"x":1234,"y":1210,"width":1235,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"395.5","44.8",[86,1237,25],{"x":1238,"y":1221,"style":1231},"417.9",[57,1240],{"x":1241,"y":1210,"width":1242,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"442.3","45.6",[86,1244,1246],{"x":1245,"y":1221,"style":1231},"465.1","Feb",[57,1248],{"x":1249,"y":1210,"width":1250,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"489.9","46.2",[86,1252,1254],{"x":1253,"y":1221,"style":1231},"513.0","Mar",[57,1256],{"x":1257,"y":1210,"width":1258,"height":1085,"rx":60,"fill":103,"stroke":104,"style":1066},"538.1","45.8",[86,1260,740],{"x":1261,"y":1221,"style":1231},"561.0",[86,1263,1265],{"x":174,"y":1264,"style":176},"312.0","new month tabs go between Start and End, and every summary formula includes them with no edits",[179,1267,1269],{"id":1268},"step-3-insert-new-months-in-the-right-position","Step 3 — Insert new months in the right position",[10,1271,1272,1273,1276,1277,1280,1281,1283],{},"Because the reference is positional, ",[14,1274,1275],{},"where"," a new sheet goes decides whether it counts. With openpyxl, pass an index to ",[19,1278,1279],{},"create_sheet"," so April lands before ",[19,1282,740],{},":",[184,1285,1287],{"className":223,"code":1286,"language":225,"meta":189,"style":189},"wb = load_workbook(\"quarter.xlsx\")\napril = month_sheet(wb, \"Apr\", [12880, 7020, None, 3050, None], index=wb.sheetnames.index(\"End\"))\nwb.save(\"quarter.xlsx\")\nprint(wb.sheetnames)   # ['Summary', 'Start', 'Jan', 'Feb', 'Mar', 'Apr', 'End']\n",[19,1288,1289,1302,1355,1363],{"__ignoreMap":189},[193,1290,1291,1293,1295,1298,1300],{"class":195,"line":196},[193,1292,649],{"class":236},[193,1294,437],{"class":232},[193,1296,1297],{"class":236}," load_workbook(",[193,1299,967],{"class":203},[193,1301,515],{"class":236},[193,1303,1304,1307,1309,1312,1315,1318,1321,1323,1326,1328,1330,1332,1335,1337,1339,1342,1345,1347,1350,1352],{"class":195,"line":246},[193,1305,1306],{"class":236},"april ",[193,1308,437],{"class":232},[193,1310,1311],{"class":236}," month_sheet(wb, ",[193,1313,1314],{"class":203},"\"Apr\"",[193,1316,1317],{"class":236},", [",[193,1319,1320],{"class":269},"12880",[193,1322,282],{"class":236},[193,1324,1325],{"class":269},"7020",[193,1327,282],{"class":236},[193,1329,336],{"class":269},[193,1331,282],{"class":236},[193,1333,1334],{"class":269},"3050",[193,1336,282],{"class":236},[193,1338,336],{"class":269},[193,1340,1341],{"class":236},"], ",[193,1343,1344],{"class":506},"index",[193,1346,437],{"class":232},[193,1348,1349],{"class":236},"wb.sheetnames.index(",[193,1351,785],{"class":203},[193,1353,1354],{"class":236},"))\n",[193,1356,1357,1359,1361],{"class":195,"line":259},[193,1358,964],{"class":236},[193,1360,967],{"class":203},[193,1362,515],{"class":236},[193,1364,1365,1367,1370],{"class":195,"line":266},[193,1366,705],{"class":269},[193,1368,1369],{"class":236},"(wb.sheetnames)   ",[193,1371,1372],{"class":593},"# ['Summary', 'Start', 'Jan', 'Feb', 'Mar', 'Apr', 'End']\n",[10,1374,1375,1376,1379,1380,1382],{},"Had ",[19,1377,1378],{},"create_sheet(\"Apr\")"," appended it after ",[19,1381,740],{},", the summary would silently ignore April. That is the main risk of 3D references in general — and the same thing happens when a person drags a tab around in Excel. Check the order in code whenever you add or reorder sheets, and put the check where a failure would stop the job:",[184,1384,1386],{"className":223,"code":1385,"language":225,"meta":189,"style":189},"names = wb.sheetnames\nmonths = names[names.index(\"Start\") + 1 : names.index(\"End\")]\nassert months == [\"Jan\", \"Feb\", \"Mar\", \"Apr\"], months\n",[19,1387,1388,1398,1427],{"__ignoreMap":189},[193,1389,1390,1393,1395],{"class":195,"line":196},[193,1391,1392],{"class":236},"names ",[193,1394,437],{"class":232},[193,1396,1397],{"class":236}," wb.sheetnames\n",[193,1399,1400,1403,1405,1408,1410,1413,1416,1419,1422,1424],{"class":195,"line":246},[193,1401,1402],{"class":236},"months ",[193,1404,437],{"class":232},[193,1406,1407],{"class":236}," names[names.index(",[193,1409,763],{"class":203},[193,1411,1412],{"class":236},") ",[193,1414,1415],{"class":232},"+",[193,1417,1418],{"class":269}," 1",[193,1420,1421],{"class":236}," : names.index(",[193,1423,785],{"class":203},[193,1425,1426],{"class":236},")]\n",[193,1428,1429,1432,1435,1438,1440,1443,1445,1448,1450,1453,1455,1457],{"class":195,"line":259},[193,1430,1431],{"class":232},"assert",[193,1433,1434],{"class":236}," months ",[193,1436,1437],{"class":232},"==",[193,1439,276],{"class":236},[193,1441,1442],{"class":203},"\"Jan\"",[193,1444,282],{"class":236},[193,1446,1447],{"class":203},"\"Feb\"",[193,1449,282],{"class":236},[193,1451,1452],{"class":203},"\"Mar\"",[193,1454,282],{"class":236},[193,1456,1314],{"class":203},[193,1458,1459],{"class":236},"], months\n",[179,1461,1463],{"id":1462},"step-4-quote-names-with-spaces-once","Step 4 — Quote names with spaces, once",[10,1465,1466,1467,1470,1471,1474,1475,1478],{},"Real month tabs are often named ",[19,1468,1469],{},"Jan 2026"," or ",[19,1472,1473],{},"North - Q1",". A 3D reference to such names needs quotes around the ",[14,1476,1477],{},"whole span",", not around each name:",[184,1480,1482],{"className":223,"code":1481,"language":225,"meta":189,"style":189},"def three_d(first: str, last: str, cell: str, func: str = \"SUM\") -> str:\n    needs_quotes = any(ch in first + last for ch in \" -'&()\")\n    span = f\"{first}:{last}\"\n    if needs_quotes:\n        span = \"'\" + span.replace(\"'\", \"''\") + \"'\"\n    return f\"={func}({span}!{cell})\"\n\nprint(three_d(\"Start\", \"End\", \"B2\"))               # =SUM(Start:End!B2)\nprint(three_d(\"Jan 2026\", \"Dec 2026\", \"B2\"))       # =SUM('Jan 2026:Dec 2026'!B2)\nprint(three_d(\"Start\", \"End\", \"B2\", \"AVERAGE\"))    # =AVERAGE(Start:End!B2)\n",[19,1483,1484,1525,1560,1592,1600,1631,1668,1672,1694,1718],{"__ignoreMap":189},[193,1485,1486,1488,1491,1494,1497,1500,1502,1505,1507,1510,1512,1514,1517,1520,1522],{"class":195,"line":196},[193,1487,427],{"class":232},[193,1489,1490],{"class":430}," three_d",[193,1492,1493],{"class":236},"(first: ",[193,1495,1496],{"class":269},"str",[193,1498,1499],{"class":236},", last: ",[193,1501,1496],{"class":269},[193,1503,1504],{"class":236},", cell: ",[193,1506,1496],{"class":269},[193,1508,1509],{"class":236},", func: ",[193,1511,1496],{"class":269},[193,1513,273],{"class":232},[193,1515,1516],{"class":203}," \"SUM\"",[193,1518,1519],{"class":236},") -> ",[193,1521,1496],{"class":269},[193,1523,1524],{"class":236},":\n",[193,1526,1527,1530,1532,1535,1538,1540,1543,1545,1548,1550,1553,1555,1558],{"class":195,"line":246},[193,1528,1529],{"class":236},"    needs_quotes ",[193,1531,437],{"class":232},[193,1533,1534],{"class":269}," any",[193,1536,1537],{"class":236},"(ch ",[193,1539,527],{"class":232},[193,1541,1542],{"class":236}," first ",[193,1544,1415],{"class":232},[193,1546,1547],{"class":236}," last ",[193,1549,682],{"class":232},[193,1551,1552],{"class":236}," ch ",[193,1554,527],{"class":232},[193,1556,1557],{"class":203}," \" -'&()\"",[193,1559,515],{"class":236},[193,1561,1562,1565,1567,1570,1573,1575,1578,1580,1582,1584,1587,1589],{"class":195,"line":259},[193,1563,1564],{"class":236},"    span ",[193,1566,437],{"class":232},[193,1568,1569],{"class":232}," f",[193,1571,1572],{"class":203},"\"",[193,1574,935],{"class":934},[193,1576,1577],{"class":236},"first",[193,1579,941],{"class":934},[193,1581,1283],{"class":203},[193,1583,935],{"class":934},[193,1585,1586],{"class":236},"last",[193,1588,941],{"class":934},[193,1590,1591],{"class":203},"\"\n",[193,1593,1594,1597],{"class":195,"line":266},[193,1595,1596],{"class":232},"    if",[193,1598,1599],{"class":236}," needs_quotes:\n",[193,1601,1602,1605,1607,1610,1613,1616,1619,1621,1624,1626,1628],{"class":195,"line":306},[193,1603,1604],{"class":236},"        span ",[193,1606,437],{"class":232},[193,1608,1609],{"class":203}," \"'\"",[193,1611,1612],{"class":232}," +",[193,1614,1615],{"class":236}," span.replace(",[193,1617,1618],{"class":203},"\"'\"",[193,1620,282],{"class":236},[193,1622,1623],{"class":203},"\"''\"",[193,1625,1412],{"class":236},[193,1627,1415],{"class":232},[193,1629,1630],{"class":203}," \"'\"\n",[193,1632,1633,1635,1637,1640,1642,1645,1647,1649,1651,1653,1655,1658,1660,1663,1665],{"class":195,"line":317},[193,1634,635],{"class":232},[193,1636,1569],{"class":232},[193,1638,1639],{"class":203},"\"=",[193,1641,935],{"class":934},[193,1643,1644],{"class":236},"func",[193,1646,941],{"class":934},[193,1648,533],{"class":203},[193,1650,935],{"class":934},[193,1652,193],{"class":236},[193,1654,941],{"class":934},[193,1656,1657],{"class":203},"!",[193,1659,935],{"class":934},[193,1661,1662],{"class":236},"cell",[193,1664,941],{"class":934},[193,1666,1667],{"class":203},")\"\n",[193,1669,1670],{"class":195,"line":351},[193,1671,263],{"emptyLinePlaceholder":262},[193,1673,1674,1676,1679,1681,1683,1685,1687,1689,1692],{"class":195,"line":382},[193,1675,705],{"class":269},[193,1677,1678],{"class":236},"(three_d(",[193,1680,763],{"class":203},[193,1682,282],{"class":236},[193,1684,785],{"class":203},[193,1686,282],{"class":236},[193,1688,1004],{"class":203},[193,1690,1691],{"class":236},"))               ",[193,1693,1010],{"class":593},[193,1695,1696,1698,1700,1703,1705,1708,1710,1712,1715],{"class":195,"line":413},[193,1697,705],{"class":269},[193,1699,1678],{"class":236},[193,1701,1702],{"class":203},"\"Jan 2026\"",[193,1704,282],{"class":236},[193,1706,1707],{"class":203},"\"Dec 2026\"",[193,1709,282],{"class":236},[193,1711,1004],{"class":203},[193,1713,1714],{"class":236},"))       ",[193,1716,1717],{"class":593},"# =SUM('Jan 2026:Dec 2026'!B2)\n",[193,1719,1720,1722,1724,1726,1728,1730,1732,1734,1736,1739,1742],{"class":195,"line":419},[193,1721,705],{"class":269},[193,1723,1678],{"class":236},[193,1725,763],{"class":203},[193,1727,282],{"class":236},[193,1729,785],{"class":203},[193,1731,282],{"class":236},[193,1733,1004],{"class":203},[193,1735,282],{"class":236},[193,1737,1738],{"class":203},"\"AVERAGE\"",[193,1740,1741],{"class":236},"))    ",[193,1743,1744],{"class":593},"# =AVERAGE(Start:End!B2)\n",[10,1746,1747],{},"Apostrophes inside a sheet name are doubled, as elsewhere in Excel formulas. Keeping the logic in one helper means every summary cell follows the same quoting rule.",[179,1749,1751],{"id":1750},"step-5-check-the-totals-without-excel","Step 5 — Check the totals without Excel",[10,1753,1754],{},"openpyxl writes formulas as text; it never calculates them. To check the summary before sending the file, recalculate it headlessly with LibreOffice, which understands 3D references, and read the cached values:",[184,1756,1758],{"className":223,"code":1757,"language":225,"meta":189,"style":189},"import subprocess\nfrom pathlib import Path\n\nPath(\"recalculated\").mkdir(exist_ok=True)\nsubprocess.run([\"soffice\", \"--headless\", \"--convert-to\", \"xlsx\", \"--outdir\", \"recalculated\", \"quarter.xlsx\"],\n               check=True, capture_output=True, timeout=120)\nvalues = load_workbook(\"recalculated\u002Fquarter.xlsx\", data_only=True)[\"Summary\"]\nprint(values[\"B2\"].value, values[\"B6\"].value)   # 50440 10160\n",[19,1759,1760,1767,1779,1783,1803,1841,1871,1898],{"__ignoreMap":189},[193,1761,1762,1764],{"class":195,"line":196},[193,1763,240],{"class":232},[193,1765,1766],{"class":236}," subprocess\n",[193,1768,1769,1771,1774,1776],{"class":195,"line":246},[193,1770,233],{"class":232},[193,1772,1773],{"class":236}," pathlib ",[193,1775,240],{"class":232},[193,1777,1778],{"class":236}," Path\n",[193,1780,1781],{"class":195,"line":259},[193,1782,263],{"emptyLinePlaceholder":262},[193,1784,1785,1788,1791,1794,1797,1799,1801],{"class":195,"line":266},[193,1786,1787],{"class":236},"Path(",[193,1789,1790],{"class":203},"\"recalculated\"",[193,1792,1793],{"class":236},").mkdir(",[193,1795,1796],{"class":506},"exist_ok",[193,1798,437],{"class":232},[193,1800,512],{"class":269},[193,1802,515],{"class":236},[193,1804,1805,1808,1811,1813,1816,1818,1821,1823,1826,1828,1831,1833,1835,1837,1839],{"class":195,"line":306},[193,1806,1807],{"class":236},"subprocess.run([",[193,1809,1810],{"class":203},"\"soffice\"",[193,1812,282],{"class":236},[193,1814,1815],{"class":203},"\"--headless\"",[193,1817,282],{"class":236},[193,1819,1820],{"class":203},"\"--convert-to\"",[193,1822,282],{"class":236},[193,1824,1825],{"class":203},"\"xlsx\"",[193,1827,282],{"class":236},[193,1829,1830],{"class":203},"\"--outdir\"",[193,1832,282],{"class":236},[193,1834,1790],{"class":203},[193,1836,282],{"class":236},[193,1838,967],{"class":203},[193,1840,348],{"class":236},[193,1842,1843,1846,1848,1850,1852,1855,1857,1859,1861,1864,1866,1869],{"class":195,"line":317},[193,1844,1845],{"class":506},"               check",[193,1847,437],{"class":232},[193,1849,512],{"class":269},[193,1851,282],{"class":236},[193,1853,1854],{"class":506},"capture_output",[193,1856,437],{"class":232},[193,1858,512],{"class":269},[193,1860,282],{"class":236},[193,1862,1863],{"class":506},"timeout",[193,1865,437],{"class":232},[193,1867,1868],{"class":269},"120",[193,1870,515],{"class":236},[193,1872,1873,1876,1878,1880,1883,1885,1888,1890,1892,1894,1896],{"class":195,"line":351},[193,1874,1875],{"class":236},"values ",[193,1877,437],{"class":232},[193,1879,1297],{"class":236},[193,1881,1882],{"class":203},"\"recalculated\u002Fquarter.xlsx\"",[193,1884,282],{"class":236},[193,1886,1887],{"class":506},"data_only",[193,1889,437],{"class":232},[193,1891,512],{"class":269},[193,1893,995],{"class":236},[193,1895,998],{"class":203},[193,1897,303],{"class":236},[193,1899,1900,1902,1905,1907,1910,1912,1914],{"class":195,"line":382},[193,1901,705],{"class":269},[193,1903,1904],{"class":236},"(values[",[193,1906,1004],{"class":203},[193,1908,1909],{"class":236},"].value, values[",[193,1911,602],{"class":203},[193,1913,1007],{"class":236},[193,1915,1916],{"class":593},"# 50440 10160\n",[10,1918,1919],{},"Revenue across the four months is 50,440 and operating profit 10,160 — April included, which proves the new tab sits inside the bookends. A useful habit is to compute the same totals independently in pandas from the data you wrote, and compare them with the recalculated summary; a mismatch means a sheet is outside the span or a layout has drifted.",[10,1921,1922,1923,1926,1927,1930,1931,1935],{},"Pure-Python calculation engines are not a substitute here: the ",[19,1924,1925],{},"formulas"," package, for example, rejects ",[19,1928,1929],{},"Start:End!B2"," as an invalid formula. The trade-offs between recalculation approaches are covered in ",[32,1932,1934],{"href":1933},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frecalculate-excel-formulas-without-excel-in-python\u002F","Recalculate Excel Formulas Without Excel in Python",".",[38,1937,47,1942,47,1945,47,1948,47,1951,47,1954,47,1959,47,1965,47,1969,47,1974,47,1976,47,1983,47,1986,47,1990,47,1992,47,1997,47,2000,47,2004,47,2006,47,2011,47,2014,47,2018,47,2020,47,2027,47,2030,47,2034,47,2036],{"viewBox":1938,"role":41,"ariaLabelledBy":1939,"xmlns":45,"style":46},"4 0 752 242",[1940,1941],"threed-support-t","threed-support-d",[49,1943,1944],{"id":1940},"Which Excel functions accept 3D references",[53,1946,1947],{"id":1941},"SUM, AVERAGE, COUNT, MAX and MIN accept a span of sheets. SUMIF, COUNTIF, VLOOKUP, INDEX and MATCH do not, so conditional totals across sheets need a helper cell on every sheet or a different approach.",[57,1949],{"x":59,"y":60,"width":61,"height":1950,"fill":63},"242",[57,1952],{"x":99,"y":100,"width":1953,"height":1069,"rx":60,"fill":63,"stroke":104,"style":1066},"250.9",[57,1955],{"x":1956,"y":100,"width":1957,"height":1069,"rx":60,"fill":71,"stroke":72,"style":1958},"270.9","469.1","stroke-width:1.2px",[86,1960,1964],{"x":1961,"y":1962,"style":1963},"505.5","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Accepts Sheet1:Sheet3!A1",[57,1966],{"x":99,"y":1967,"width":1953,"height":1968,"rx":60,"fill":103,"stroke":104,"style":1066},"56.0","34.0",[86,1970,1973],{"x":1084,"y":1971,"style":1972},"78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","SUM, AVERAGE, COUNT, COUNTA",[57,1975],{"x":1956,"y":1967,"width":1957,"height":1968,"rx":60,"fill":63,"stroke":104,"style":1066},[75,1977,1979],{"transform":1978},"translate(496.5,64.0) scale(0.750)",[79,1980],{"d":1981,"fill":82,"stroke":1135,"style":1982},"M5 12.5l4.5 4.5L19 7","stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[57,1984],{"x":99,"y":1985,"width":1953,"height":1968,"rx":60,"fill":103,"stroke":104,"style":1066},"90.0",[86,1987,1989],{"x":1084,"y":1988,"style":1972},"112.0","MAX, MIN, PRODUCT",[57,1991],{"x":1956,"y":1985,"width":1957,"height":1968,"rx":60,"fill":63,"stroke":104,"style":1066},[75,1993,1995],{"transform":1994},"translate(496.5,98.0) scale(0.750)",[79,1996],{"d":1981,"fill":82,"stroke":1135,"style":1982},[57,1998],{"x":99,"y":1999,"width":1953,"height":1968,"rx":60,"fill":103,"stroke":104,"style":1066},"124.0",[86,2001,2003],{"x":1084,"y":2002,"style":1972},"146.0","STDEV, VAR families",[57,2005],{"x":1956,"y":1999,"width":1957,"height":1968,"rx":60,"fill":63,"stroke":104,"style":1066},[75,2007,2009],{"transform":2008},"translate(496.5,132.0) scale(0.750)",[79,2010],{"d":1981,"fill":82,"stroke":1135,"style":1982},[57,2012],{"x":99,"y":2013,"width":1953,"height":1968,"rx":60,"fill":103,"stroke":104,"style":1066},"158.0",[86,2015,2017],{"x":1084,"y":2016,"style":1972},"180.0","SUMIF, SUMIFS, COUNTIF",[57,2019],{"x":1956,"y":2013,"width":1957,"height":1968,"rx":60,"fill":63,"stroke":104,"style":1066},[75,2021,2023],{"transform":2022},"translate(497.5,167.0) scale(0.667)",[79,2024],{"d":2025,"fill":82,"stroke":2026,"style":1055},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)",[57,2028],{"x":99,"y":2029,"width":1953,"height":1968,"rx":60,"fill":103,"stroke":104,"style":1066},"192.0",[86,2031,2033],{"x":1084,"y":2032,"style":1972},"214.0","VLOOKUP, INDEX, MATCH",[57,2035],{"x":1956,"y":2029,"width":1957,"height":1968,"rx":60,"fill":63,"stroke":104,"style":1066},[75,2037,2039],{"transform":2038},"translate(497.5,201.0) scale(0.667)",[79,2040],{"d":2025,"fill":82,"stroke":2026,"style":1055},[10,2042,2043,2044,2047],{},"For a conditional total across sheets — say, revenue only from months above target — put the condition in a helper cell on every month sheet (",[19,2045,2046],{},"=IF(B2>12000,B2,0)",") and 3D-sum the helper cell, or compute the figure in pandas and write it as a value.",[179,2049,2051],{"id":2050},"common-pitfalls","Common pitfalls",[2053,2054,2055,2066,2076,2082],"ul",{},[2056,2057,2058,2062,2063,2065],"li",{},[2059,2060,2061],"strong",{},"Sheets outside the bookends."," A tab appended after ",[19,2064,740],{}," is ignored without any error. Always insert with an explicit index and assert the order.",[2056,2067,2068,2071,2072,2075],{},[2059,2069,2070],{},"Layouts drifting apart."," A 3D reference adds the same ",[14,2073,2074],{},"address"," on every sheet. If one month gains an extra row, B6 on that sheet is no longer operating profit. Generate every month from one function, as in Step 1.",[2056,2077,2078,2081],{},[2059,2079,2080],{},"Hidden sheets still count."," Hiding a month tab does not remove it from a 3D range. Move it outside the bookends to exclude it.",[2056,2083,2084,2087,2088,2090],{},[2059,2085,2086],{},"Deleting a bookend."," Excel adjusts the reference to the neighbouring sheet, so deleting ",[19,2089,736],{}," quietly changes the span. Protect the workbook structure if others will edit it.",[179,2092,2094],{"id":2093},"performance-and-scale-notes","Performance and scale notes",[10,2096,2097,2098,2102],{},"A 3D formula is as cheap as a normal SUM for Excel, and writing a few dozen of them with openpyxl is instantaneous. The limits are human: beyond a year or two of monthly tabs the workbook becomes hard to navigate, and it is usually better to keep all months in one long table and let a pivot or SUMIFS build the summary. The one-table approach also makes the data easy to read back into pandas — see ",[32,2099,2101],{"href":2100},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fread-all-sheets-from-an-excel-file-into-dataframes\u002F","Read All Sheets from an Excel File into DataFrames"," for combining existing monthly tabs into one frame.",[179,2104,2106],{"id":2105},"conclusion","Conclusion",[10,2108,2109,2110,737,2112,2114,2115,2117,2118,2120],{},"Generate identical month sheets from one function, surround them with empty ",[19,2111,736],{},[19,2113,740],{}," bookends, and write summary cells as ",[19,2116,1080],{}," — quoting the whole span when names contain spaces. Insert new tabs with an explicit index before ",[19,2119,740],{},", assert the tab order in code, and recalculate with LibreOffice when you need to check totals before the file leaves Python. Keep to aggregate functions; conditional totals need a helper cell on each sheet.",[179,2122,2124],{"id":2123},"frequently-asked-questions","Frequently asked questions",[10,2126,2127,2130],{},[2059,2128,2129],{},"What is a 3D reference in Excel?","\nA reference that spans a range of worksheets as well as cells, written First:Last!Cell — for example =SUM(Jan:Mar!B2) adds B2 on Jan, Mar and every sheet positioned between them in the tab order.",[10,2132,2133,2136],{},[2059,2134,2135],{},"Which functions accept 3D references?","\nAggregates such as SUM, AVERAGE, COUNT, COUNTA, MAX, MIN, PRODUCT and the STDEV and VAR families. Conditional and lookup functions such as SUMIF, COUNTIF and VLOOKUP do not accept them.",[10,2138,2139,2142],{},[2059,2140,2141],{},"Why did my 3D total change when I moved a sheet?","\nA 3D reference is defined by sheet positions, not a list of names. Any sheet dragged between the first and last sheet is included, and any sheet dragged outside is excluded, even though the formula text is unchanged.",[10,2144,2145,2148],{},[2059,2146,2147],{},"How do I write a 3D reference when sheet names contain spaces?","\nQuote the whole sheet span once, for example =SUM('Jan 2026:Dec 2026'!B2). Quoting each name separately is not valid.",[179,2150,2152],{"id":2151},"related","Related",[2053,2154,2155,2161,2168,2175],{},[2056,2156,2157,2158,2160],{},"Up one level: ",[32,2159,35],{"href":34}," — reading and writing multi-sheet workbooks.",[2056,2162,2163,2167],{},[32,2164,2166],{"href":2165},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002F","Working with Excel Formulas in Python"," — writing and reading formulas in general.",[2056,2169,2170,2174],{},[32,2171,2173],{"href":2172},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python\u002F","Add a Summary Sheet to an Excel Report with Python"," — summaries computed in pandas instead.",[2056,2176,2177,2181],{},[32,2178,2180],{"href":2179},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcreate-a-sheet-for-each-group-in-a-dataframe\u002F","Create a Sheet for Each Group in a DataFrame"," — generating the per-period tabs.",[2183,2184,2185],"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 .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":189,"searchDepth":246,"depth":246,"links":2187},[2188,2189,2190,2191,2192,2193,2194,2195,2196,2197,2198],{"id":181,"depth":246,"text":182},{"id":216,"depth":246,"text":217},{"id":721,"depth":246,"text":722},{"id":1268,"depth":246,"text":1269},{"id":1462,"depth":246,"text":1463},{"id":1750,"depth":246,"text":1751},{"id":2050,"depth":246,"text":2051},{"id":2093,"depth":246,"text":2094},{"id":2105,"depth":246,"text":2106},{"id":2123,"depth":246,"text":2124},{"id":2151,"depth":246,"text":2152},"2026-09-23","Build a summary sheet that totals the same cell on every monthly or regional tab with Excel 3D references like =SUM(Jan:Dec!B2), written from Python with openpyxl — bookend sheets, quoting rules, inserting new tabs in the right place, and which functions support 3D ranges.","md",[2203,2205,2207,2209],{"q":2129,"a":2204},"A reference that spans a range of worksheets as well as cells, written First:Last!Cell — for example =SUM(Jan:Mar!B2) adds B2 on Jan, Mar and every sheet positioned between them in the tab order.",{"q":2135,"a":2206},"Aggregates such as SUM, AVERAGE, COUNT, COUNTA, MAX, MIN, PRODUCT and the STDEV and VAR families. Conditional and lookup functions such as SUMIF, COUNTIF and VLOOKUP do not accept them.",{"q":2141,"a":2208},"A 3D reference is defined by sheet positions, not a list of names. Any sheet dragged between the first and last sheet is included, and any sheet dragged outside is excluded, even though the formula text is unchanged.",{"q":2147,"a":2210},"Quote the whole sheet span once, for example =SUM('Jan 2026:Dec 2026'!B2). Quoting each name separately is not valid.","sum the same cell across many sheets with 3d references from python",null,{"breadcrumb":2214},[2215,2218,2221],{"name":2216,"item":2217},"Home","\u002F",{"name":2219,"item":2220},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":35,"item":34},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references",{"title":2224,"description":2225},"Excel 3D References from Python — Sum a Cell Across Sheets","Write =SUM(First:Last!B2) formulas with openpyxl to total identical layouts across worksheets, use Start and End bookend sheets, insert new tabs inside the range, quote names with spaces, and verify results.","sum-the-same-cell-across-sheets-with-3d-references","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsum-the-same-cell-across-sheets-with-3d-references\u002Findex",[2229,2230,2231],"sheets-and-files","excel-formulas","openpyxl","how-to","baDx54kDOI3u2-ks2vuj92FXI5_AwPkHfZaBYSy0GN0",[2235,2239],{"title":2236,"path":2237,"stem":2238,"children":-1},"Split One Excel Sheet into Multiple Files by Column Value","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsplit-one-excel-sheet-into-multiple-files-by-value","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsplit-one-excel-sheet-into-multiple-files-by-value\u002Findex",{"title":2240,"path":2241,"stem":2242,"children":-1},"Writing DataFrames to Excel with Pandas","\u002Fgetting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas","getting-started-with-python-excel-automation\u002Fwriting-dataframes-to-excel-with-pandas\u002Findex",[2244,2246],{"path":2245,"title":2219},"\u002Fgetting-started-with-python-excel-automation",{"path":2247,"title":35},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python",[],[2250,2253,2256,2259,2262,2265],{"path":2251,"title":2252,"kind":2232},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fconsolidate-timesheets-from-many-workbooks","Consolidate Timesheets from Many Workbooks with Python",{"path":2254,"title":2255,"kind":2232},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fcombine-sheets-with-different-layouts-into-one-table","Combine Sheets with Different Layouts into One Table",{"path":2257,"title":2258,"kind":2232},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fappend-new-rows-to-a-master-excel-file-without-duplicates","Append New Rows to a Master Excel File Without Duplicates",{"path":2260,"title":2261,"kind":2232},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fconcatenate-excel-sheets-with-different-columns","Concatenate Excel Sheets with Different Columns",{"path":2263,"title":2264,"kind":2232},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-excel-files-with-different-column-names","Merge Excel Files with Different Column Names in pandas",{"path":2266,"title":2267,"kind":2232},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-more-than-two-excel-files-on-a-key-column","Merge More Than Two Excel Files on a Key Column",1790972076334]