[{"data":1,"prerenderedAt":2199},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report":2164,"ancestors:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report":2173,"children:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report":2178,"related:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report":2179},{"id":4,"title":5,"body":6,"dateModified":2130,"datePublished":2130,"description":2131,"extension":2132,"faq":2133,"keyword":2142,"kind":2143,"meta":2144,"navigation":88,"path":2153,"seo":2154,"slug":2157,"stem":2158,"topics":2159,"type":2162,"__hash__":2163},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report\u002Findex.md","Build a Customer Credit Limit Report",{"type":7,"value":8,"toc":2119},"minimark",[9,19,24,53,56,60,529,541,675,679,1063,1083,1086,1158,1412,1416,1997,2004,2008,2011,2015,2049,2053,2056,2060,2066,2072,2078,2084,2088,2115],[10,11,12,13,18],"p",{},"A credit limit is the most a company is willing to be owed by one customer. Checking it sounds simple — compare the receivables balance with the limit — but the usual spreadsheet version misses the part that causes the trouble: orders the company has accepted and is about to ship are exposure too, even though no invoice exists yet. A customer at 70% of its limit with a large order going out tomorrow will be at 120% next week. The other half of the picture is behaviour: a customer well within its limit but paying ninety days late is a bigger risk than one near its limit who pays on time. This page builds a credit limit report with pandas that puts both together: exposure from open invoices and open orders, utilisation of the limit, the overdue position, a clear rule for which accounts go on hold or need review, and the headroom left for new orders, written to a colour-coded workbook. It belongs to ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002F","Building Finance and Operations Reports",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[32,33,34],"code",{"__ignoreMap":30},[35,36,39,43,47,50],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," pandas",[35,51,52],{"class":45}," openpyxl\n",[10,54,55],{},"Three inputs: customers with their credit limit and payment terms; open invoices with due dates and the amount still unpaid; and open sales orders — accepted, not yet shipped or invoiced — with their value. The report is run on 5 October 2026.",[20,57,59],{"id":58},"step-1-exposure-is-invoices-plus-open-orders","Step 1 — Exposure is invoices plus open orders",[25,61,65],{"className":62,"code":63,"language":64,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nasof = pd.Timestamp(\"2026-10-05\")\ncustomers = pd.read_excel(\"customers.xlsx\")\ninvoices = pd.read_excel(\"open_invoices.xlsx\", parse_dates=[\"Invoice date\", \"Due date\"])\norders = pd.read_excel(\"open_orders.xlsx\")\n\ninvoices[\"Days overdue\"] = (asof - invoices[\"Due date\"]).dt.days.clip(lower=0)\nar = invoices.groupby(\"Customer\").agg(\n    Receivables=(\"Open amount\", \"sum\"),\n    Overdue=(\"Open amount\", lambda s: s[invoices.loc[s.index, \"Days overdue\"] > 0].sum()),\n    Overdue_60=(\"Open amount\", lambda s: s[invoices.loc[s.index, \"Days overdue\"] > 60].sum()),\n    Oldest_days=(\"Days overdue\", \"max\"))\nbook = (customers.set_index(\"Customer\")\n        .join(ar).join(orders.groupby(\"Customer\")[\"Value not yet invoiced\"].sum().rename(\"Open orders\"))\n        .fillna(0))\nbook[\"Exposure\"] = book[\"Receivables\"] + book[\"Open orders\"]\nbook[\"Utilisation\"] = (book[\"Exposure\"] \u002F book[\"Credit limit\"]).round(3)\nbook[\"Headroom\"] = (book[\"Credit limit\"] - book[\"Exposure\"]).round(2)\nprint(book[[\"Credit limit\", \"Receivables\", \"Open orders\", \"Exposure\", \"Utilisation\", \"Overdue\", \"Oldest_days\"]].round(2).to_string())\n","python",[32,66,67,83,90,108,124,160,175,180,219,236,258,291,320,340,355,377,387,418,453,484],{"__ignoreMap":30},[35,68,69,73,77,80],{"class":37,"line":38},[35,70,72],{"class":71},"s-kum","import",[35,74,76],{"class":75},"skGVy"," pandas ",[35,78,79],{"class":71},"as",[35,81,82],{"class":75}," pd\n",[35,84,86],{"class":37,"line":85},2,[35,87,89],{"emptyLinePlaceholder":88},true,"\n",[35,91,93,96,99,102,105],{"class":37,"line":92},3,[35,94,95],{"class":75},"asof ",[35,97,98],{"class":71},"=",[35,100,101],{"class":75}," pd.Timestamp(",[35,103,104],{"class":45},"\"2026-10-05\"",[35,106,107],{"class":75},")\n",[35,109,111,114,116,119,122],{"class":37,"line":110},4,[35,112,113],{"class":75},"customers ",[35,115,98],{"class":71},[35,117,118],{"class":75}," pd.read_excel(",[35,120,121],{"class":45},"\"customers.xlsx\"",[35,123,107],{"class":75},[35,125,127,130,132,134,137,140,144,146,149,152,154,157],{"class":37,"line":126},5,[35,128,129],{"class":75},"invoices ",[35,131,98],{"class":71},[35,133,118],{"class":75},[35,135,136],{"class":45},"\"open_invoices.xlsx\"",[35,138,139],{"class":75},", ",[35,141,143],{"class":142},"sa561","parse_dates",[35,145,98],{"class":71},[35,147,148],{"class":75},"[",[35,150,151],{"class":45},"\"Invoice date\"",[35,153,139],{"class":75},[35,155,156],{"class":45},"\"Due date\"",[35,158,159],{"class":75},"])\n",[35,161,163,166,168,170,173],{"class":37,"line":162},6,[35,164,165],{"class":75},"orders ",[35,167,98],{"class":71},[35,169,118],{"class":75},[35,171,172],{"class":45},"\"open_orders.xlsx\"",[35,174,107],{"class":75},[35,176,178],{"class":37,"line":177},7,[35,179,89],{"emptyLinePlaceholder":88},[35,181,183,186,189,192,194,197,200,203,205,208,211,213,217],{"class":37,"line":182},8,[35,184,185],{"class":75},"invoices[",[35,187,188],{"class":45},"\"Days overdue\"",[35,190,191],{"class":75},"] ",[35,193,98],{"class":71},[35,195,196],{"class":75}," (asof ",[35,198,199],{"class":71},"-",[35,201,202],{"class":75}," invoices[",[35,204,156],{"class":45},[35,206,207],{"class":75},"]).dt.days.clip(",[35,209,210],{"class":142},"lower",[35,212,98],{"class":71},[35,214,216],{"class":215},"sP0c6","0",[35,218,107],{"class":75},[35,220,222,225,227,230,233],{"class":37,"line":221},9,[35,223,224],{"class":75},"ar ",[35,226,98],{"class":71},[35,228,229],{"class":75}," invoices.groupby(",[35,231,232],{"class":45},"\"Customer\"",[35,234,235],{"class":75},").agg(\n",[35,237,239,242,244,247,250,252,255],{"class":37,"line":238},10,[35,240,241],{"class":142},"    Receivables",[35,243,98],{"class":71},[35,245,246],{"class":75},"(",[35,248,249],{"class":45},"\"Open amount\"",[35,251,139],{"class":75},[35,253,254],{"class":45},"\"sum\"",[35,256,257],{"class":75},"),\n",[35,259,261,264,266,268,270,272,275,278,280,282,285,288],{"class":37,"line":260},11,[35,262,263],{"class":142},"    Overdue",[35,265,98],{"class":71},[35,267,246],{"class":75},[35,269,249],{"class":45},[35,271,139],{"class":75},[35,273,274],{"class":71},"lambda",[35,276,277],{"class":75}," s: s[invoices.loc[s.index, ",[35,279,188],{"class":45},[35,281,191],{"class":75},[35,283,284],{"class":71},">",[35,286,287],{"class":215}," 0",[35,289,290],{"class":75},"].sum()),\n",[35,292,294,297,299,301,303,305,307,309,311,313,315,318],{"class":37,"line":293},12,[35,295,296],{"class":142},"    Overdue_60",[35,298,98],{"class":71},[35,300,246],{"class":75},[35,302,249],{"class":45},[35,304,139],{"class":75},[35,306,274],{"class":71},[35,308,277],{"class":75},[35,310,188],{"class":45},[35,312,191],{"class":75},[35,314,284],{"class":71},[35,316,317],{"class":215}," 60",[35,319,290],{"class":75},[35,321,323,326,328,330,332,334,337],{"class":37,"line":322},13,[35,324,325],{"class":142},"    Oldest_days",[35,327,98],{"class":71},[35,329,246],{"class":75},[35,331,188],{"class":45},[35,333,139],{"class":75},[35,335,336],{"class":45},"\"max\"",[35,338,339],{"class":75},"))\n",[35,341,343,346,348,351,353],{"class":37,"line":342},14,[35,344,345],{"class":75},"book ",[35,347,98],{"class":71},[35,349,350],{"class":75}," (customers.set_index(",[35,352,232],{"class":45},[35,354,107],{"class":75},[35,356,358,361,363,366,369,372,375],{"class":37,"line":357},15,[35,359,360],{"class":75},"        .join(ar).join(orders.groupby(",[35,362,232],{"class":45},[35,364,365],{"class":75},")[",[35,367,368],{"class":45},"\"Value not yet invoiced\"",[35,370,371],{"class":75},"].sum().rename(",[35,373,374],{"class":45},"\"Open orders\"",[35,376,339],{"class":75},[35,378,380,383,385],{"class":37,"line":379},16,[35,381,382],{"class":75},"        .fillna(",[35,384,216],{"class":215},[35,386,339],{"class":75},[35,388,390,393,396,398,400,403,406,408,411,413,415],{"class":37,"line":389},17,[35,391,392],{"class":75},"book[",[35,394,395],{"class":45},"\"Exposure\"",[35,397,191],{"class":75},[35,399,98],{"class":71},[35,401,402],{"class":75}," book[",[35,404,405],{"class":45},"\"Receivables\"",[35,407,191],{"class":75},[35,409,410],{"class":71},"+",[35,412,402],{"class":75},[35,414,374],{"class":45},[35,416,417],{"class":75},"]\n",[35,419,421,423,426,428,430,433,435,437,440,442,445,448,451],{"class":37,"line":420},18,[35,422,392],{"class":75},[35,424,425],{"class":45},"\"Utilisation\"",[35,427,191],{"class":75},[35,429,98],{"class":71},[35,431,432],{"class":75}," (book[",[35,434,395],{"class":45},[35,436,191],{"class":75},[35,438,439],{"class":71},"\u002F",[35,441,402],{"class":75},[35,443,444],{"class":45},"\"Credit limit\"",[35,446,447],{"class":75},"]).round(",[35,449,450],{"class":215},"3",[35,452,107],{"class":75},[35,454,456,458,461,463,465,467,469,471,473,475,477,479,482],{"class":37,"line":455},19,[35,457,392],{"class":75},[35,459,460],{"class":45},"\"Headroom\"",[35,462,191],{"class":75},[35,464,98],{"class":71},[35,466,432],{"class":75},[35,468,444],{"class":45},[35,470,191],{"class":75},[35,472,199],{"class":71},[35,474,402],{"class":75},[35,476,395],{"class":45},[35,478,447],{"class":75},[35,480,481],{"class":215},"2",[35,483,107],{"class":75},[35,485,487,490,493,495,497,499,501,503,505,507,509,511,513,516,518,521,524,526],{"class":37,"line":486},20,[35,488,489],{"class":215},"print",[35,491,492],{"class":75},"(book[[",[35,494,444],{"class":45},[35,496,139],{"class":75},[35,498,405],{"class":45},[35,500,139],{"class":75},[35,502,374],{"class":45},[35,504,139],{"class":75},[35,506,395],{"class":45},[35,508,139],{"class":75},[35,510,425],{"class":45},[35,512,139],{"class":75},[35,514,515],{"class":45},"\"Overdue\"",[35,517,139],{"class":75},[35,519,520],{"class":45},"\"Oldest_days\"",[35,522,523],{"class":75},"]].round(",[35,525,481],{"class":215},[35,527,528],{"class":75},").to_string())\n",[10,530,531,532,536,537,540],{},"For each customer the receivables are the sum of open invoice amounts, split into what is overdue at all, what is more than sixty days overdue, and how many days the oldest overdue invoice has been waiting. Joining open orders by customer adds the committed but uninvoiced value. ",[533,534,535],"em",{},"Exposure"," — what the company would be owed once those orders ship — is the sum, and ",[533,538,539],{},"utilisation"," is exposure as a share of the limit. Customers with no open invoices or orders get zeros rather than disappearing, because a customer with a limit and no balance is still part of the credit book, with all of its limit available. Headroom, the limit minus exposure, is the figure sales needs most often, every time a new order comes in: an order bigger than the headroom needs a credit decision before it is accepted.",[542,543,551,552,551,556,551,560,551,567,551,577,551,584,551,589,551,594,551,599,551,604,551,609,551,613,551,617,551,620,551,625,551,630,551,634,551,638,551,641,551,645,551,650,551,654,551,658,551,661,551,666,551,671],"svg",{"viewBox":544,"role":545,"ariaLabelledBy":546,"xmlns":549,"style":550},"84 0 592 471","img",[547,548],"credit-exposure-t","credit-exposure-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:592px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[553,554,555],"title",{"id":547},"What makes up a customer's exposure",[557,558,559],"desc",{"id":548},"Exposure is the open invoices, split into amounts not yet due, overdue, and more than sixty days overdue, plus orders accepted but not yet invoiced. Exposure divided by the credit limit gives utilisation, and the limit minus exposure gives the headroom for new orders.",[561,562],"rect",{"x":563,"y":216,"width":564,"height":565,"fill":566},"84","592","471","#ffffff",[561,568],{"x":569,"y":570,"width":571,"height":572,"rx":573,"fill":574,"stroke":575,"style":576},"100.0","16.0","560.0","59.0","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[578,579,583],"text",{"x":580,"y":581,"style":582},"380.0","41.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Not yet due",[578,585,588],{"x":580,"y":586,"style":587},"61.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","open invoices",[37,590],{"x1":580,"y1":591,"x2":580,"y2":592,"stroke":593,"style":576},"80.0","102.0","var(--brand,#5b5cf0)",[595,596],"polygon",{"points":597,"fill":598},"380.0,106.0 375.0,97.0 385.0,97.0","#5b5cf0",[561,600],{"x":569,"y":601,"width":571,"height":572,"rx":573,"fill":602,"stroke":603,"style":576},"111.0","#fdefd8","var(--gold,#b4740a)",[578,605,608],{"x":580,"y":606,"style":607},"136.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Overdue",[578,610,612],{"x":580,"y":611,"style":587},"156.0","past the due date",[37,614],{"x1":580,"y1":615,"x2":580,"y2":616,"stroke":593,"style":576},"175.0","197.0",[595,618],{"points":619,"fill":598},"380.0,201.0 375.0,192.0 385.0,192.0",[561,621],{"x":569,"y":622,"width":571,"height":572,"rx":573,"fill":623,"stroke":624,"style":576},"206.0","#fce9e9","var(--danger,#dc2626)",[578,626,629],{"x":580,"y":627,"style":628},"231.5","font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","More than 60 days overdue",[578,631,633],{"x":580,"y":632,"style":587},"251.0","the risk signal",[37,635],{"x1":580,"y1":636,"x2":580,"y2":637,"stroke":593,"style":576},"270.0","292.0",[595,639],{"points":640,"fill":598},"380.0,296.0 375.0,287.0 385.0,287.0",[561,642],{"x":569,"y":643,"width":571,"height":572,"rx":573,"fill":644,"stroke":593,"style":576},"301.0","#f0f4ff",[578,646,649],{"x":580,"y":647,"style":648},"326.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Open orders",[578,651,653],{"x":580,"y":652,"style":587},"346.0","accepted, not invoiced",[37,655],{"x1":580,"y1":656,"x2":580,"y2":657,"stroke":593,"style":576},"365.0","387.0",[595,659],{"points":660,"fill":598},"380.0,391.0 375.0,382.0 385.0,382.0",[561,662],{"x":569,"y":663,"width":571,"height":572,"rx":573,"fill":664,"stroke":665,"style":576},"396.0","#d9f4f1","var(--teal,#0f9488)",[578,667,670],{"x":580,"y":668,"style":669},"421.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","= Exposure vs limit",[578,672,674],{"x":580,"y":673,"style":587},"441.0","utilisation and headroom",[20,676,678],{"id":677},"step-2-decide-hold-review-or-ok","Step 2 — Decide hold, review or OK",[25,680,682],{"className":62,"code":681,"language":64,"meta":30,"style":30},"def decision(r):\n    if r[\"Utilisation\"] > 1.0 or r[\"Overdue_60\"] > 0:\n        return \"HOLD\"\n    if r[\"Utilisation\"] > 0.85 or r[\"Oldest_days\"] > 30:\n        return \"REVIEW\"\n    return \"OK\"\n\nbook[\"Decision\"] = book.apply(decision, axis=1)\nbook[\"Reason\"] = book.apply(lambda r: \"; \".join(x for x in [\n    f\"{r['Utilisation']:.0%} of limit\" if r[\"Utilisation\"] > 0.85 else \"\",\n    f\"{r['Overdue_60']:,.0f} over 60 days\" if r[\"Overdue_60\"] > 0 else \"\",\n    f\"oldest {int(r['Oldest_days'])} days late\" if 30 \u003C r[\"Oldest_days\"] else \"\"] if x), axis=1)\nprint(book.sort_values([\"Decision\", \"Utilisation\"], ascending=[True, False])[[\"Exposure\", \"Utilisation\", \"Overdue_60\", \"Oldest_days\", \"Decision\", \"Reason\"]].round(2).to_string())\n",[32,683,684,696,730,738,768,775,783,787,811,848,900,941,1001],{"__ignoreMap":30},[35,685,686,689,693],{"class":37,"line":38},[35,687,688],{"class":71},"def",[35,690,692],{"class":691},"s_Opv"," decision",[35,694,695],{"class":75},"(r):\n",[35,697,698,701,704,706,708,710,713,716,718,721,723,725,727],{"class":37,"line":85},[35,699,700],{"class":71},"    if",[35,702,703],{"class":75}," r[",[35,705,425],{"class":45},[35,707,191],{"class":75},[35,709,284],{"class":71},[35,711,712],{"class":215}," 1.0",[35,714,715],{"class":71}," or",[35,717,703],{"class":75},[35,719,720],{"class":45},"\"Overdue_60\"",[35,722,191],{"class":75},[35,724,284],{"class":71},[35,726,287],{"class":215},[35,728,729],{"class":75},":\n",[35,731,732,735],{"class":37,"line":92},[35,733,734],{"class":71},"        return",[35,736,737],{"class":45}," \"HOLD\"\n",[35,739,740,742,744,746,748,750,753,755,757,759,761,763,766],{"class":37,"line":110},[35,741,700],{"class":71},[35,743,703],{"class":75},[35,745,425],{"class":45},[35,747,191],{"class":75},[35,749,284],{"class":71},[35,751,752],{"class":215}," 0.85",[35,754,715],{"class":71},[35,756,703],{"class":75},[35,758,520],{"class":45},[35,760,191],{"class":75},[35,762,284],{"class":71},[35,764,765],{"class":215}," 30",[35,767,729],{"class":75},[35,769,770,772],{"class":37,"line":126},[35,771,734],{"class":71},[35,773,774],{"class":45}," \"REVIEW\"\n",[35,776,777,780],{"class":37,"line":162},[35,778,779],{"class":71},"    return",[35,781,782],{"class":45}," \"OK\"\n",[35,784,785],{"class":37,"line":177},[35,786,89],{"emptyLinePlaceholder":88},[35,788,789,791,794,796,798,801,804,806,809],{"class":37,"line":182},[35,790,392],{"class":75},[35,792,793],{"class":45},"\"Decision\"",[35,795,191],{"class":75},[35,797,98],{"class":71},[35,799,800],{"class":75}," book.apply(decision, ",[35,802,803],{"class":142},"axis",[35,805,98],{"class":71},[35,807,808],{"class":215},"1",[35,810,107],{"class":75},[35,812,813,815,818,820,822,825,827,830,833,836,839,842,845],{"class":37,"line":221},[35,814,392],{"class":75},[35,816,817],{"class":45},"\"Reason\"",[35,819,191],{"class":75},[35,821,98],{"class":71},[35,823,824],{"class":75}," book.apply(",[35,826,274],{"class":71},[35,828,829],{"class":75}," r: ",[35,831,832],{"class":45},"\"; \"",[35,834,835],{"class":75},".join(x ",[35,837,838],{"class":71},"for",[35,840,841],{"class":75}," x ",[35,843,844],{"class":71},"in",[35,846,847],{"class":75}," [\n",[35,849,850,853,856,860,863,866,869,872,875,878,881,883,885,887,889,891,894,897],{"class":37,"line":238},[35,851,852],{"class":71},"    f",[35,854,855],{"class":45},"\"",[35,857,859],{"class":858},"sSjpA","{",[35,861,862],{"class":75},"r[",[35,864,865],{"class":45},"'Utilisation'",[35,867,868],{"class":75},"]",[35,870,871],{"class":71},":.0%",[35,873,874],{"class":858},"}",[35,876,877],{"class":45}," of limit\"",[35,879,880],{"class":71}," if",[35,882,703],{"class":75},[35,884,425],{"class":45},[35,886,191],{"class":75},[35,888,284],{"class":71},[35,890,752],{"class":215},[35,892,893],{"class":71}," else",[35,895,896],{"class":45}," \"\"",[35,898,899],{"class":75},",\n",[35,901,902,904,906,908,910,913,915,918,920,923,925,927,929,931,933,935,937,939],{"class":37,"line":260},[35,903,852],{"class":71},[35,905,855],{"class":45},[35,907,859],{"class":858},[35,909,862],{"class":75},[35,911,912],{"class":45},"'Overdue_60'",[35,914,868],{"class":75},[35,916,917],{"class":71},":,.0f",[35,919,874],{"class":858},[35,921,922],{"class":45}," over 60 days\"",[35,924,880],{"class":71},[35,926,703],{"class":75},[35,928,720],{"class":45},[35,930,191],{"class":75},[35,932,284],{"class":71},[35,934,287],{"class":215},[35,936,893],{"class":71},[35,938,896],{"class":45},[35,940,899],{"class":75},[35,942,943,945,948,950,953,956,959,962,964,967,969,971,974,976,978,980,983,985,987,990,993,995,997,999],{"class":37,"line":293},[35,944,852],{"class":71},[35,946,947],{"class":45},"\"oldest ",[35,949,859],{"class":858},[35,951,952],{"class":215},"int",[35,954,955],{"class":75},"(r[",[35,957,958],{"class":45},"'Oldest_days'",[35,960,961],{"class":75},"])",[35,963,874],{"class":858},[35,965,966],{"class":45}," days late\"",[35,968,880],{"class":71},[35,970,765],{"class":215},[35,972,973],{"class":71}," \u003C",[35,975,703],{"class":75},[35,977,520],{"class":45},[35,979,191],{"class":75},[35,981,982],{"class":71},"else",[35,984,896],{"class":45},[35,986,191],{"class":75},[35,988,989],{"class":71},"if",[35,991,992],{"class":75}," x), ",[35,994,803],{"class":142},[35,996,98],{"class":71},[35,998,808],{"class":215},[35,1000,107],{"class":75},[35,1002,1003,1005,1008,1010,1012,1014,1017,1020,1022,1024,1027,1029,1032,1035,1037,1039,1041,1043,1045,1047,1049,1051,1053,1055,1057,1059,1061],{"class":37,"line":322},[35,1004,489],{"class":215},[35,1006,1007],{"class":75},"(book.sort_values([",[35,1009,793],{"class":45},[35,1011,139],{"class":75},[35,1013,425],{"class":45},[35,1015,1016],{"class":75},"], ",[35,1018,1019],{"class":142},"ascending",[35,1021,98],{"class":71},[35,1023,148],{"class":75},[35,1025,1026],{"class":215},"True",[35,1028,139],{"class":75},[35,1030,1031],{"class":215},"False",[35,1033,1034],{"class":75},"])[[",[35,1036,395],{"class":45},[35,1038,139],{"class":75},[35,1040,425],{"class":45},[35,1042,139],{"class":75},[35,1044,720],{"class":45},[35,1046,139],{"class":75},[35,1048,520],{"class":45},[35,1050,139],{"class":75},[35,1052,793],{"class":45},[35,1054,139],{"class":75},[35,1056,817],{"class":45},[35,1058,523],{"class":75},[35,1060,481],{"class":215},[35,1062,528],{"class":75},[10,1064,1065,1066,1070,1071,1074,1075,1078,1079,1082],{},"The decision rule is deliberately simple, because the people applying it need to be able to explain it to a customer. An account goes on ",[1067,1068,1069],"strong",{},"hold"," — no new orders shipped until resolved — if its exposure is over the limit, or if anything is more than sixty days overdue. It needs ",[1067,1072,1073],{},"review"," if it is above 85% of its limit or has an invoice more than thirty days late. Everything else is ",[1067,1076,1077],{},"OK",". The reason column spells out which condition triggered the decision, so the credit controller calling the customer knows what the conversation is about. The thresholds are company policy, and changing them is a one-line edit. Some companies add a third tier between review and hold — ",[533,1080,1081],{},"stop new orders, but ship what is already accepted"," — which fits naturally as another branch in the same function.",[10,1084,1085],{},"The interesting accounts are the ones the receivables balance alone would miss. Customer H looks comfortable on invoices alone, but a large open order takes its exposure over the limit; customer F is at only 32% of its limit but has an invoice 78 days overdue, which on its own is reason enough to hold further shipments. Six of the twelve accounts end up on hold — four because open orders push them past their limits, two because of old debts — and one, customer A at 91% of its limit, needs review.",[542,1087,551,1093,551,1096,551,1099,551,1104,551,1109,551,1113,551,1116,551,1120,551,1125,551,1128,551,1131,551,1134,551,1137,551,1140,551,1144,551,1147,551,1150,551,1155],{"viewBox":1088,"role":545,"ariaLabelledBy":1089,"xmlns":549,"style":1092},"4 0 752 108",[1090,1091],"credit-rule-t","credit-rule-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[553,1094,1095],{"id":1090},"The decision rule",[557,1097,1098],{"id":1091},"An account is put on hold if exposure is over its credit limit or any amount is more than sixty days overdue. Otherwise it is marked for review if it is above 85 percent of its limit or has an invoice more than thirty days late. Otherwise it is OK.",[561,1100],{"x":1101,"y":216,"width":1102,"height":1103,"fill":566},"4","752","108",[561,1105],{"x":1106,"y":570,"width":1107,"height":1108,"rx":573,"fill":623,"stroke":624,"style":576},"20.0","216.0","76.5",[578,1110,1112],{"x":1111,"y":581,"style":628},"128.0","Over limit or > 60 days",[578,1114,1115],{"x":1111,"y":572,"style":628},"overdue?",[578,1117,1119],{"x":1111,"y":1118,"style":587},"78.5","HOLD",[37,1121],{"x1":1122,"y1":1123,"x2":1124,"y2":1123,"stroke":593,"style":576},"241.0","54.2","263.0",[595,1126],{"points":1127,"fill":598},"267.0,54.2 258.0,59.2 258.0,49.2",[561,1129],{"x":1130,"y":570,"width":1107,"height":1108,"rx":573,"fill":602,"stroke":603,"style":576},"272.0",[578,1132,1133],{"x":580,"y":581,"style":607},"> 85% of limit or > 30 days",[578,1135,1136],{"x":580,"y":572,"style":607},"late?",[578,1138,1139],{"x":580,"y":1118,"style":587},"REVIEW",[37,1141],{"x1":1142,"y1":1123,"x2":1143,"y2":1123,"stroke":593,"style":576},"493.0","515.0",[595,1145],{"points":1146,"fill":598},"519.0,54.2 510.0,59.2 510.0,49.2",[561,1148],{"x":1149,"y":570,"width":1107,"height":1108,"rx":573,"fill":664,"stroke":665,"style":576},"524.0",[578,1151,1154],{"x":1152,"y":1153,"style":669},"632.0","50.2","Otherwise",[578,1156,1077],{"x":1152,"y":1157,"style":587},"69.8",[542,1159,551,1165,551,1168,551,1171,551,1176,551,1182,551,1186,551,1192,551,1196,551,1200,551,1204,551,1208,551,1212,551,1216,551,1220,551,1224,551,1228,551,1231,551,1234,551,1238,551,1240,551,1242,551,1244,551,1247,551,1249,551,1252,551,1254,551,1257,551,1260,551,1263,551,1265,551,1269,551,1271,551,1274,551,1276,551,1279,551,1281,551,1284,551,1286,551,1289,551,1292,551,1295,551,1297,551,1300,551,1302,551,1305,551,1307,551,1310,551,1312,551,1314,551,1316,551,1319,551,1322,551,1325,551,1327,551,1330,551,1332,551,1335,551,1337,551,1340,551,1342,551,1344,551,1346,551,1349,551,1352,551,1356,551,1358,551,1361,551,1363,551,1366,551,1368,551,1371,551,1373,551,1375,551,1377,551,1380,551,1383,551,1387,551,1389,551,1392,551,1394,551,1397,551,1399,551,1402,551,1404,551,1407,551,1409],{"viewBox":1160,"role":545,"ariaLabelledBy":1161,"xmlns":549,"style":1164},"97 0 565 224",[1162,1163],"credit-book-t","credit-book-d","width:100%;max-width:565px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[553,1166,1167],{"id":1162},"The accounts that need action",[557,1169,1170],{"id":1163},"Customer H is at 111 percent of its limit once a large open order is counted. Customers E, K and B are just over their limits. D and F are on hold for invoices more than sixty days overdue, F at only 32 percent of its limit. Customer A needs review at 91 percent.",[561,1172],{"x":1173,"y":216,"width":1174,"height":1175,"fill":566},"97","565","224",[561,1177],{"x":1178,"y":570,"width":1179,"height":1180,"rx":216,"fill":574,"stroke":575,"style":1181},"113.7","32.0","24.0","stroke-width:1px",[561,1183],{"x":1184,"y":570,"width":1185,"height":1180,"rx":216,"fill":574,"stroke":575,"style":1181},"145.7","84.7",[578,1187,1191],{"x":1188,"y":1189,"style":1190},"188.1","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[561,1193],{"x":1194,"y":570,"width":1195,"height":1180,"rx":216,"fill":574,"stroke":575,"style":1181},"230.4","82.2",[578,1197,1199],{"x":1198,"y":1189,"style":1190},"271.5","B",[561,1201],{"x":1202,"y":570,"width":1203,"height":1180,"rx":216,"fill":574,"stroke":575,"style":1181},"312.6","86.1",[578,1205,1207],{"x":1206,"y":1189,"style":1190},"355.6","C",[561,1209],{"x":1210,"y":570,"width":1211,"height":1180,"rx":216,"fill":574,"stroke":575,"style":1181},"398.7","77.5",[578,1213,1215],{"x":1214,"y":1189,"style":1190},"437.4","D",[561,1217],{"x":1218,"y":570,"width":1219,"height":1180,"rx":216,"fill":574,"stroke":575,"style":1181},"476.1","170.1",[578,1221,1223],{"x":1222,"y":1189,"style":1190},"561.2","E",[561,1225],{"x":1178,"y":1226,"width":1179,"height":1227,"rx":216,"fill":574,"stroke":575,"style":1181},"40.0","28.0",[578,1229,808],{"x":1230,"y":572,"style":1190},"129.7",[561,1232],{"x":1184,"y":1226,"width":1185,"height":1227,"rx":216,"fill":644,"stroke":593,"style":1233},"stroke-width:1.5px",[578,1235,1237],{"x":1188,"y":572,"style":1236},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Customer",[561,1239],{"x":1194,"y":1226,"width":1195,"height":1227,"rx":216,"fill":644,"stroke":593,"style":1233},[578,1241,535],{"x":1198,"y":572,"style":1236},[561,1243],{"x":1202,"y":1226,"width":1203,"height":1227,"rx":216,"fill":644,"stroke":593,"style":1233},[578,1245,1246],{"x":1206,"y":572,"style":1236},"Utilisation",[561,1248],{"x":1210,"y":1226,"width":1211,"height":1227,"rx":216,"fill":644,"stroke":593,"style":1233},[578,1250,1251],{"x":1214,"y":572,"style":1236},"Decision",[561,1253],{"x":1218,"y":1226,"width":1219,"height":1227,"rx":216,"fill":644,"stroke":593,"style":1233},[578,1255,1256],{"x":1222,"y":572,"style":1236},"Reason",[561,1258],{"x":1178,"y":1259,"width":1179,"height":1227,"rx":216,"fill":574,"stroke":575,"style":1181},"68.0",[578,1261,481],{"x":1230,"y":1262,"style":1190},"87.0",[561,1264],{"x":1184,"y":1259,"width":1185,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1266,1268],{"x":1188,"y":1262,"style":1267},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Cust H",[561,1270],{"x":1194,"y":1259,"width":1195,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1272,1273],{"x":1198,"y":1262,"style":1267},"66,688",[561,1275],{"x":1202,"y":1259,"width":1203,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1277,1278],{"x":1206,"y":1262,"style":1267},"111%",[561,1280],{"x":1210,"y":1259,"width":1211,"height":1227,"rx":216,"fill":623,"stroke":624,"style":1233},[578,1282,1119],{"x":1214,"y":1262,"style":1283},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[561,1285],{"x":1218,"y":1259,"width":1219,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1287,1288],{"x":1222,"y":1262,"style":1267},"open order takes it over",[561,1290],{"x":1178,"y":1291,"width":1179,"height":1227,"rx":216,"fill":574,"stroke":575,"style":1181},"96.0",[578,1293,450],{"x":1230,"y":1294,"style":1190},"115.0",[561,1296],{"x":1184,"y":1291,"width":1185,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1298,1299],{"x":1188,"y":1294,"style":1267},"Cust E",[561,1301],{"x":1194,"y":1291,"width":1195,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1303,1304],{"x":1198,"y":1294,"style":1267},"11,040",[561,1306],{"x":1202,"y":1291,"width":1203,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1308,1309],{"x":1206,"y":1294,"style":1267},"110%",[561,1311],{"x":1210,"y":1291,"width":1211,"height":1227,"rx":216,"fill":623,"stroke":624,"style":1233},[578,1313,1119],{"x":1214,"y":1294,"style":1283},[561,1315],{"x":1218,"y":1291,"width":1219,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1317,1318],{"x":1222,"y":1294,"style":1267},"over limit",[561,1320],{"x":1178,"y":1321,"width":1179,"height":1227,"rx":216,"fill":574,"stroke":575,"style":1181},"124.0",[578,1323,1101],{"x":1230,"y":1324,"style":1190},"143.0",[561,1326],{"x":1184,"y":1321,"width":1185,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1328,1329],{"x":1188,"y":1324,"style":1267},"Cust D",[561,1331],{"x":1194,"y":1321,"width":1195,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1333,1334],{"x":1198,"y":1324,"style":1267},"23,656",[561,1336],{"x":1202,"y":1321,"width":1203,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1338,1339],{"x":1206,"y":1324,"style":1267},"79%",[561,1341],{"x":1210,"y":1321,"width":1211,"height":1227,"rx":216,"fill":623,"stroke":624,"style":1233},[578,1343,1119],{"x":1214,"y":1324,"style":1283},[561,1345],{"x":1218,"y":1321,"width":1219,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1347,1348],{"x":1222,"y":1324,"style":1267},"6,889 over 60 days",[561,1350],{"x":1178,"y":1351,"width":1179,"height":1227,"rx":216,"fill":574,"stroke":575,"style":1181},"152.0",[578,1353,1355],{"x":1230,"y":1354,"style":1190},"171.0","5",[561,1357],{"x":1184,"y":1351,"width":1185,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1359,1360],{"x":1188,"y":1354,"style":1267},"Cust F",[561,1362],{"x":1194,"y":1351,"width":1195,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1364,1365],{"x":1198,"y":1354,"style":1267},"12,962",[561,1367],{"x":1202,"y":1351,"width":1203,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1369,1370],{"x":1206,"y":1354,"style":1267},"32%",[561,1372],{"x":1210,"y":1351,"width":1211,"height":1227,"rx":216,"fill":623,"stroke":624,"style":1233},[578,1374,1119],{"x":1214,"y":1354,"style":1283},[561,1376],{"x":1218,"y":1351,"width":1219,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1378,1379],{"x":1222,"y":1354,"style":1267},"4,569 over 60 days",[561,1381],{"x":1178,"y":1382,"width":1179,"height":1227,"rx":216,"fill":574,"stroke":575,"style":1181},"180.0",[578,1384,1386],{"x":1230,"y":1385,"style":1190},"199.0","6",[561,1388],{"x":1184,"y":1382,"width":1185,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1390,1391],{"x":1188,"y":1385,"style":1267},"Cust A",[561,1393],{"x":1194,"y":1382,"width":1195,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1395,1396],{"x":1198,"y":1385,"style":1267},"45,424",[561,1398],{"x":1202,"y":1382,"width":1203,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1400,1401],{"x":1206,"y":1385,"style":1267},"91%",[561,1403],{"x":1210,"y":1382,"width":1211,"height":1227,"rx":216,"fill":602,"stroke":603,"style":1233},[578,1405,1139],{"x":1214,"y":1385,"style":1406},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle",[561,1408],{"x":1218,"y":1382,"width":1219,"height":1227,"rx":216,"fill":566,"stroke":575,"style":1181},[578,1410,1411],{"x":1222,"y":1385,"style":1267},"near limit",[20,1413,1415],{"id":1414},"step-3-the-report-for-the-credit-team","Step 3 — The report for the credit team",[25,1417,1419],{"className":62,"code":1418,"language":64,"meta":30,"style":30},"from openpyxl import load_workbook\nfrom openpyxl.formatting.rule import CellIsRule\nfrom openpyxl.styles import PatternFill\n\norder = {\"HOLD\": 0, \"REVIEW\": 1, \"OK\": 2}\nout = (book.reset_index()\n       .sort_values(by=[\"Decision\", \"Utilisation\"], key=lambda s: s.map(order) if s.name == \"Decision\" else -s)\n       [[\"Customer\", \"Decision\", \"Reason\", \"Credit limit\", \"Receivables\", \"Open orders\", \"Exposure\", \"Utilisation\",\n         \"Headroom\", \"Overdue\", \"Overdue_60\", \"Oldest_days\", \"Terms (days)\"]]\n       .rename(columns={\"Overdue_60\": \"Over 60 days\", \"Oldest_days\": \"Oldest overdue (days)\"}))\npath = \"credit_limits_2026-10-05.xlsx\"\nwith pd.ExcelWriter(path) as xw:\n    out.to_excel(xw, sheet_name=\"Credit book\", index=False)\n    invoices[invoices[\"Days overdue\"] > 0].sort_values(\"Days overdue\", ascending=False).to_excel(xw, sheet_name=\"Overdue invoices\", index=False)\nwb = load_workbook(path)\nws = wb[\"Credit book\"]\nn = ws.max_row\nfor text, colour in [(\"HOLD\", \"FFC7CE\"), (\"REVIEW\", \"FFEB9C\"), (\"OK\", \"C6EFCE\")]:\n    ws.conditional_formatting.add(f\"B2:B{n}\", CellIsRule(operator=\"equal\", formula=[f'\"{text}\"'], fill=PatternFill(\"solid\", bgColor=colour)))\nfor row in ws.iter_rows(min_row=2, min_col=4, max_col=11):\n    for cell in row:\n        cell.number_format = \"0%\" if cell.column == 8 else \"#,##0\"\nwb.save(path)\nprint(out[\"Decision\"].value_counts().to_dict())\n",[32,1420,1421,1434,1446,1458,1462,1501,1511,1559,1596,1621,1652,1662,1675,1699,1745,1755,1769,1779,1820,1897,1938,1952,1978,1984],{"__ignoreMap":30},[35,1422,1423,1426,1429,1431],{"class":37,"line":38},[35,1424,1425],{"class":71},"from",[35,1427,1428],{"class":75}," openpyxl ",[35,1430,72],{"class":71},[35,1432,1433],{"class":75}," load_workbook\n",[35,1435,1436,1438,1441,1443],{"class":37,"line":85},[35,1437,1425],{"class":71},[35,1439,1440],{"class":75}," openpyxl.formatting.rule ",[35,1442,72],{"class":71},[35,1444,1445],{"class":75}," CellIsRule\n",[35,1447,1448,1450,1453,1455],{"class":37,"line":92},[35,1449,1425],{"class":71},[35,1451,1452],{"class":75}," openpyxl.styles ",[35,1454,72],{"class":71},[35,1456,1457],{"class":75}," PatternFill\n",[35,1459,1460],{"class":37,"line":110},[35,1461,89],{"emptyLinePlaceholder":88},[35,1463,1464,1467,1469,1472,1475,1478,1480,1482,1485,1487,1489,1491,1494,1496,1498],{"class":37,"line":126},[35,1465,1466],{"class":75},"order ",[35,1468,98],{"class":71},[35,1470,1471],{"class":75}," {",[35,1473,1474],{"class":45},"\"HOLD\"",[35,1476,1477],{"class":75},": ",[35,1479,216],{"class":215},[35,1481,139],{"class":75},[35,1483,1484],{"class":45},"\"REVIEW\"",[35,1486,1477],{"class":75},[35,1488,808],{"class":215},[35,1490,139],{"class":75},[35,1492,1493],{"class":45},"\"OK\"",[35,1495,1477],{"class":75},[35,1497,481],{"class":215},[35,1499,1500],{"class":75},"}\n",[35,1502,1503,1506,1508],{"class":37,"line":162},[35,1504,1505],{"class":75},"out ",[35,1507,98],{"class":71},[35,1509,1510],{"class":75}," (book.reset_index()\n",[35,1512,1513,1516,1519,1521,1523,1525,1527,1529,1531,1534,1537,1540,1542,1545,1548,1551,1553,1556],{"class":37,"line":177},[35,1514,1515],{"class":75},"       .sort_values(",[35,1517,1518],{"class":142},"by",[35,1520,98],{"class":71},[35,1522,148],{"class":75},[35,1524,793],{"class":45},[35,1526,139],{"class":75},[35,1528,425],{"class":45},[35,1530,1016],{"class":75},[35,1532,1533],{"class":142},"key",[35,1535,1536],{"class":71},"=lambda",[35,1538,1539],{"class":75}," s: s.map(order) ",[35,1541,989],{"class":71},[35,1543,1544],{"class":75}," s.name ",[35,1546,1547],{"class":71},"==",[35,1549,1550],{"class":45}," \"Decision\"",[35,1552,893],{"class":71},[35,1554,1555],{"class":71}," -",[35,1557,1558],{"class":75},"s)\n",[35,1560,1561,1564,1566,1568,1570,1572,1574,1576,1578,1580,1582,1584,1586,1588,1590,1592,1594],{"class":37,"line":182},[35,1562,1563],{"class":75},"       [[",[35,1565,232],{"class":45},[35,1567,139],{"class":75},[35,1569,793],{"class":45},[35,1571,139],{"class":75},[35,1573,817],{"class":45},[35,1575,139],{"class":75},[35,1577,444],{"class":45},[35,1579,139],{"class":75},[35,1581,405],{"class":45},[35,1583,139],{"class":75},[35,1585,374],{"class":45},[35,1587,139],{"class":75},[35,1589,395],{"class":45},[35,1591,139],{"class":75},[35,1593,425],{"class":45},[35,1595,899],{"class":75},[35,1597,1598,1601,1603,1605,1607,1609,1611,1613,1615,1618],{"class":37,"line":221},[35,1599,1600],{"class":45},"         \"Headroom\"",[35,1602,139],{"class":75},[35,1604,515],{"class":45},[35,1606,139],{"class":75},[35,1608,720],{"class":45},[35,1610,139],{"class":75},[35,1612,520],{"class":45},[35,1614,139],{"class":75},[35,1616,1617],{"class":45},"\"Terms (days)\"",[35,1619,1620],{"class":75},"]]\n",[35,1622,1623,1626,1629,1631,1633,1635,1637,1640,1642,1644,1646,1649],{"class":37,"line":238},[35,1624,1625],{"class":75},"       .rename(",[35,1627,1628],{"class":142},"columns",[35,1630,98],{"class":71},[35,1632,859],{"class":75},[35,1634,720],{"class":45},[35,1636,1477],{"class":75},[35,1638,1639],{"class":45},"\"Over 60 days\"",[35,1641,139],{"class":75},[35,1643,520],{"class":45},[35,1645,1477],{"class":75},[35,1647,1648],{"class":45},"\"Oldest overdue (days)\"",[35,1650,1651],{"class":75},"}))\n",[35,1653,1654,1657,1659],{"class":37,"line":260},[35,1655,1656],{"class":75},"path ",[35,1658,98],{"class":71},[35,1660,1661],{"class":45}," \"credit_limits_2026-10-05.xlsx\"\n",[35,1663,1664,1667,1670,1672],{"class":37,"line":293},[35,1665,1666],{"class":71},"with",[35,1668,1669],{"class":75}," pd.ExcelWriter(path) ",[35,1671,79],{"class":71},[35,1673,1674],{"class":75}," xw:\n",[35,1676,1677,1680,1683,1685,1688,1690,1693,1695,1697],{"class":37,"line":322},[35,1678,1679],{"class":75},"    out.to_excel(xw, ",[35,1681,1682],{"class":142},"sheet_name",[35,1684,98],{"class":71},[35,1686,1687],{"class":45},"\"Credit book\"",[35,1689,139],{"class":75},[35,1691,1692],{"class":142},"index",[35,1694,98],{"class":71},[35,1696,1031],{"class":215},[35,1698,107],{"class":75},[35,1700,1701,1704,1706,1708,1710,1712,1715,1717,1719,1721,1723,1725,1728,1730,1732,1735,1737,1739,1741,1743],{"class":37,"line":342},[35,1702,1703],{"class":75},"    invoices[invoices[",[35,1705,188],{"class":45},[35,1707,191],{"class":75},[35,1709,284],{"class":71},[35,1711,287],{"class":215},[35,1713,1714],{"class":75},"].sort_values(",[35,1716,188],{"class":45},[35,1718,139],{"class":75},[35,1720,1019],{"class":142},[35,1722,98],{"class":71},[35,1724,1031],{"class":215},[35,1726,1727],{"class":75},").to_excel(xw, ",[35,1729,1682],{"class":142},[35,1731,98],{"class":71},[35,1733,1734],{"class":45},"\"Overdue invoices\"",[35,1736,139],{"class":75},[35,1738,1692],{"class":142},[35,1740,98],{"class":71},[35,1742,1031],{"class":215},[35,1744,107],{"class":75},[35,1746,1747,1750,1752],{"class":37,"line":357},[35,1748,1749],{"class":75},"wb ",[35,1751,98],{"class":71},[35,1753,1754],{"class":75}," load_workbook(path)\n",[35,1756,1757,1760,1762,1765,1767],{"class":37,"line":379},[35,1758,1759],{"class":75},"ws ",[35,1761,98],{"class":71},[35,1763,1764],{"class":75}," wb[",[35,1766,1687],{"class":45},[35,1768,417],{"class":75},[35,1770,1771,1774,1776],{"class":37,"line":389},[35,1772,1773],{"class":75},"n ",[35,1775,98],{"class":71},[35,1777,1778],{"class":75}," ws.max_row\n",[35,1780,1781,1783,1786,1788,1791,1793,1795,1798,1801,1803,1805,1808,1810,1812,1814,1817],{"class":37,"line":420},[35,1782,838],{"class":71},[35,1784,1785],{"class":75}," text, colour ",[35,1787,844],{"class":71},[35,1789,1790],{"class":75}," [(",[35,1792,1474],{"class":45},[35,1794,139],{"class":75},[35,1796,1797],{"class":45},"\"FFC7CE\"",[35,1799,1800],{"class":75},"), (",[35,1802,1484],{"class":45},[35,1804,139],{"class":75},[35,1806,1807],{"class":45},"\"FFEB9C\"",[35,1809,1800],{"class":75},[35,1811,1493],{"class":45},[35,1813,139],{"class":75},[35,1815,1816],{"class":45},"\"C6EFCE\"",[35,1818,1819],{"class":75},")]:\n",[35,1821,1822,1825,1828,1831,1833,1836,1838,1840,1843,1846,1848,1851,1853,1856,1858,1860,1862,1865,1867,1869,1871,1874,1876,1879,1881,1884,1887,1889,1892,1894],{"class":37,"line":455},[35,1823,1824],{"class":75},"    ws.conditional_formatting.add(",[35,1826,1827],{"class":71},"f",[35,1829,1830],{"class":45},"\"B2:B",[35,1832,859],{"class":858},[35,1834,1835],{"class":75},"n",[35,1837,874],{"class":858},[35,1839,855],{"class":45},[35,1841,1842],{"class":75},", CellIsRule(",[35,1844,1845],{"class":142},"operator",[35,1847,98],{"class":71},[35,1849,1850],{"class":45},"\"equal\"",[35,1852,139],{"class":75},[35,1854,1855],{"class":142},"formula",[35,1857,98],{"class":71},[35,1859,148],{"class":75},[35,1861,1827],{"class":71},[35,1863,1864],{"class":45},"'\"",[35,1866,859],{"class":858},[35,1868,578],{"class":75},[35,1870,874],{"class":858},[35,1872,1873],{"class":45},"\"'",[35,1875,1016],{"class":75},[35,1877,1878],{"class":142},"fill",[35,1880,98],{"class":71},[35,1882,1883],{"class":75},"PatternFill(",[35,1885,1886],{"class":45},"\"solid\"",[35,1888,139],{"class":75},[35,1890,1891],{"class":142},"bgColor",[35,1893,98],{"class":71},[35,1895,1896],{"class":75},"colour)))\n",[35,1898,1899,1901,1904,1906,1909,1912,1914,1916,1918,1921,1923,1925,1927,1930,1932,1935],{"class":37,"line":486},[35,1900,838],{"class":71},[35,1902,1903],{"class":75}," row ",[35,1905,844],{"class":71},[35,1907,1908],{"class":75}," ws.iter_rows(",[35,1910,1911],{"class":142},"min_row",[35,1913,98],{"class":71},[35,1915,481],{"class":215},[35,1917,139],{"class":75},[35,1919,1920],{"class":142},"min_col",[35,1922,98],{"class":71},[35,1924,1101],{"class":215},[35,1926,139],{"class":75},[35,1928,1929],{"class":142},"max_col",[35,1931,98],{"class":71},[35,1933,1934],{"class":215},"11",[35,1936,1937],{"class":75},"):\n",[35,1939,1941,1944,1947,1949],{"class":37,"line":1940},21,[35,1942,1943],{"class":71},"    for",[35,1945,1946],{"class":75}," cell ",[35,1948,844],{"class":71},[35,1950,1951],{"class":75}," row:\n",[35,1953,1955,1958,1960,1963,1965,1968,1970,1973,1975],{"class":37,"line":1954},22,[35,1956,1957],{"class":75},"        cell.number_format ",[35,1959,98],{"class":71},[35,1961,1962],{"class":45}," \"0%\"",[35,1964,880],{"class":71},[35,1966,1967],{"class":75}," cell.column ",[35,1969,1547],{"class":71},[35,1971,1972],{"class":215}," 8",[35,1974,893],{"class":71},[35,1976,1977],{"class":45}," \"#,##0\"\n",[35,1979,1981],{"class":37,"line":1980},23,[35,1982,1983],{"class":75},"wb.save(path)\n",[35,1985,1987,1989,1992,1994],{"class":37,"line":1986},24,[35,1988,489],{"class":215},[35,1990,1991],{"class":75},"(out[",[35,1993,793],{"class":45},[35,1995,1996],{"class":75},"].value_counts().to_dict())\n",[10,1998,1999,2000,18],{},"The credit book lists customers in order of urgency — holds first, then reviews, then the rest, each group sorted by utilisation — with the decision coloured and the reason next to it. The money columns show how exposure is made up, and headroom tells sales how much more each customer can order. A second sheet lists every overdue invoice, oldest first, which is the credit controller's call list. Sending the report each morning to sales and credit together removes the most common source of friction between them: sales finding out about a hold only when an order is stopped at the warehouse. Chasing the overdue balances is covered in ",[14,2001,2003],{"href":2002},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-credit-control-dunning-letter-run\u002F","Build a Credit Control Dunning Letter Run",[20,2005,2007],{"id":2006},"keeping-the-limits-themselves-current","Keeping the limits themselves current",[10,2009,2010],{},"The report is only as good as the limits it checks against, and limits set years ago drift away from reality in both directions. A customer who has grown and always pays on time ends up on hold every month because its limit was set when it was small, which irritates sales and the customer for no benefit. A customer whose business has weakened keeps a generous limit nobody has looked at. Two columns make the review easy to add: the highest utilisation over the last twelve months and the average days late over the same period. A customer that regularly runs above 90% while paying on time is a candidate for a higher limit; one that pays late and rarely uses half its limit can safely have a lower one. Reviewing the limits quarterly with those two numbers, and recording when each limit was last reviewed, keeps the hold list meaningful.",[20,2012,2014],{"id":2013},"common-pitfalls","Common pitfalls",[2016,2017,2018,2025,2031,2037,2043],"ul",{},[2019,2020,2021,2024],"li",{},[1067,2022,2023],{},"Receivables only."," Add accepted but uninvoiced orders; they become receivables within days.",[2019,2026,2027,2030],{},[1067,2028,2029],{},"Utilisation without behaviour."," Combine it with overdue balances; late payers are the real risk.",[2019,2032,2033,2036],{},[1067,2034,2035],{},"Customers with nothing open dropped."," Keep them, with zero exposure and full headroom.",[2019,2038,2039,2042],{},[1067,2040,2041],{},"Opaque decisions."," Show the reason for every hold or review.",[2019,2044,2045,2048],{},[1067,2046,2047],{},"Thresholds buried in code."," Keep them as named values that policy can change.",[20,2050,2052],{"id":2051},"conclusion","Conclusion",[10,2054,2055],{},"A useful credit limit report measures exposure as open invoices plus open orders, compares it with each customer's limit, and combines that with how overdue the account is. A simple, explainable rule — hold if over the limit or more than sixty days overdue, review above 85% of the limit or thirty days late — sorts the book by urgency, and the headroom column tells sales what each customer can still order.",[20,2057,2059],{"id":2058},"frequently-asked-questions","Frequently asked questions",[10,2061,2062,2065],{},[1067,2063,2064],{},"How do I calculate credit exposure in pandas?","\nSum each customer's open invoice amounts and their accepted but uninvoiced orders, then join them to the customer list.",[10,2067,2068,2071],{},[1067,2069,2070],{},"Why include open orders in credit exposure?","\nThey will become invoices once shipped, so ignoring them understates how much the customer will owe.",[10,2073,2074,2077],{},[1067,2075,2076],{},"When should an account be put on credit hold?","\nTypical rules are exposure above the limit or any invoice more than a set number of days overdue, such as sixty.",[10,2079,2080,2083],{},[1067,2081,2082],{},"How do I calculate headroom for new orders?","\nSubtract the customer's exposure from their credit limit; an order larger than the headroom needs approval.",[20,2085,2087],{"id":2086},"related","Related",[2016,2089,2090,2096,2103,2108],{},[2019,2091,2092,2093,2095],{},"Up one level: ",[14,2094,17],{"href":16}," — finance and operations reports.",[2019,2097,2098,2102],{},[14,2099,2101],{"href":2100},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-cash-flow-forecast-report\u002F","Build a Cash Flow Forecast Report"," — when the receivables turn into cash.",[2019,2104,2105,2107],{},[14,2106,2003],{"href":2002}," — chasing the overdue invoices.",[2019,2109,2110,2114],{},[14,2111,2113],{"href":2112},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-an-entire-row-based-on-a-cell-value-with-openpyxl\u002F","Highlight an Entire Row Based on a Cell Value with openpyxl"," — colouring by decision.",[2116,2117,2118],"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 .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":30,"searchDepth":85,"depth":85,"links":2120},[2121,2122,2123,2124,2125,2126,2127,2128,2129],{"id":22,"depth":85,"text":23},{"id":58,"depth":85,"text":59},{"id":677,"depth":85,"text":678},{"id":1414,"depth":85,"text":1415},{"id":2006,"depth":85,"text":2007},{"id":2013,"depth":85,"text":2014},{"id":2051,"depth":85,"text":2052},{"id":2058,"depth":85,"text":2059},{"id":2086,"depth":85,"text":2087},"2026-10-02","Check every customer's exposure against their credit limit with pandas: add open invoices to orders accepted but not yet invoiced, measure utilisation of the limit, combine it with how overdue the account is, decide which accounts to put on hold, which to review and which to leave alone, show what headroom remains for new orders, and write a colour-coded Excel report for the credit team.","md",[2134,2136,2138,2140],{"q":2064,"a":2135},"Sum each customer's open invoice amounts and their accepted but uninvoiced orders, then join them to the customer list.",{"q":2070,"a":2137},"They will become invoices once shipped, so ignoring them understates how much the customer will owe.",{"q":2076,"a":2139},"Typical rules are exposure above the limit or any invoice more than a set number of days overdue, such as sixty.",{"q":2082,"a":2141},"Subtract the customer's exposure from their credit limit; an order larger than the headroom needs approval.","customer credit limit report python",null,{"breadcrumb":2145},[2146,2148,2151],{"name":2147,"item":439},"Home",{"name":2149,"item":2150},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":2152,"item":16},"Building Finance and Operations Reports in Excel with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report",{"title":2155,"description":2156},"Build a Customer Credit Limit Report in Excel with Python","Credit limit report with pandas: exposure from open invoices plus open orders, limit utilisation, overdue balances, hold and review rules, remaining headroom and a colour-coded Excel sheet.","build-a-customer-credit-limit-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-credit-limit-report\u002Findex",[2160,2161],"report-layouts","conditional-formatting","how-to","j88ODS48AbOothsM0l2MffJgZc6bEQ-AR_cSazmS9gQ",[2165,2169],{"title":2166,"path":2167,"stem":2168,"children":-1},"Build a Customer Churn Report from Subscription Data","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-churn-report-from-subscription-data","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-churn-report-from-subscription-data\u002Findex",{"title":2170,"path":2171,"stem":2172,"children":-1},"Build a Customer Profitability Report","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-profitability-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-customer-profitability-report\u002Findex",[2174,2176],{"path":2175,"title":2152},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports",{"path":2177,"title":2149},"\u002Fautomating-reporting-workflows",[],[2180,2184,2187,2190,2193,2196],{"path":2181,"title":2182,"kind":2183},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards","Building Multi-Sheet Excel Dashboards","guide",{"path":2185,"title":2186,"kind":2162},"\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":2188,"title":2189,"kind":2162},"\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":2191,"title":2192,"kind":2162},"\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":2194,"title":2195,"kind":2162},"\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":2197,"title":2198,"kind":2162},"\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",1790972121351]