[{"data":1,"prerenderedAt":2207},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report":2172,"ancestors:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report":2181,"children:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report":2186,"related:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report":2187},{"id":4,"title":5,"body":6,"dateModified":2138,"datePublished":2138,"description":2139,"extension":2140,"faq":2141,"keyword":2150,"kind":2151,"meta":2152,"navigation":97,"path":2161,"seo":2162,"slug":2165,"stem":2166,"topics":2167,"type":2170,"__hash__":2171},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report\u002Findex.md","Build an Inventory Turnover and Days on Hand Report",{"type":7,"value":8,"toc":2126},"minimark",[9,28,33,62,65,69,470,477,481,484,819,838,1036,1040,1358,1361,1442,1449,1566,1570,2007,2010,2014,2017,2021,2056,2060,2063,2067,2073,2079,2085,2091,2095,2122],[10,11,12,13,17,18,21,22,27],"p",{},"Inventory is cash sitting on shelves. Too little and orders cannot be fulfilled; too much and money is tied up, storage costs rise and stock goes out of date. Two measures summarise the balance. ",[14,15,16],"em",{},"Inventory turnover"," — cost of goods sold over a year divided by the average value of stock held — says how many times the stock was sold and replaced; higher means leaner. ",[14,19,20],{},"Days on hand"," — how many days the current stock would last at the current rate of use — says how long today's stock will sit before it is consumed. Both are simple ratios, and both are usually wrong in hand-built spreadsheets because they use a single year-end stock figure instead of an average, or annual usage instead of recent usage. This page computes them properly per item from monthly stock snapshots and usage, classifies items as fast, slow-moving or dead, values the cash tied up in each class, summarises by category against targets and writes the report to Excel. It belongs to ",[23,24,26],"a",{"href":25},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002F","Building Finance and Operations Reports",".",[29,30,32],"h2",{"id":31},"prerequisites","Prerequisites",[34,35,40],"pre",{"className":36,"code":37,"language":38,"meta":39,"style":39},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[41,42,43],"code",{"__ignoreMap":39},[44,45,48,52,56,59],"span",{"class":46,"line":47},"line",1,[44,49,51],{"class":50},"sMTad","pip",[44,53,55],{"class":54},"srMev"," install",[44,57,58],{"class":54}," pandas",[44,60,61],{"class":54}," openpyxl\n",[10,63,64],{},"The workbook holds an item list with category and unit cost, twelve months of usage per item (units issued to sales or production) and month-end stock quantities per item, October 2025 to September 2026. Valuing everything at a single standard unit cost keeps the example clear; with costed movements the same calculations apply to values directly.",[29,66,68],{"id":67},"step-1-annual-cogs-and-average-inventory-per-item","Step 1 — Annual COGS and average inventory per item",[34,70,74],{"className":71,"code":72,"language":73,"meta":39,"style":39},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nitems = pd.read_excel(\"inventory.xlsx\", sheet_name=\"Items\").set_index(\"SKU\")\nusage = pd.read_excel(\"inventory.xlsx\", sheet_name=\"Usage\")\nstock = pd.read_excel(\"inventory.xlsx\", sheet_name=\"Month-end stock\")\n\nuse = usage.pivot_table(index=\"SKU\", columns=\"Month\", values=\"Qty used\", aggfunc=\"sum\")\nonhand = stock.pivot_table(index=\"SKU\", columns=\"Month\", values=\"Qty on hand\", aggfunc=\"sum\")\nr = items.copy()\nr[\"Annual COGS\"] = use.sum(axis=1) * r[\"Unit cost\"]\nr[\"Average inventory\"] = onhand.mean(axis=1) * r[\"Unit cost\"]\nr[\"Stock now\"] = onhand.iloc[:, -1] * r[\"Unit cost\"]\nr[\"Turnover\"] = (r[\"Annual COGS\"] \u002F r[\"Average inventory\"]).where(r[\"Average inventory\"] > 0)\nprint(r[[\"Category\", \"Annual COGS\", \"Average inventory\", \"Stock now\", \"Turnover\"]].round(1).to_string())\n","python",[41,75,76,92,99,135,158,181,186,236,280,291,332,363,393,434],{"__ignoreMap":39},[44,77,78,82,86,89],{"class":46,"line":47},[44,79,81],{"class":80},"s-kum","import",[44,83,85],{"class":84},"skGVy"," pandas ",[44,87,88],{"class":80},"as",[44,90,91],{"class":84}," pd\n",[44,93,95],{"class":46,"line":94},2,[44,96,98],{"emptyLinePlaceholder":97},true,"\n",[44,100,102,105,108,111,114,117,121,123,126,129,132],{"class":46,"line":101},3,[44,103,104],{"class":84},"items ",[44,106,107],{"class":80},"=",[44,109,110],{"class":84}," pd.read_excel(",[44,112,113],{"class":54},"\"inventory.xlsx\"",[44,115,116],{"class":84},", ",[44,118,120],{"class":119},"sa561","sheet_name",[44,122,107],{"class":80},[44,124,125],{"class":54},"\"Items\"",[44,127,128],{"class":84},").set_index(",[44,130,131],{"class":54},"\"SKU\"",[44,133,134],{"class":84},")\n",[44,136,138,141,143,145,147,149,151,153,156],{"class":46,"line":137},4,[44,139,140],{"class":84},"usage ",[44,142,107],{"class":80},[44,144,110],{"class":84},[44,146,113],{"class":54},[44,148,116],{"class":84},[44,150,120],{"class":119},[44,152,107],{"class":80},[44,154,155],{"class":54},"\"Usage\"",[44,157,134],{"class":84},[44,159,161,164,166,168,170,172,174,176,179],{"class":46,"line":160},5,[44,162,163],{"class":84},"stock ",[44,165,107],{"class":80},[44,167,110],{"class":84},[44,169,113],{"class":54},[44,171,116],{"class":84},[44,173,120],{"class":119},[44,175,107],{"class":80},[44,177,178],{"class":54},"\"Month-end stock\"",[44,180,134],{"class":84},[44,182,184],{"class":46,"line":183},6,[44,185,98],{"emptyLinePlaceholder":97},[44,187,189,192,194,197,200,202,204,206,209,211,214,216,219,221,224,226,229,231,234],{"class":46,"line":188},7,[44,190,191],{"class":84},"use ",[44,193,107],{"class":80},[44,195,196],{"class":84}," usage.pivot_table(",[44,198,199],{"class":119},"index",[44,201,107],{"class":80},[44,203,131],{"class":54},[44,205,116],{"class":84},[44,207,208],{"class":119},"columns",[44,210,107],{"class":80},[44,212,213],{"class":54},"\"Month\"",[44,215,116],{"class":84},[44,217,218],{"class":119},"values",[44,220,107],{"class":80},[44,222,223],{"class":54},"\"Qty used\"",[44,225,116],{"class":84},[44,227,228],{"class":119},"aggfunc",[44,230,107],{"class":80},[44,232,233],{"class":54},"\"sum\"",[44,235,134],{"class":84},[44,237,239,242,244,247,249,251,253,255,257,259,261,263,265,267,270,272,274,276,278],{"class":46,"line":238},8,[44,240,241],{"class":84},"onhand ",[44,243,107],{"class":80},[44,245,246],{"class":84}," stock.pivot_table(",[44,248,199],{"class":119},[44,250,107],{"class":80},[44,252,131],{"class":54},[44,254,116],{"class":84},[44,256,208],{"class":119},[44,258,107],{"class":80},[44,260,213],{"class":54},[44,262,116],{"class":84},[44,264,218],{"class":119},[44,266,107],{"class":80},[44,268,269],{"class":54},"\"Qty on hand\"",[44,271,116],{"class":84},[44,273,228],{"class":119},[44,275,107],{"class":80},[44,277,233],{"class":54},[44,279,134],{"class":84},[44,281,283,286,288],{"class":46,"line":282},9,[44,284,285],{"class":84},"r ",[44,287,107],{"class":80},[44,289,290],{"class":84}," items.copy()\n",[44,292,294,297,300,303,305,308,311,313,317,320,323,326,329],{"class":46,"line":293},10,[44,295,296],{"class":84},"r[",[44,298,299],{"class":54},"\"Annual COGS\"",[44,301,302],{"class":84},"] ",[44,304,107],{"class":80},[44,306,307],{"class":84}," use.sum(",[44,309,310],{"class":119},"axis",[44,312,107],{"class":80},[44,314,316],{"class":315},"sP0c6","1",[44,318,319],{"class":84},") ",[44,321,322],{"class":80},"*",[44,324,325],{"class":84}," r[",[44,327,328],{"class":54},"\"Unit cost\"",[44,330,331],{"class":84},"]\n",[44,333,335,337,340,342,344,347,349,351,353,355,357,359,361],{"class":46,"line":334},11,[44,336,296],{"class":84},[44,338,339],{"class":54},"\"Average inventory\"",[44,341,302],{"class":84},[44,343,107],{"class":80},[44,345,346],{"class":84}," onhand.mean(",[44,348,310],{"class":119},[44,350,107],{"class":80},[44,352,316],{"class":315},[44,354,319],{"class":84},[44,356,322],{"class":80},[44,358,325],{"class":84},[44,360,328],{"class":54},[44,362,331],{"class":84},[44,364,366,368,371,373,375,378,381,383,385,387,389,391],{"class":46,"line":365},12,[44,367,296],{"class":84},[44,369,370],{"class":54},"\"Stock now\"",[44,372,302],{"class":84},[44,374,107],{"class":80},[44,376,377],{"class":84}," onhand.iloc[:, ",[44,379,380],{"class":80},"-",[44,382,316],{"class":315},[44,384,302],{"class":84},[44,386,322],{"class":80},[44,388,325],{"class":84},[44,390,328],{"class":54},[44,392,331],{"class":84},[44,394,396,398,401,403,405,408,410,412,415,417,419,422,424,426,429,432],{"class":46,"line":395},13,[44,397,296],{"class":84},[44,399,400],{"class":54},"\"Turnover\"",[44,402,302],{"class":84},[44,404,107],{"class":80},[44,406,407],{"class":84}," (r[",[44,409,299],{"class":54},[44,411,302],{"class":84},[44,413,414],{"class":80},"\u002F",[44,416,325],{"class":84},[44,418,339],{"class":54},[44,420,421],{"class":84},"]).where(r[",[44,423,339],{"class":54},[44,425,302],{"class":84},[44,427,428],{"class":80},">",[44,430,431],{"class":315}," 0",[44,433,134],{"class":84},[44,435,437,440,443,446,448,450,452,454,456,458,460,462,465,467],{"class":46,"line":436},14,[44,438,439],{"class":315},"print",[44,441,442],{"class":84},"(r[[",[44,444,445],{"class":54},"\"Category\"",[44,447,116],{"class":84},[44,449,299],{"class":54},[44,451,116],{"class":84},[44,453,339],{"class":54},[44,455,116],{"class":84},[44,457,370],{"class":54},[44,459,116],{"class":84},[44,461,400],{"class":54},[44,463,464],{"class":84},"]].round(",[44,466,316],{"class":315},[44,468,469],{"class":84},").to_string())\n",[10,471,472,473,476],{},"Turnover uses the ",[14,474,475],{},"average"," of the twelve month-end values rather than the year-end figure, because stock levels swing — an item replenished just before the year end would look slow-moving on a single snapshot. Annual cost of goods sold is a year's usage at cost. The result reads directly: most items turn over four to five times a year, roughly every ten to twelve weeks, while the bracket K-510 turns over barely a tenth of a time and two items — the tool T-610 and the packaging P-710 — not at all: they were not used once in twelve months.",[29,478,480],{"id":479},"step-2-days-on-hand-from-recent-usage","Step 2 — Days on hand from recent usage",[10,482,483],{},"Turnover looks backwards over the whole year. Days on hand looks forward: how long will today's stock last at the rate it is being used now?",[34,485,487],{"className":71,"code":486,"language":73,"meta":39,"style":39},"recent_daily = use.iloc[:, -3:].sum(axis=1) \u002F 91                     # last three months, about 91 days\nr[\"Daily usage (units)\"] = recent_daily\nr[\"Days on hand\"] = (onhand.iloc[:, -1] \u002F recent_daily).where(recent_daily > 0)\nmonths_since_use = use.apply(lambda row: (row[::-1] > 0).values.argmax() if (row > 0).any() else len(row), axis=1)\nr[\"Months since last use\"] = months_since_use\n\ndef stock_class(x):\n    if x[\"Stock now\"] == 0:\n        return \"No stock\"\n    if x[\"Months since last use\"] >= 6:\n        return \"Dead\"\n    if x[\"Days on hand\"] > 180:\n        return \"Slow-moving\"\n    return \"Healthy\" if x[\"Days on hand\"] >= 14 else \"Low cover\"\n\nr[\"Class\"] = r.apply(stock_class, axis=1)\nprint(r[[\"Stock now\", \"Days on hand\", \"Months since last use\", \"Class\"]].round(0).to_string())\n",[41,488,489,524,538,569,628,642,646,658,678,686,704,711,728,735,763,768,791],{"__ignoreMap":39},[44,490,491,494,496,499,501,504,507,509,511,513,515,517,520],{"class":46,"line":47},[44,492,493],{"class":84},"recent_daily ",[44,495,107],{"class":80},[44,497,498],{"class":84}," use.iloc[:, ",[44,500,380],{"class":80},[44,502,503],{"class":315},"3",[44,505,506],{"class":84},":].sum(",[44,508,310],{"class":119},[44,510,107],{"class":80},[44,512,316],{"class":315},[44,514,319],{"class":84},[44,516,414],{"class":80},[44,518,519],{"class":315}," 91",[44,521,523],{"class":522},"s-wDw","                     # last three months, about 91 days\n",[44,525,526,528,531,533,535],{"class":46,"line":94},[44,527,296],{"class":84},[44,529,530],{"class":54},"\"Daily usage (units)\"",[44,532,302],{"class":84},[44,534,107],{"class":80},[44,536,537],{"class":84}," recent_daily\n",[44,539,540,542,545,547,549,552,554,556,558,560,563,565,567],{"class":46,"line":101},[44,541,296],{"class":84},[44,543,544],{"class":54},"\"Days on hand\"",[44,546,302],{"class":84},[44,548,107],{"class":80},[44,550,551],{"class":84}," (onhand.iloc[:, ",[44,553,380],{"class":80},[44,555,316],{"class":315},[44,557,302],{"class":84},[44,559,414],{"class":80},[44,561,562],{"class":84}," recent_daily).where(recent_daily ",[44,564,428],{"class":80},[44,566,431],{"class":315},[44,568,134],{"class":84},[44,570,571,574,576,579,582,585,587,589,591,593,595,598,601,604,606,608,611,614,617,620,622,624,626],{"class":46,"line":137},[44,572,573],{"class":84},"months_since_use ",[44,575,107],{"class":80},[44,577,578],{"class":84}," use.apply(",[44,580,581],{"class":80},"lambda",[44,583,584],{"class":84}," row: (row[::",[44,586,380],{"class":80},[44,588,316],{"class":315},[44,590,302],{"class":84},[44,592,428],{"class":80},[44,594,431],{"class":315},[44,596,597],{"class":84},").values.argmax() ",[44,599,600],{"class":80},"if",[44,602,603],{"class":84}," (row ",[44,605,428],{"class":80},[44,607,431],{"class":315},[44,609,610],{"class":84},").any() ",[44,612,613],{"class":80},"else",[44,615,616],{"class":315}," len",[44,618,619],{"class":84},"(row), ",[44,621,310],{"class":119},[44,623,107],{"class":80},[44,625,316],{"class":315},[44,627,134],{"class":84},[44,629,630,632,635,637,639],{"class":46,"line":160},[44,631,296],{"class":84},[44,633,634],{"class":54},"\"Months since last use\"",[44,636,302],{"class":84},[44,638,107],{"class":80},[44,640,641],{"class":84}," months_since_use\n",[44,643,644],{"class":46,"line":183},[44,645,98],{"emptyLinePlaceholder":97},[44,647,648,651,655],{"class":46,"line":188},[44,649,650],{"class":80},"def",[44,652,654],{"class":653},"s_Opv"," stock_class",[44,656,657],{"class":84},"(x):\n",[44,659,660,663,666,668,670,673,675],{"class":46,"line":238},[44,661,662],{"class":80},"    if",[44,664,665],{"class":84}," x[",[44,667,370],{"class":54},[44,669,302],{"class":84},[44,671,672],{"class":80},"==",[44,674,431],{"class":315},[44,676,677],{"class":84},":\n",[44,679,680,683],{"class":46,"line":282},[44,681,682],{"class":80},"        return",[44,684,685],{"class":54}," \"No stock\"\n",[44,687,688,690,692,694,696,699,702],{"class":46,"line":293},[44,689,662],{"class":80},[44,691,665],{"class":84},[44,693,634],{"class":54},[44,695,302],{"class":84},[44,697,698],{"class":80},">=",[44,700,701],{"class":315}," 6",[44,703,677],{"class":84},[44,705,706,708],{"class":46,"line":334},[44,707,682],{"class":80},[44,709,710],{"class":54}," \"Dead\"\n",[44,712,713,715,717,719,721,723,726],{"class":46,"line":365},[44,714,662],{"class":80},[44,716,665],{"class":84},[44,718,544],{"class":54},[44,720,302],{"class":84},[44,722,428],{"class":80},[44,724,725],{"class":315}," 180",[44,727,677],{"class":84},[44,729,730,732],{"class":46,"line":395},[44,731,682],{"class":80},[44,733,734],{"class":54}," \"Slow-moving\"\n",[44,736,737,740,743,746,748,750,752,754,757,760],{"class":46,"line":436},[44,738,739],{"class":80},"    return",[44,741,742],{"class":54}," \"Healthy\"",[44,744,745],{"class":80}," if",[44,747,665],{"class":84},[44,749,544],{"class":54},[44,751,302],{"class":84},[44,753,698],{"class":80},[44,755,756],{"class":315}," 14",[44,758,759],{"class":80}," else",[44,761,762],{"class":54}," \"Low cover\"\n",[44,764,766],{"class":46,"line":765},15,[44,767,98],{"emptyLinePlaceholder":97},[44,769,771,773,776,778,780,783,785,787,789],{"class":46,"line":770},16,[44,772,296],{"class":84},[44,774,775],{"class":54},"\"Class\"",[44,777,302],{"class":84},[44,779,107],{"class":80},[44,781,782],{"class":84}," r.apply(stock_class, ",[44,784,310],{"class":119},[44,786,107],{"class":80},[44,788,316],{"class":315},[44,790,134],{"class":84},[44,792,794,796,798,800,802,804,806,808,810,812,814,817],{"class":46,"line":793},17,[44,795,439],{"class":315},[44,797,442],{"class":84},[44,799,370],{"class":54},[44,801,116],{"class":84},[44,803,544],{"class":54},[44,805,116],{"class":84},[44,807,634],{"class":54},[44,809,116],{"class":84},[44,811,775],{"class":54},[44,813,464],{"class":84},[44,815,816],{"class":315},"0",[44,818,469],{"class":84},[10,820,821,822,825,826,829,830,833,834,837],{},"Using the last three months' usage, rather than the year's, makes days on hand respond to change: an item whose demand has picked up recently shows fewer days of cover than its annual average would suggest. Items are then classified with simple, explainable rules: no use for six months or more is ",[14,823,824],{},"dead"," stock, more than 180 days of cover is ",[14,827,828],{},"slow-moving",", under two weeks is ",[14,831,832],{},"low cover"," and worth checking against lead times, and the rest is ",[14,835,836],{},"healthy",". K-510 is an interesting case — unused for nine months and then used again — so it is no longer dead, but at its recent rate of use the stock on hand would last about 1,400 days, nearly four years, which makes it the clearest slow-moving item. The two items never used are dead.",[839,840,848,849,848,853,848,857,848,864,848,874,848,885,848,891,848,895,848,899,848,902,848,907,848,916,848,921,848,927,848,933,848,937,848,942,848,945,848,948,848,952,848,955,848,960,848,967,848,971,848,974,848,977,848,980,848,984,848,987,848,991,848,998,848,1002,848,1005,848,1007,848,1010,848,1014,848,1017,848,1022,848,1029,848,1033],"svg",{"viewBox":841,"role":842,"ariaLabelledBy":843,"xmlns":846,"style":847},"4 0 752 272","img",[844,845],"inv-class-t","inv-class-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  ",[850,851,852],"title",{"id":844},"How each item is classified",[854,855,856],"desc",{"id":845},"Items with no use for six months or more are dead stock. Items with more than 180 days of cover at recent usage are slow-moving. Items with less than 14 days of cover are low cover. The rest are healthy.",[858,859],"rect",{"x":860,"y":816,"width":861,"height":862,"fill":863},"4","752","272","#ffffff",[858,865],{"x":866,"y":867,"width":868,"height":869,"rx":870,"fill":871,"stroke":872,"style":873},"240.0","16.0","280.0","75.5","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[875,876,878],"g",{"transform":877},"translate(368.0,29.0) scale(1.000)",[879,880],"path",{"d":881,"fill":882,"stroke":883,"style":884},"M12 2.5l8.5 4.7v9.6L12 21.5l-8.5-4.7V7.2z M3.5 7.2l8.5 4.7 8.5-4.7 M12 11.9v9.6","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[886,887,890],"text",{"x":888,"y":869,"style":889},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Item with stock on hand",[46,892],{"x1":888,"y1":893,"x2":888,"y2":894,"stroke":872,"style":873},"91.5","111.5",[46,896],{"x1":897,"y1":894,"x2":898,"y2":894,"stroke":872,"style":873},"104.0","656.0",[46,900],{"x1":897,"y1":894,"x2":897,"y2":901,"stroke":872,"style":873},"157.5",[903,904],"polygon",{"points":905,"fill":906},"104.0,161.5 99.0,152.5 109.0,152.5","#5b5cf0",[858,908],{"x":909,"y":910,"width":911,"height":912,"rx":913,"fill":863,"stroke":914,"style":915},"40.7","121.5","126.5","21.0","10","var(--line,#cdd5e6)","stroke-width:1.2px",[886,917,920],{"x":897,"y":918,"style":919},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","no use ≥ 6 months",[858,922],{"x":923,"y":924,"width":925,"height":926,"rx":870,"fill":871,"stroke":872,"style":873},"20.0","163.5","168.0","93.0",[875,928,930],{"transform":929},"translate(92.0,176.5) scale(1.000)",[879,931],{"d":932,"fill":882,"stroke":883,"style":884},"M4 7h16 M10 11v6 M14 11v6 M6 7l1 13h10l1-13 M9 7V4h6v3",[886,934,936],{"x":897,"y":935,"style":889},"223.0","Dead",[886,938,941],{"x":897,"y":939,"style":940},"242.5","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","write down, sell off",[46,943],{"x1":944,"y1":894,"x2":944,"y2":901,"stroke":872,"style":873},"288.0",[903,946],{"points":947,"fill":906},"288.0,161.5 283.0,152.5 293.0,152.5",[858,949],{"x":950,"y":910,"width":951,"height":912,"rx":913,"fill":863,"stroke":914,"style":915},"228.7","118.5",[886,953,954],{"x":944,"y":918,"style":919},"> 180 days cover",[858,956],{"x":957,"y":924,"width":925,"height":926,"rx":870,"fill":958,"stroke":959,"style":873},"204.0","#fdefd8","var(--gold,#b4740a)",[875,961,963],{"transform":962},"translate(276.0,176.5) scale(1.000)",[879,964],{"d":965,"fill":882,"stroke":966,"style":884},"M12 3a9 9 0 1 0 .01 0z M12 7v5l3.5 2","var(--gold-ink,#7a4e06)",[886,968,970],{"x":944,"y":935,"style":969},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Slow-moving",[886,972,973],{"x":944,"y":939,"style":940},"stop reordering",[46,975],{"x1":976,"y1":894,"x2":976,"y2":901,"stroke":872,"style":873},"472.0",[903,978],{"points":979,"fill":906},"472.0,161.5 467.0,152.5 477.0,152.5",[858,981],{"x":982,"y":910,"width":983,"height":912,"rx":913,"fill":863,"stroke":914,"style":915},"416.7","110.7",[886,985,986],{"x":976,"y":918,"style":919},"\u003C 14 days cover",[858,988],{"x":989,"y":924,"width":925,"height":926,"rx":870,"fill":990,"stroke":914,"style":873},"388.0","#e7ebef",[875,992,994],{"transform":993},"translate(460.0,176.5) scale(1.000)",[879,995],{"d":996,"fill":882,"stroke":997,"style":884},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--text,#172033)",[886,999,1001],{"x":976,"y":935,"style":1000},"font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Low cover",[886,1003,1004],{"x":976,"y":939,"style":940},"check lead time",[46,1006],{"x1":898,"y1":894,"x2":898,"y2":901,"stroke":872,"style":873},[903,1008],{"points":1009,"fill":906},"656.0,161.5 651.0,152.5 661.0,152.5",[858,1011],{"x":1012,"y":910,"width":1013,"height":912,"rx":913,"fill":863,"stroke":914,"style":915},"617.5","77.0",[886,1015,1016],{"x":898,"y":918,"style":919},"otherwise",[858,1018],{"x":1019,"y":924,"width":925,"height":926,"rx":870,"fill":1020,"stroke":1021,"style":873},"572.0","#d9f4f1","var(--teal,#0f9488)",[875,1023,1025],{"transform":1024},"translate(644.0,176.5) scale(1.000)",[879,1026],{"d":1027,"fill":882,"stroke":1028,"style":884},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[886,1030,1032],{"x":898,"y":935,"style":1031},"font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Healthy",[886,1034,1035],{"x":898,"y":939,"style":940},"keep as is",[29,1037,1039],{"id":1038},"step-3-cash-tied-up-by-class-and-category","Step 3 — Cash tied up, by class and category",[34,1041,1043],{"className":71,"code":1042,"language":73,"meta":39,"style":39},"by_class = r.groupby(\"Class\")[\"Stock now\"].agg([\"count\", \"sum\"]).rename(columns={\"count\": \"Items\", \"sum\": \"Value\"})\nby_class[\"Share\"] = by_class[\"Value\"] \u002F by_class[\"Value\"].sum()\nprint(by_class.round(2).to_string())\n\nTARGET_TURNS = {\"Fasteners\": 6, \"Brackets\": 4, \"Tools\": 2, \"Packaging\": 8}\ncat = r.groupby(\"Category\").agg(COGS=(\"Annual COGS\", \"sum\"), Avg_inventory=(\"Average inventory\", \"sum\"), Stock_now=(\"Stock now\", \"sum\"))\ncat[\"Turnover\"] = cat[\"COGS\"] \u002F cat[\"Avg_inventory\"]\ncat[\"Target\"] = cat.index.map(TARGET_TURNS)\ncat[\"Excess vs target\"] = (cat[\"Avg_inventory\"] - cat[\"COGS\"] \u002F cat[\"Target\"]).clip(lower=0)\nprint(cat.round(1).to_string())\n",[41,1044,1045,1101,1129,1141,1145,1195,1257,1285,1303,1347],{"__ignoreMap":39},[44,1046,1047,1050,1052,1055,1057,1060,1062,1065,1068,1070,1072,1075,1077,1079,1082,1084,1087,1089,1091,1093,1095,1098],{"class":46,"line":47},[44,1048,1049],{"class":84},"by_class ",[44,1051,107],{"class":80},[44,1053,1054],{"class":84}," r.groupby(",[44,1056,775],{"class":54},[44,1058,1059],{"class":84},")[",[44,1061,370],{"class":54},[44,1063,1064],{"class":84},"].agg([",[44,1066,1067],{"class":54},"\"count\"",[44,1069,116],{"class":84},[44,1071,233],{"class":54},[44,1073,1074],{"class":84},"]).rename(",[44,1076,208],{"class":119},[44,1078,107],{"class":80},[44,1080,1081],{"class":84},"{",[44,1083,1067],{"class":54},[44,1085,1086],{"class":84},": ",[44,1088,125],{"class":54},[44,1090,116],{"class":84},[44,1092,233],{"class":54},[44,1094,1086],{"class":84},[44,1096,1097],{"class":54},"\"Value\"",[44,1099,1100],{"class":84},"})\n",[44,1102,1103,1106,1109,1111,1113,1116,1118,1120,1122,1124,1126],{"class":46,"line":94},[44,1104,1105],{"class":84},"by_class[",[44,1107,1108],{"class":54},"\"Share\"",[44,1110,302],{"class":84},[44,1112,107],{"class":80},[44,1114,1115],{"class":84}," by_class[",[44,1117,1097],{"class":54},[44,1119,302],{"class":84},[44,1121,414],{"class":80},[44,1123,1115],{"class":84},[44,1125,1097],{"class":54},[44,1127,1128],{"class":84},"].sum()\n",[44,1130,1131,1133,1136,1139],{"class":46,"line":101},[44,1132,439],{"class":315},[44,1134,1135],{"class":84},"(by_class.round(",[44,1137,1138],{"class":315},"2",[44,1140,469],{"class":84},[44,1142,1143],{"class":46,"line":137},[44,1144,98],{"emptyLinePlaceholder":97},[44,1146,1147,1150,1153,1156,1159,1161,1164,1166,1169,1171,1173,1175,1178,1180,1182,1184,1187,1189,1192],{"class":46,"line":160},[44,1148,1149],{"class":315},"TARGET_TURNS",[44,1151,1152],{"class":80}," =",[44,1154,1155],{"class":84}," {",[44,1157,1158],{"class":54},"\"Fasteners\"",[44,1160,1086],{"class":84},[44,1162,1163],{"class":315},"6",[44,1165,116],{"class":84},[44,1167,1168],{"class":54},"\"Brackets\"",[44,1170,1086],{"class":84},[44,1172,860],{"class":315},[44,1174,116],{"class":84},[44,1176,1177],{"class":54},"\"Tools\"",[44,1179,1086],{"class":84},[44,1181,1138],{"class":315},[44,1183,116],{"class":84},[44,1185,1186],{"class":54},"\"Packaging\"",[44,1188,1086],{"class":84},[44,1190,1191],{"class":315},"8",[44,1193,1194],{"class":84},"}\n",[44,1196,1197,1200,1202,1204,1206,1209,1212,1214,1217,1219,1221,1223,1226,1229,1231,1233,1235,1237,1239,1241,1244,1246,1248,1250,1252,1254],{"class":46,"line":183},[44,1198,1199],{"class":84},"cat ",[44,1201,107],{"class":80},[44,1203,1054],{"class":84},[44,1205,445],{"class":54},[44,1207,1208],{"class":84},").agg(",[44,1210,1211],{"class":119},"COGS",[44,1213,107],{"class":80},[44,1215,1216],{"class":84},"(",[44,1218,299],{"class":54},[44,1220,116],{"class":84},[44,1222,233],{"class":54},[44,1224,1225],{"class":84},"), ",[44,1227,1228],{"class":119},"Avg_inventory",[44,1230,107],{"class":80},[44,1232,1216],{"class":84},[44,1234,339],{"class":54},[44,1236,116],{"class":84},[44,1238,233],{"class":54},[44,1240,1225],{"class":84},[44,1242,1243],{"class":119},"Stock_now",[44,1245,107],{"class":80},[44,1247,1216],{"class":84},[44,1249,370],{"class":54},[44,1251,116],{"class":84},[44,1253,233],{"class":54},[44,1255,1256],{"class":84},"))\n",[44,1258,1259,1262,1264,1266,1268,1271,1274,1276,1278,1280,1283],{"class":46,"line":188},[44,1260,1261],{"class":84},"cat[",[44,1263,400],{"class":54},[44,1265,302],{"class":84},[44,1267,107],{"class":80},[44,1269,1270],{"class":84}," cat[",[44,1272,1273],{"class":54},"\"COGS\"",[44,1275,302],{"class":84},[44,1277,414],{"class":80},[44,1279,1270],{"class":84},[44,1281,1282],{"class":54},"\"Avg_inventory\"",[44,1284,331],{"class":84},[44,1286,1287,1289,1292,1294,1296,1299,1301],{"class":46,"line":238},[44,1288,1261],{"class":84},[44,1290,1291],{"class":54},"\"Target\"",[44,1293,302],{"class":84},[44,1295,107],{"class":80},[44,1297,1298],{"class":84}," cat.index.map(",[44,1300,1149],{"class":315},[44,1302,134],{"class":84},[44,1304,1305,1307,1310,1312,1314,1317,1319,1321,1323,1325,1327,1329,1331,1333,1335,1338,1341,1343,1345],{"class":46,"line":282},[44,1306,1261],{"class":84},[44,1308,1309],{"class":54},"\"Excess vs target\"",[44,1311,302],{"class":84},[44,1313,107],{"class":80},[44,1315,1316],{"class":84}," (cat[",[44,1318,1282],{"class":54},[44,1320,302],{"class":84},[44,1322,380],{"class":80},[44,1324,1270],{"class":84},[44,1326,1273],{"class":54},[44,1328,302],{"class":84},[44,1330,414],{"class":80},[44,1332,1270],{"class":84},[44,1334,1291],{"class":54},[44,1336,1337],{"class":84},"]).clip(",[44,1339,1340],{"class":119},"lower",[44,1342,107],{"class":80},[44,1344,816],{"class":315},[44,1346,134],{"class":84},[44,1348,1349,1351,1354,1356],{"class":46,"line":293},[44,1350,439],{"class":315},[44,1352,1353],{"class":84},"(cat.round(",[44,1355,316],{"class":315},[44,1357,469],{"class":84},[10,1359,1360],{},"Valuing stock by class turns ratios into money: how much of today's stock value is dead or slow-moving, and therefore a candidate for a write-down, a clearance sale or simply not reordering. The category view compares actual turnover with a target per category — packaging should turn fast, tools slowly — and converts the gap into money: the stock a category would hold if it met its target turnover on the same cost of sales, subtracted from what it actually holds on average, is the cash that better stock control would release. That number is what makes an inventory review worth a manager's time. Here dead and slow-moving stock make up almost 30% of today's stock value, about 8,700, and packaging is furthest from its target, turning 2.7 times against a target of 8 — roughly 5,800 more stock than the target implies, much of it in the unused P-710. Tools already beat their target and need no action.",[839,1362,848,1368,848,1371,848,1374,848,1378,848,1384,848,1390,848,1395,848,1400,848,1403,848,1407,848,1410,848,1415,848,1418,848,1422,848,1425,848,1429,848,1431,848,1435,848,1438],{"viewBox":1363,"role":842,"ariaLabelledBy":1364,"xmlns":846,"style":1367},"4 0 743 180",[1365,1366],"inv-excess-t","inv-excess-d","width:100%;max-width:743px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[850,1369,1370],{"id":1365},"Average stock above what the target turnover implies",[854,1372,1373],{"id":1366},"Packaging holds about 5,785 more average stock than its target turnover of 8 implies, brackets about 4,225 more against a target of 4, and fasteners about 2,560 more against a target of 6. Tools turn faster than their target of 2 and show no excess.",[858,1375],{"x":860,"y":816,"width":1376,"height":1377,"fill":863},"743","180",[858,1379],{"x":923,"y":867,"width":1380,"height":1381,"rx":1382,"fill":990,"stroke":914,"style":1383},"158.7","28.0","7","stroke-width:1px",[886,1385,1389],{"x":1386,"y":1387,"style":1388},"99.3","34.5","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Packaging (2.7 vs 8)",[858,1391],{"x":1392,"y":867,"width":1393,"height":1381,"rx":1382,"fill":871,"stroke":872,"style":1394},"188.7","498.9","stroke-width:1.5px",[886,1396,1399],{"x":1397,"y":1387,"style":1398},"695.6","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:start","5,785",[858,1401],{"x":923,"y":1402,"width":1380,"height":1381,"rx":1382,"fill":990,"stroke":914,"style":1383},"56.0",[886,1404,1406],{"x":1386,"y":1405,"style":1388},"74.5","Brackets (1.3 vs 4)",[858,1408],{"x":1392,"y":1402,"width":1409,"height":1381,"rx":1382,"fill":958,"stroke":959,"style":1394},"364.3",[886,1411,1414],{"x":1412,"y":1405,"style":1413},"561.0","font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:start","4,225",[858,1416],{"x":923,"y":1417,"width":1380,"height":1381,"rx":1382,"fill":990,"stroke":914,"style":1383},"96.0",[886,1419,1421],{"x":1386,"y":1420,"style":1388},"114.5","Fasteners (4.4 vs 6)",[858,1423],{"x":1392,"y":1417,"width":1424,"height":1381,"rx":1382,"fill":958,"stroke":959,"style":1394},"220.9",[886,1426,1428],{"x":1427,"y":1420,"style":1413},"417.6","2,561",[858,1430],{"x":923,"y":918,"width":1380,"height":1381,"rx":1382,"fill":990,"stroke":914,"style":1383},[886,1432,1434],{"x":1386,"y":1433,"style":1388},"154.5","Tools (2.3 vs 2)",[858,1436],{"x":1392,"y":918,"width":1437,"height":1381,"rx":1382,"fill":1020,"stroke":1021,"style":1394},"6.0",[886,1439,882],{"x":1440,"y":1433,"style":1441},"202.7","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:start",[10,1443,1444,1445,27],{},"Reordering logic for the healthy and low-cover items is covered in ",[23,1446,1448],{"href":1447},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report\u002F","Build an Inventory Reorder Report",[839,1450,848,1455,848,1458,848,1461,848,1464,848,1468,848,1472,848,1478,848,1481,848,1485,848,1488,848,1492,848,1495,848,1501,848,1503,848,1507,848,1511,848,1513,848,1516,848,1518,848,1521,848,1524,848,1527,848,1529,848,1533,848,1535,848,1538,848,1540,848,1543,848,1546,848,1550,848,1552,848,1556,848,1558,848,1561,848,1563],{"viewBox":1451,"role":842,"ariaLabelledBy":1452,"xmlns":846,"style":847},"4 0 752 174",[1453,1454],"inv-measures-t","inv-measures-d",[850,1456,1457],{"id":1453},"Turnover and days on hand compared",[854,1459,1460],{"id":1454},"Inventory turnover divides a year's cost of goods sold by average inventory value and looks back over the year. Days on hand divides current stock by recent daily usage and looks forward. Turnover suits targets and comparisons between categories; days on hand suits decisions about individual items.",[858,1462],{"x":860,"y":816,"width":861,"height":1463,"fill":863},"174",[858,1465],{"x":923,"y":867,"width":1466,"height":1467,"rx":816,"fill":863,"stroke":914,"style":1383},"141.0","40.0",[858,1469],{"x":1470,"y":867,"width":1471,"height":1467,"rx":816,"fill":871,"stroke":872,"style":915},"161.0","193.0",[886,1473,1477],{"x":1474,"y":1475,"style":1476},"257.5","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","formula",[858,1479],{"x":1480,"y":867,"width":1471,"height":1467,"rx":816,"fill":871,"stroke":872,"style":915},"354.0",[886,1482,1484],{"x":1483,"y":1475,"style":1476},"450.5","looks",[858,1486],{"x":1487,"y":867,"width":1471,"height":1467,"rx":816,"fill":871,"stroke":872,"style":915},"547.0",[886,1489,1491],{"x":1490,"y":1475,"style":1476},"643.5","best for",[858,1493],{"x":923,"y":1402,"width":1466,"height":1494,"rx":816,"fill":990,"stroke":914,"style":1383},"34.0",[886,1496,1500],{"x":1497,"y":1498,"style":1499},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Turnover",[858,1502],{"x":1470,"y":1402,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1504,1506],{"x":1474,"y":1505,"style":940},"71.0","annual COGS ÷ average",[886,1508,1510],{"x":1474,"y":1509,"style":940},"84.0","inventory",[858,1512],{"x":1480,"y":1402,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1514,1515],{"x":1483,"y":1013,"style":940},"back over a year",[858,1517],{"x":1487,"y":1402,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1519,1520],{"x":1490,"y":1013,"style":940},"targets by category",[858,1522],{"x":923,"y":1523,"width":1466,"height":1494,"rx":816,"fill":990,"stroke":914,"style":1383},"90.0",[886,1525,20],{"x":1497,"y":1526,"style":1499},"112.0",[858,1528],{"x":1470,"y":1523,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1530,1532],{"x":1474,"y":1531,"style":940},"111.0","stock now ÷ recent daily use",[858,1534],{"x":1480,"y":1523,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1536,1537],{"x":1483,"y":1531,"style":940},"forward from today",[858,1539],{"x":1487,"y":1523,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1541,1542],{"x":1490,"y":1531,"style":940},"item decisions",[858,1544],{"x":923,"y":1545,"width":1466,"height":1494,"rx":816,"fill":990,"stroke":914,"style":1383},"124.0",[886,1547,1549],{"x":1497,"y":1548,"style":1499},"146.0","Months since use",[858,1551],{"x":1470,"y":1545,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1553,1555],{"x":1474,"y":1554,"style":940},"145.0","last month with usage",[858,1557],{"x":1480,"y":1545,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1559,1560],{"x":1483,"y":1554,"style":940},"back",[858,1562],{"x":1487,"y":1545,"width":1471,"height":1494,"rx":816,"fill":863,"stroke":914,"style":1383},[886,1564,1565],{"x":1490,"y":1554,"style":940},"dead stock",[29,1567,1569],{"id":1568},"step-4-write-the-report","Step 4 — Write the report",[34,1571,1573],{"className":71,"code":1572,"language":73,"meta":39,"style":39},"from openpyxl import load_workbook\nfrom openpyxl.styles import PatternFill\n\npath = \"inventory_turnover_2026-09.xlsx\"\ncols = [\"Category\", \"Unit cost\", \"Annual COGS\", \"Average inventory\", \"Stock now\", \"Turnover\", \"Days on hand\", \"Months since last use\", \"Class\"]\nwith pd.ExcelWriter(path, engine=\"openpyxl\") as xw:\n    r[cols].sort_values([\"Class\", \"Stock now\"], ascending=[True, False]).to_excel(xw, sheet_name=\"Items\")\n    by_class.to_excel(xw, sheet_name=\"By class\")\n    cat.to_excel(xw, sheet_name=\"By category\")\nwb = load_workbook(path)\nws = wb[\"Items\"]\nfills = {\"Dead\": \"FFC7CE\", \"Slow-moving\": \"FFEB9C\", \"Low cover\": \"DDEBF7\"}\nclass_col = [c.value for c in ws[1]].index(\"Class\") + 1\nfor row in ws.iter_rows(min_row=2):\n    colour = fills.get(row[class_col - 1].value)\n    if colour:\n        for cell in row:\n            cell.fill = PatternFill(\"solid\", fgColor=colour)\n    for cell in row[2:8]:\n        cell.number_format = \"#,##0.0\" if cell.column in (7, 8) else \"#,##0\"\nwb.save(path)\nprint(wb.sheetnames)\n",[41,1574,1575,1588,1600,1604,1614,1660,1683,1724,1738,1752,1762,1776,1815,1852,1874,1892,1899,1912,1936,1959,1993,1999],{"__ignoreMap":39},[44,1576,1577,1580,1583,1585],{"class":46,"line":47},[44,1578,1579],{"class":80},"from",[44,1581,1582],{"class":84}," openpyxl ",[44,1584,81],{"class":80},[44,1586,1587],{"class":84}," load_workbook\n",[44,1589,1590,1592,1595,1597],{"class":46,"line":94},[44,1591,1579],{"class":80},[44,1593,1594],{"class":84}," openpyxl.styles ",[44,1596,81],{"class":80},[44,1598,1599],{"class":84}," PatternFill\n",[44,1601,1602],{"class":46,"line":101},[44,1603,98],{"emptyLinePlaceholder":97},[44,1605,1606,1609,1611],{"class":46,"line":137},[44,1607,1608],{"class":84},"path ",[44,1610,107],{"class":80},[44,1612,1613],{"class":54}," \"inventory_turnover_2026-09.xlsx\"\n",[44,1615,1616,1619,1621,1624,1626,1628,1630,1632,1634,1636,1638,1640,1642,1644,1646,1648,1650,1652,1654,1656,1658],{"class":46,"line":160},[44,1617,1618],{"class":84},"cols ",[44,1620,107],{"class":80},[44,1622,1623],{"class":84}," [",[44,1625,445],{"class":54},[44,1627,116],{"class":84},[44,1629,328],{"class":54},[44,1631,116],{"class":84},[44,1633,299],{"class":54},[44,1635,116],{"class":84},[44,1637,339],{"class":54},[44,1639,116],{"class":84},[44,1641,370],{"class":54},[44,1643,116],{"class":84},[44,1645,400],{"class":54},[44,1647,116],{"class":84},[44,1649,544],{"class":54},[44,1651,116],{"class":84},[44,1653,634],{"class":54},[44,1655,116],{"class":84},[44,1657,775],{"class":54},[44,1659,331],{"class":84},[44,1661,1662,1665,1668,1671,1673,1676,1678,1680],{"class":46,"line":183},[44,1663,1664],{"class":80},"with",[44,1666,1667],{"class":84}," pd.ExcelWriter(path, ",[44,1669,1670],{"class":119},"engine",[44,1672,107],{"class":80},[44,1674,1675],{"class":54},"\"openpyxl\"",[44,1677,319],{"class":84},[44,1679,88],{"class":80},[44,1681,1682],{"class":84}," xw:\n",[44,1684,1685,1688,1690,1692,1694,1697,1700,1702,1705,1708,1710,1713,1716,1718,1720,1722],{"class":46,"line":188},[44,1686,1687],{"class":84},"    r[cols].sort_values([",[44,1689,775],{"class":54},[44,1691,116],{"class":84},[44,1693,370],{"class":54},[44,1695,1696],{"class":84},"], ",[44,1698,1699],{"class":119},"ascending",[44,1701,107],{"class":80},[44,1703,1704],{"class":84},"[",[44,1706,1707],{"class":315},"True",[44,1709,116],{"class":84},[44,1711,1712],{"class":315},"False",[44,1714,1715],{"class":84},"]).to_excel(xw, ",[44,1717,120],{"class":119},[44,1719,107],{"class":80},[44,1721,125],{"class":54},[44,1723,134],{"class":84},[44,1725,1726,1729,1731,1733,1736],{"class":46,"line":238},[44,1727,1728],{"class":84},"    by_class.to_excel(xw, ",[44,1730,120],{"class":119},[44,1732,107],{"class":80},[44,1734,1735],{"class":54},"\"By class\"",[44,1737,134],{"class":84},[44,1739,1740,1743,1745,1747,1750],{"class":46,"line":282},[44,1741,1742],{"class":84},"    cat.to_excel(xw, ",[44,1744,120],{"class":119},[44,1746,107],{"class":80},[44,1748,1749],{"class":54},"\"By category\"",[44,1751,134],{"class":84},[44,1753,1754,1757,1759],{"class":46,"line":293},[44,1755,1756],{"class":84},"wb ",[44,1758,107],{"class":80},[44,1760,1761],{"class":84}," load_workbook(path)\n",[44,1763,1764,1767,1769,1772,1774],{"class":46,"line":334},[44,1765,1766],{"class":84},"ws ",[44,1768,107],{"class":80},[44,1770,1771],{"class":84}," wb[",[44,1773,125],{"class":54},[44,1775,331],{"class":84},[44,1777,1778,1781,1783,1785,1788,1790,1793,1795,1798,1800,1803,1805,1808,1810,1813],{"class":46,"line":365},[44,1779,1780],{"class":84},"fills ",[44,1782,107],{"class":80},[44,1784,1155],{"class":84},[44,1786,1787],{"class":54},"\"Dead\"",[44,1789,1086],{"class":84},[44,1791,1792],{"class":54},"\"FFC7CE\"",[44,1794,116],{"class":84},[44,1796,1797],{"class":54},"\"Slow-moving\"",[44,1799,1086],{"class":84},[44,1801,1802],{"class":54},"\"FFEB9C\"",[44,1804,116],{"class":84},[44,1806,1807],{"class":54},"\"Low cover\"",[44,1809,1086],{"class":84},[44,1811,1812],{"class":54},"\"DDEBF7\"",[44,1814,1194],{"class":84},[44,1816,1817,1820,1822,1825,1828,1831,1834,1837,1839,1842,1844,1846,1849],{"class":46,"line":395},[44,1818,1819],{"class":84},"class_col ",[44,1821,107],{"class":80},[44,1823,1824],{"class":84}," [c.value ",[44,1826,1827],{"class":80},"for",[44,1829,1830],{"class":84}," c ",[44,1832,1833],{"class":80},"in",[44,1835,1836],{"class":84}," ws[",[44,1838,316],{"class":315},[44,1840,1841],{"class":84},"]].index(",[44,1843,775],{"class":54},[44,1845,319],{"class":84},[44,1847,1848],{"class":80},"+",[44,1850,1851],{"class":315}," 1\n",[44,1853,1854,1856,1859,1861,1864,1867,1869,1871],{"class":46,"line":436},[44,1855,1827],{"class":80},[44,1857,1858],{"class":84}," row ",[44,1860,1833],{"class":80},[44,1862,1863],{"class":84}," ws.iter_rows(",[44,1865,1866],{"class":119},"min_row",[44,1868,107],{"class":80},[44,1870,1138],{"class":315},[44,1872,1873],{"class":84},"):\n",[44,1875,1876,1879,1881,1884,1886,1889],{"class":46,"line":765},[44,1877,1878],{"class":84},"    colour ",[44,1880,107],{"class":80},[44,1882,1883],{"class":84}," fills.get(row[class_col ",[44,1885,380],{"class":80},[44,1887,1888],{"class":315}," 1",[44,1890,1891],{"class":84},"].value)\n",[44,1893,1894,1896],{"class":46,"line":770},[44,1895,662],{"class":80},[44,1897,1898],{"class":84}," colour:\n",[44,1900,1901,1904,1907,1909],{"class":46,"line":793},[44,1902,1903],{"class":80},"        for",[44,1905,1906],{"class":84}," cell ",[44,1908,1833],{"class":80},[44,1910,1911],{"class":84}," row:\n",[44,1913,1915,1918,1920,1923,1926,1928,1931,1933],{"class":46,"line":1914},18,[44,1916,1917],{"class":84},"            cell.fill ",[44,1919,107],{"class":80},[44,1921,1922],{"class":84}," PatternFill(",[44,1924,1925],{"class":54},"\"solid\"",[44,1927,116],{"class":84},[44,1929,1930],{"class":119},"fgColor",[44,1932,107],{"class":80},[44,1934,1935],{"class":84},"colour)\n",[44,1937,1939,1942,1944,1946,1949,1951,1954,1956],{"class":46,"line":1938},19,[44,1940,1941],{"class":80},"    for",[44,1943,1906],{"class":84},[44,1945,1833],{"class":80},[44,1947,1948],{"class":84}," row[",[44,1950,1138],{"class":315},[44,1952,1953],{"class":84},":",[44,1955,1191],{"class":315},[44,1957,1958],{"class":84},"]:\n",[44,1960,1962,1965,1967,1970,1972,1975,1977,1980,1982,1984,1986,1988,1990],{"class":46,"line":1961},20,[44,1963,1964],{"class":84},"        cell.number_format ",[44,1966,107],{"class":80},[44,1968,1969],{"class":54}," \"#,##0.0\"",[44,1971,745],{"class":80},[44,1973,1974],{"class":84}," cell.column ",[44,1976,1833],{"class":80},[44,1978,1979],{"class":84}," (",[44,1981,1382],{"class":315},[44,1983,116],{"class":84},[44,1985,1191],{"class":315},[44,1987,319],{"class":84},[44,1989,613],{"class":80},[44,1991,1992],{"class":54}," \"#,##0\"\n",[44,1994,1996],{"class":46,"line":1995},21,[44,1997,1998],{"class":84},"wb.save(path)\n",[44,2000,2002,2004],{"class":46,"line":2001},22,[44,2003,439],{"class":315},[44,2005,2006],{"class":84},"(wb.sheetnames)\n",[10,2008,2009],{},"The item sheet is sorted by class and then by value, so the dead and slow-moving stock with the most money in it is at the top of its section, shaded by class. The class and category sheets carry the money summaries. Run monthly, the report also shows movement between classes — an item drifting from healthy to slow-moving is cheaper to deal with than one that has already become dead.",[29,2011,2013],{"id":2012},"getting-the-inputs-right","Getting the inputs right",[10,2015,2016],{},"Two details make the difference between a turnover report people trust and one they argue with. First, value usage and stock on the same basis: if stock is valued at standard cost, cost of goods sold must be too, or the ratio mixes two price levels. Second, decide what counts as usage. Sales to customers obviously do; transfers between warehouses do not, because the item has not left the business; scrap and write-offs arguably do not either, because they would flatter the turnover of items that are being thrown away. Record the definition on the report. When stock levels are only available at year end, an average of opening and closing stock is a poor but common substitute; twelve monthly snapshots, as here, are much better and usually easy to obtain from the stock system's month-end valuation report.",[29,2018,2020],{"id":2019},"common-pitfalls","Common pitfalls",[2022,2023,2024,2032,2038,2044,2050],"ul",{},[2025,2026,2027,2031],"li",{},[2028,2029,2030],"strong",{},"Year-end stock instead of an average."," Use monthly snapshots for turnover.",[2025,2033,2034,2037],{},[2028,2035,2036],{},"Annual usage for days on hand."," Use recent usage so cover reflects current demand.",[2025,2039,2040,2043],{},[2028,2041,2042],{},"Inconsistent valuation."," Value COGS and stock on the same cost basis.",[2025,2045,2046,2049],{},[2028,2047,2048],{},"Counting transfers as usage."," They inflate turnover without selling anything.",[2025,2051,2052,2055],{},[2028,2053,2054],{},"Ratios without money."," Value the slow and dead stock and the excess against target.",[29,2057,2059],{"id":2058},"conclusion","Conclusion",[10,2061,2062],{},"Compute inventory turnover per item as a year's cost of goods sold over the average of monthly stock values, and days on hand as current stock over recent daily usage. Classify items as dead, slow-moving, low cover or healthy with simple rules, value the stock in each class, compare category turnover with targets to find the cash tied up, and write an item sheet sorted by class and value with category and class summaries.",[29,2064,2066],{"id":2065},"frequently-asked-questions","Frequently asked questions",[10,2068,2069,2072],{},[2028,2070,2071],{},"How do I calculate inventory turnover in pandas?","\nDivide a year's cost of goods sold by the average inventory value, using the mean of the monthly stock values rather than a single year-end figure.",[10,2074,2075,2078],{},[2028,2076,2077],{},"How do I calculate days on hand?","\nDivide current stock quantity by recent average daily usage, for example the last three months' usage divided by about 91 days.",[10,2080,2081,2084],{},[2028,2082,2083],{},"What counts as dead stock?","\nA common rule is stock with no usage for six months or more; choose a period that fits the business and apply it consistently.",[10,2086,2087,2090],{},[2028,2088,2089],{},"How do I show how much cash is tied up in excess stock?","\nCompare average inventory with the inventory a target turnover would imply, cost of goods sold divided by the target, and report the positive difference.",[29,2092,2094],{"id":2093},"related","Related",[2022,2096,2097,2103,2108,2115],{},[2025,2098,2099,2100,2102],{},"Up one level: ",[23,2101,26],{"href":25}," — finance reports from Python.",[2025,2104,2105,2107],{},[23,2106,1448],{"href":1447}," — what to order next.",[2025,2109,2110,2114],{},[23,2111,2113],{"href":2112},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fcalculate-weighted-average-cost-inventory-valuation\u002F","Calculate Weighted Average Cost Inventory Valuation"," — valuing the stock.",[2025,2116,2117,2121],{},[23,2118,2120],{"href":2119},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-an-abc-xyz-inventory-classification\u002F","Run an ABC-XYZ Inventory Classification"," — classifying by value and variability.",[2123,2124,2125],"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-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":39,"searchDepth":94,"depth":94,"links":2127},[2128,2129,2130,2131,2132,2133,2134,2135,2136,2137],{"id":31,"depth":94,"text":32},{"id":67,"depth":94,"text":68},{"id":479,"depth":94,"text":480},{"id":1038,"depth":94,"text":1039},{"id":1568,"depth":94,"text":1569},{"id":2012,"depth":94,"text":2013},{"id":2019,"depth":94,"text":2020},{"id":2058,"depth":94,"text":2059},{"id":2065,"depth":94,"text":2066},{"id":2093,"depth":94,"text":2094},"2026-10-02","Measure how hard stock is working with pandas: annual cost of goods sold against average inventory value for inventory turnover, days on hand from current stock and recent usage, slow-moving and dead stock classification, the cash tied up in each class, a category summary against targets, and an Excel report that shows where to act.","md",[2142,2144,2146,2148],{"q":2071,"a":2143},"Divide a year's cost of goods sold by the average inventory value, using the mean of the monthly stock values rather than a single year-end figure.",{"q":2077,"a":2145},"Divide current stock quantity by recent average daily usage, for example the last three months' usage divided by about 91 days.",{"q":2083,"a":2147},"A common rule is stock with no usage for six months or more; choose a period that fits the business and apply it consistently.",{"q":2089,"a":2149},"Compare average inventory with the inventory a target turnover would imply, cost of goods sold divided by the target, and report the positive difference.","inventory turnover days on hand report python",null,{"breadcrumb":2153},[2154,2156,2159],{"name":2155,"item":414},"Home",{"name":2157,"item":2158},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":2160,"item":25},"Building Finance and Operations Reports in Excel with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report",{"title":2163,"description":2164},"Build an Inventory Turnover and Days on Hand Report in Python","Inventory turnover and days on hand with pandas: COGS over average inventory from monthly snapshots, forward-looking days of cover, slow-moving and dead stock rules, cash tied up by class and category, targets and an Excel report.","build-an-inventory-turnover-and-days-on-hand-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-turnover-and-days-on-hand-report\u002Findex",[2168,2169],"report-layouts","pivots-and-summaries","how-to","BZekrIgWZetUxqcNHMubu_Yva9bsYOLmmbbMeYuyPq0",[2173,2177],{"title":2174,"path":2175,"stem":2176,"children":-1},"Build an Inventory Reorder Report with Python and Excel","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report\u002Findex",{"title":2178,"path":2179,"stem":2180,"children":-1},"Build an On-Time Delivery (OTIF) Report with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-on-time-delivery-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-on-time-delivery-report\u002Findex",[2182,2184],{"path":2183,"title":2160},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports",{"path":2185,"title":2157},"\u002Fautomating-reporting-workflows",[],[2188,2192,2195,2198,2201,2204],{"path":2189,"title":2190,"kind":2191},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards","Building Multi-Sheet Excel Dashboards","guide",{"path":2193,"title":2194,"kind":2170},"\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":2196,"title":2197,"kind":2170},"\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":2199,"title":2200,"kind":2170},"\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":2202,"title":2203,"kind":2170},"\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":2205,"title":2206,"kind":2170},"\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",1790972127774]