[{"data":1,"prerenderedAt":2170},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report":2135,"ancestors:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report":2144,"children:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report":2149,"related:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report":2150},{"id":4,"title":5,"body":6,"dateModified":2100,"datePublished":2100,"description":2101,"extension":2102,"faq":2103,"keyword":2112,"kind":2113,"meta":2114,"navigation":240,"path":2123,"seo":2124,"slug":2127,"stem":2128,"topics":2129,"type":2133,"__hash__":2134},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report\u002Findex.md","Build an Inventory Reorder Report with Python and Excel",{"type":7,"value":8,"toc":2087},"minimark",[9,19,139,144,178,192,196,199,464,475,479,482,704,707,711,719,877,880,999,1003,1006,1297,1305,1309,1312,1687,1945,1948,1952,1992,1996,2015,2019,2022,2026,2032,2038,2044,2050,2054,2083],[10,11,12,13,18],"p",{},"Every week someone in operations looks at the stock list and decides what to order. Done by eye, it produces two failures: items that run out because nobody noticed they were low, and items ordered twice because nobody remembered the order already placed. A reorder report replaces the eyeballing with a rule: for each item, work out how much will be used before a new order could arrive, add a safety margin, and order when the stock on hand plus the stock on order falls below that. This page, part of ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002F","Building Finance and Operations Reports",", builds that report from a stock export, a sales history and a list of open purchase orders.",[20,21,29,30,29,34,29,38,29,46,29,56,29,67,29,74,29,79,29,85,29,90,29,95,29,102,29,107,29,111,29,115,29,118,29,123,29,130,29,135],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"84 0 592 383","img",[25,26],"inv-rop-t","inv-rop-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  ",[31,32,33],"title",{"id":25},"What the reorder point is made of",[35,36,37],"desc",{"id":26},"The reorder point is built from two layers: the demand expected during the lead time, which is average daily usage multiplied by lead time in days, and safety stock on top to cover demand that runs above average. When on hand plus on order falls below the reorder point, the item is ordered up to a target level.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"84","0","592","383","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"100.0","16.0","560.0","93.0","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[57,58,60],"g",{"transform":59},"translate(368.0,29.0) scale(1.000)",[61,62],"path",{"d":63,"fill":64,"stroke":65,"style":66},"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(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[68,69,73],"text",{"x":70,"y":71,"style":72},"380.0","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Order up to target",[68,75,78],{"x":70,"y":76,"style":77},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","reorder point + a review period of usage",[80,81],"line",{"x1":70,"y1":82,"x2":70,"y2":83,"stroke":84,"style":55},"114.0","136.0","var(--brand,#5b5cf0)",[86,87],"polygon",{"points":88,"fill":89},"380.0,140.0 375.0,131.0 385.0,131.0","#5b5cf0",[39,91],{"x":48,"y":92,"width":50,"height":51,"rx":52,"fill":93,"stroke":94,"style":55},"145.0","#fdefd8","var(--gold,#b4740a)",[57,96,98],{"transform":97},"translate(368.0,158.0) scale(1.000)",[61,99],{"d":100,"fill":64,"stroke":101,"style":66},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold-ink,#7a4e06)",[68,103,106],{"x":70,"y":104,"style":105},"204.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Safety stock",[68,108,110],{"x":70,"y":109,"style":77},"224.0","z × σ × √lead time",[80,112],{"x1":70,"y1":113,"x2":70,"y2":114,"stroke":84,"style":55},"243.0","265.0",[86,116],{"points":117,"fill":89},"380.0,269.0 375.0,260.0 385.0,260.0",[39,119],{"x":48,"y":120,"width":50,"height":51,"rx":52,"fill":121,"stroke":122,"style":55},"274.0","#d9f4f1","var(--teal,#0f9488)",[57,124,126],{"transform":125},"translate(368.0,287.0) scale(1.000)",[61,127],{"d":128,"fill":64,"stroke":129,"style":66},"M12 3a9 9 0 1 0 .01 0z M12 7v5l3.5 2","var(--teal-ink,#0b6157)",[68,131,134],{"x":70,"y":132,"style":133},"333.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Lead-time demand",[68,136,138],{"x":70,"y":137,"style":77},"353.0","daily usage × lead time",[140,141,143],"h2",{"id":142},"prerequisites","Prerequisites",[145,146,151],"pre",{"className":147,"code":148,"language":149,"meta":150,"style":150},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas numpy openpyxl xlsxwriter\n","bash","",[152,153,154],"code",{"__ignoreMap":150},[155,156,158,162,166,169,172,175],"span",{"class":80,"line":157},1,[155,159,161],{"class":160},"sMTad","pip",[155,163,165],{"class":164},"srMev"," install",[155,167,168],{"class":164}," pandas",[155,170,171],{"class":164}," numpy",[155,173,174],{"class":164}," openpyxl",[155,176,177],{"class":164}," xlsxwriter\n",[10,179,180,183,184,187,188,191],{},[152,181,182],{},"stock.xlsx"," lists six items with their supplier, stock on hand, lead time, pack size and minimum order quantity. ",[152,185,186],{},"sales_history.xlsx"," has daily sales for the last 90 days. ",[152,189,190],{},"open_pos.xlsx"," lists purchase orders placed but not yet received.",[140,193,195],{"id":194},"step-1-work-out-daily-usage-and-its-variability","Step 1 — Work out daily usage and its variability",[10,197,198],{},"Average daily usage comes from recent history. Divide by the number of days in the window, not the number of days with sales — days without a sale are days of zero demand:",[145,200,204],{"className":201,"code":202,"language":203,"meta":150,"style":150},"language-python shiki shiki-themes github-light github-dark-high-contrast","import numpy as np\nimport pandas as pd\n\nstock = pd.read_excel(\"stock.xlsx\")\nhistory = pd.read_excel(\"sales_history.xlsx\", parse_dates=[\"Date\"])\nwindow = pd.date_range(\"2026-06-28\", \"2026-09-25\")\n\ndaily = (history.pivot_table(index=\"Date\", columns=\"SKU\", values=\"Qty\", aggfunc=\"sum\")\n         .reindex(index=window, columns=stock[\"SKU\"], fill_value=0).fillna(0))\nusage = pd.DataFrame({\"Daily Usage\": daily.mean(), \"Usage SD\": daily.std()})\nprint(len(window), \"days\")\nprint(usage.round(2))\n","python",[152,205,206,222,235,242,260,291,312,317,367,408,431,451],{"__ignoreMap":150},[155,207,208,212,216,219],{"class":80,"line":157},[155,209,211],{"class":210},"s-kum","import",[155,213,215],{"class":214},"skGVy"," numpy ",[155,217,218],{"class":210},"as",[155,220,221],{"class":214}," np\n",[155,223,225,227,230,232],{"class":80,"line":224},2,[155,226,211],{"class":210},[155,228,229],{"class":214}," pandas ",[155,231,218],{"class":210},[155,233,234],{"class":214}," pd\n",[155,236,238],{"class":80,"line":237},3,[155,239,241],{"emptyLinePlaceholder":240},true,"\n",[155,243,245,248,251,254,257],{"class":80,"line":244},4,[155,246,247],{"class":214},"stock ",[155,249,250],{"class":210},"=",[155,252,253],{"class":214}," pd.read_excel(",[155,255,256],{"class":164},"\"stock.xlsx\"",[155,258,259],{"class":214},")\n",[155,261,263,266,268,270,273,276,280,282,285,288],{"class":80,"line":262},5,[155,264,265],{"class":214},"history ",[155,267,250],{"class":210},[155,269,253],{"class":214},[155,271,272],{"class":164},"\"sales_history.xlsx\"",[155,274,275],{"class":214},", ",[155,277,279],{"class":278},"sa561","parse_dates",[155,281,250],{"class":210},[155,283,284],{"class":214},"[",[155,286,287],{"class":164},"\"Date\"",[155,289,290],{"class":214},"])\n",[155,292,294,297,299,302,305,307,310],{"class":80,"line":293},6,[155,295,296],{"class":214},"window ",[155,298,250],{"class":210},[155,300,301],{"class":214}," pd.date_range(",[155,303,304],{"class":164},"\"2026-06-28\"",[155,306,275],{"class":214},[155,308,309],{"class":164},"\"2026-09-25\"",[155,311,259],{"class":214},[155,313,315],{"class":80,"line":314},7,[155,316,241],{"emptyLinePlaceholder":240},[155,318,320,323,325,328,331,333,335,337,340,342,345,347,350,352,355,357,360,362,365],{"class":80,"line":319},8,[155,321,322],{"class":214},"daily ",[155,324,250],{"class":210},[155,326,327],{"class":214}," (history.pivot_table(",[155,329,330],{"class":278},"index",[155,332,250],{"class":210},[155,334,287],{"class":164},[155,336,275],{"class":214},[155,338,339],{"class":278},"columns",[155,341,250],{"class":210},[155,343,344],{"class":164},"\"SKU\"",[155,346,275],{"class":214},[155,348,349],{"class":278},"values",[155,351,250],{"class":210},[155,353,354],{"class":164},"\"Qty\"",[155,356,275],{"class":214},[155,358,359],{"class":278},"aggfunc",[155,361,250],{"class":210},[155,363,364],{"class":164},"\"sum\"",[155,366,259],{"class":214},[155,368,370,373,375,377,380,382,384,387,389,392,395,397,400,403,405],{"class":80,"line":369},9,[155,371,372],{"class":214},"         .reindex(",[155,374,330],{"class":278},[155,376,250],{"class":210},[155,378,379],{"class":214},"window, ",[155,381,339],{"class":278},[155,383,250],{"class":210},[155,385,386],{"class":214},"stock[",[155,388,344],{"class":164},[155,390,391],{"class":214},"], ",[155,393,394],{"class":278},"fill_value",[155,396,250],{"class":210},[155,398,42],{"class":399},"sP0c6",[155,401,402],{"class":214},").fillna(",[155,404,42],{"class":399},[155,406,407],{"class":214},"))\n",[155,409,411,414,416,419,422,425,428],{"class":80,"line":410},10,[155,412,413],{"class":214},"usage ",[155,415,250],{"class":210},[155,417,418],{"class":214}," pd.DataFrame({",[155,420,421],{"class":164},"\"Daily Usage\"",[155,423,424],{"class":214},": daily.mean(), ",[155,426,427],{"class":164},"\"Usage SD\"",[155,429,430],{"class":214},": daily.std()})\n",[155,432,434,437,440,443,446,449],{"class":80,"line":433},11,[155,435,436],{"class":399},"print",[155,438,439],{"class":214},"(",[155,441,442],{"class":399},"len",[155,444,445],{"class":214},"(window), ",[155,447,448],{"class":164},"\"days\"",[155,450,259],{"class":214},[155,452,454,456,459,462],{"class":80,"line":453},12,[155,455,436],{"class":399},[155,457,458],{"class":214},"(usage.round(",[155,460,461],{"class":399},"2",[155,463,407],{"class":214},[10,465,466,469,470,474],{},[152,467,468],{},"reindex"," over every day in the window fills days without sales with zero, and over every SKU adds items that did not sell at all — the latch, LT-02, has no sales and a usage of zero. Averaging only the days with sales would overstate usage for slow movers, sometimes many times over. ",[14,471,473],{"href":472},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-dates-and-times-in-excel-data\u002Ffill-missing-dates-in-a-time-series-with-pandas\u002F","Fill Missing Dates in a Time Series with pandas"," explains why.",[140,476,478],{"id":477},"step-2-calculate-safety-stock-and-the-reorder-point","Step 2 — Calculate safety stock and the reorder point",[10,480,481],{},"Safety stock covers demand running above average during the lead time. The standard formula scales the day-to-day variation by the square root of the lead time and by a factor for the service level wanted — 1.65 for roughly 95% of cycles without a stock-out:",[145,483,485],{"className":201,"code":484,"language":203,"meta":150,"style":150},"Z = 1.65\nREVIEW_DAYS = 7\n\nplan = stock.merge(usage, left_on=\"SKU\", right_index=True, validate=\"one_to_one\")\nplan[\"Safety Stock\"] = np.ceil(Z * plan[\"Usage SD\"] * np.sqrt(plan[\"Lead Time Days\"]))\nplan[\"Reorder Point\"] = np.ceil(plan[\"Daily Usage\"] * plan[\"Lead Time Days\"]) + plan[\"Safety Stock\"]\nplan[\"Target\"] = plan[\"Reorder Point\"] + np.ceil(plan[\"Daily Usage\"] * REVIEW_DAYS)\nprint(plan[[\"SKU\", \"Daily Usage\", \"Lead Time Days\", \"Safety Stock\", \"Reorder Point\", \"Target\"]].round(1).to_string(index=False))\n",[152,486,487,497,508,512,551,588,625,657],{"__ignoreMap":150},[155,488,489,492,494],{"class":80,"line":157},[155,490,491],{"class":214},"Z ",[155,493,250],{"class":210},[155,495,496],{"class":399}," 1.65\n",[155,498,499,502,505],{"class":80,"line":224},[155,500,501],{"class":399},"REVIEW_DAYS",[155,503,504],{"class":210}," =",[155,506,507],{"class":399}," 7\n",[155,509,510],{"class":80,"line":237},[155,511,241],{"emptyLinePlaceholder":240},[155,513,514,517,519,522,525,527,529,531,534,536,539,541,544,546,549],{"class":80,"line":244},[155,515,516],{"class":214},"plan ",[155,518,250],{"class":210},[155,520,521],{"class":214}," stock.merge(usage, ",[155,523,524],{"class":278},"left_on",[155,526,250],{"class":210},[155,528,344],{"class":164},[155,530,275],{"class":214},[155,532,533],{"class":278},"right_index",[155,535,250],{"class":210},[155,537,538],{"class":399},"True",[155,540,275],{"class":214},[155,542,543],{"class":278},"validate",[155,545,250],{"class":210},[155,547,548],{"class":164},"\"one_to_one\"",[155,550,259],{"class":214},[155,552,553,556,559,562,564,567,570,573,575,577,579,582,585],{"class":80,"line":262},[155,554,555],{"class":214},"plan[",[155,557,558],{"class":164},"\"Safety Stock\"",[155,560,561],{"class":214},"] ",[155,563,250],{"class":210},[155,565,566],{"class":214}," np.ceil(Z ",[155,568,569],{"class":210},"*",[155,571,572],{"class":214}," plan[",[155,574,427],{"class":164},[155,576,561],{"class":214},[155,578,569],{"class":210},[155,580,581],{"class":214}," np.sqrt(plan[",[155,583,584],{"class":164},"\"Lead Time Days\"",[155,586,587],{"class":214},"]))\n",[155,589,590,592,595,597,599,602,604,606,608,610,612,615,618,620,622],{"class":80,"line":293},[155,591,555],{"class":214},[155,593,594],{"class":164},"\"Reorder Point\"",[155,596,561],{"class":214},[155,598,250],{"class":210},[155,600,601],{"class":214}," np.ceil(plan[",[155,603,421],{"class":164},[155,605,561],{"class":214},[155,607,569],{"class":210},[155,609,572],{"class":214},[155,611,584],{"class":164},[155,613,614],{"class":214},"]) ",[155,616,617],{"class":210},"+",[155,619,572],{"class":214},[155,621,558],{"class":164},[155,623,624],{"class":214},"]\n",[155,626,627,629,632,634,636,638,640,642,644,646,648,650,652,655],{"class":80,"line":314},[155,628,555],{"class":214},[155,630,631],{"class":164},"\"Target\"",[155,633,561],{"class":214},[155,635,250],{"class":210},[155,637,572],{"class":214},[155,639,594],{"class":164},[155,641,561],{"class":214},[155,643,617],{"class":210},[155,645,601],{"class":214},[155,647,421],{"class":164},[155,649,561],{"class":214},[155,651,569],{"class":210},[155,653,654],{"class":399}," REVIEW_DAYS",[155,656,259],{"class":214},[155,658,659,661,664,666,668,670,672,674,676,678,680,682,684,686,689,692,695,697,699,702],{"class":80,"line":319},[155,660,436],{"class":399},[155,662,663],{"class":214},"(plan[[",[155,665,344],{"class":164},[155,667,275],{"class":214},[155,669,421],{"class":164},[155,671,275],{"class":214},[155,673,584],{"class":164},[155,675,275],{"class":214},[155,677,558],{"class":164},[155,679,275],{"class":214},[155,681,594],{"class":164},[155,683,275],{"class":214},[155,685,631],{"class":164},[155,687,688],{"class":214},"]].round(",[155,690,691],{"class":399},"1",[155,693,694],{"class":214},").to_string(",[155,696,330],{"class":278},[155,698,250],{"class":210},[155,700,701],{"class":399},"False",[155,703,407],{"class":214},[10,705,706],{},"The target — how high to fill up when ordering — is the reorder point plus a week of usage, because the report runs weekly and the stock has to last until the next review. Items with longer lead times, the panels and frames from Corwell, need proportionally more stock in reserve.",[140,708,710],{"id":709},"step-3-count-stock-on-order","Step 3 — Count stock on order",[10,712,713,714,718],{},"An item already on order should not be ordered again. Add the quantity on open purchase orders to the stock on hand to get the ",[715,716,717],"em",{},"inventory position",", and compare that with the reorder point:",[145,720,722],{"className":201,"code":721,"language":203,"meta":150,"style":150},"pos = pd.read_excel(\"open_pos.xlsx\")\non_order = pos.groupby(\"SKU\")[\"Qty\"].sum()\nplan[\"On Order\"] = plan[\"SKU\"].map(on_order).fillna(0).astype(int)\nplan[\"Position\"] = plan[\"On Hand\"] + plan[\"On Order\"]\nplan[\"Reorder\"] = plan[\"Position\"] \u003C= plan[\"Reorder Point\"]\nprint(plan[[\"SKU\", \"On Hand\", \"On Order\", \"Position\", \"Reorder Point\", \"Reorder\"]].to_string(index=False))\n",[152,723,724,738,758,786,812,838],{"__ignoreMap":150},[155,725,726,729,731,733,736],{"class":80,"line":157},[155,727,728],{"class":214},"pos ",[155,730,250],{"class":210},[155,732,253],{"class":214},[155,734,735],{"class":164},"\"open_pos.xlsx\"",[155,737,259],{"class":214},[155,739,740,743,745,748,750,753,755],{"class":80,"line":224},[155,741,742],{"class":214},"on_order ",[155,744,250],{"class":210},[155,746,747],{"class":214}," pos.groupby(",[155,749,344],{"class":164},[155,751,752],{"class":214},")[",[155,754,354],{"class":164},[155,756,757],{"class":214},"].sum()\n",[155,759,760,762,765,767,769,771,773,776,778,781,784],{"class":80,"line":237},[155,761,555],{"class":214},[155,763,764],{"class":164},"\"On Order\"",[155,766,561],{"class":214},[155,768,250],{"class":210},[155,770,572],{"class":214},[155,772,344],{"class":164},[155,774,775],{"class":214},"].map(on_order).fillna(",[155,777,42],{"class":399},[155,779,780],{"class":214},").astype(",[155,782,783],{"class":399},"int",[155,785,259],{"class":214},[155,787,788,790,793,795,797,799,802,804,806,808,810],{"class":80,"line":244},[155,789,555],{"class":214},[155,791,792],{"class":164},"\"Position\"",[155,794,561],{"class":214},[155,796,250],{"class":210},[155,798,572],{"class":214},[155,800,801],{"class":164},"\"On Hand\"",[155,803,561],{"class":214},[155,805,617],{"class":210},[155,807,572],{"class":214},[155,809,764],{"class":164},[155,811,624],{"class":214},[155,813,814,816,819,821,823,825,827,829,832,834,836],{"class":80,"line":262},[155,815,555],{"class":214},[155,817,818],{"class":164},"\"Reorder\"",[155,820,561],{"class":214},[155,822,250],{"class":210},[155,824,572],{"class":214},[155,826,792],{"class":164},[155,828,561],{"class":214},[155,830,831],{"class":210},"\u003C=",[155,833,572],{"class":214},[155,835,594],{"class":164},[155,837,624],{"class":214},[155,839,840,842,844,846,848,850,852,854,856,858,860,862,864,866,869,871,873,875],{"class":80,"line":293},[155,841,436],{"class":399},[155,843,663],{"class":214},[155,845,344],{"class":164},[155,847,275],{"class":214},[155,849,801],{"class":164},[155,851,275],{"class":214},[155,853,764],{"class":164},[155,855,275],{"class":214},[155,857,792],{"class":164},[155,859,275],{"class":214},[155,861,594],{"class":164},[155,863,275],{"class":214},[155,865,818],{"class":164},[155,867,868],{"class":214},"]].to_string(",[155,870,330],{"class":278},[155,872,250],{"class":210},[155,874,701],{"class":399},[155,876,407],{"class":214},[10,878,879],{},"The large brackets, BR-20, are below their reorder point on hand alone, but the 100 already on order lift their position above it, so they are not reordered. Without that step the report would order them again every week until the delivery arrived — the double-ordering failure the report exists to prevent.",[20,881,29,887,29,890,29,893,29,898,29,906,29,913,29,919,29,925,29,931,29,938,29,942,29,947,29,951,29,956,29,959,29,965,29,970,29,974,29,978,29,983,29,986,29,991,29,994],{"viewBox":882,"role":23,"ariaLabelledBy":883,"xmlns":27,"style":886},"4 0 752 207",[884,885],"inv-position-t","inv-position-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,888,889],{"id":884},"Why stock on order has to be counted",[35,891,892],{"id":885},"Two panels for the large bracket, BR-20. Looking at on hand only, 55 units is below the reorder point, so it would be ordered again. Looking at the inventory position, 55 on hand plus 100 on order is 155, above the reorder point, so no new order is needed.",[39,894],{"x":895,"y":42,"width":896,"height":897,"fill":45},"4","752","207",[39,899],{"x":900,"y":49,"width":901,"height":902,"rx":903,"fill":904,"stroke":905,"style":55},"20.0","351.0","175.0","14","#fee8f2","var(--accent,#f43f8f)",[57,907,909],{"transform":908},"translate(182.5,29.0) scale(1.083)",[61,910],{"d":100,"fill":64,"stroke":911,"style":912},"var(--accent-ink,#be185d)","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[68,914,918],{"x":915,"y":916,"style":917},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","On hand only",[80,920],{"x1":921,"y1":922,"x2":923,"y2":922,"stroke":905,"style":924},"36.0","91.0","355.0","stroke-width:1px",[68,926,930],{"x":927,"y":928,"style":929},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","55 on hand",[57,932,934],{"transform":933},"translate(38.5,99.5) scale(0.625)",[61,935],{"d":936,"fill":64,"stroke":905,"style":937},"M6 6l12 12 M18 6L6 18","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[68,939,941],{"x":927,"y":940,"style":929},"137.0","below the reorder point",[57,943,945],{"transform":944},"translate(38.5,125.5) scale(0.625)",[61,946],{"d":936,"fill":64,"stroke":905,"style":937},[68,948,950],{"x":927,"y":949,"style":929},"163.0","ordered again, twice",[57,952,954],{"transform":953},"translate(38.5,151.5) scale(0.625)",[61,955],{"d":936,"fill":64,"stroke":905,"style":937},[39,957],{"x":958,"y":49,"width":901,"height":902,"rx":903,"fill":121,"stroke":122,"style":55},"389.0",[57,960,962],{"transform":961},"translate(551.5,29.0) scale(1.083)",[61,963],{"d":964,"fill":64,"stroke":129,"style":912},"M5 12.5l4.5 4.5L19 7",[68,966,969],{"x":967,"y":916,"style":968},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","On hand + on order",[80,971],{"x1":972,"y1":922,"x2":973,"y2":922,"stroke":122,"style":924},"405.0","724.0",[68,975,977],{"x":976,"y":928,"style":929},"429.0","55 + 100 = 155",[57,979,981],{"transform":980},"translate(407.5,99.5) scale(0.625)",[61,982],{"d":964,"fill":64,"stroke":122,"style":937},[68,984,985],{"x":976,"y":940,"style":929},"above the reorder point",[57,987,989],{"transform":988},"translate(407.5,125.5) scale(0.625)",[61,990],{"d":964,"fill":64,"stroke":122,"style":937},[68,992,993],{"x":976,"y":949,"style":929},"no duplicate order",[57,995,997],{"transform":996},"translate(407.5,151.5) scale(0.625)",[61,998],{"d":964,"fill":64,"stroke":122,"style":937},[140,1000,1002],{"id":1001},"step-4-size-the-orders","Step 4 — Size the orders",[10,1004,1005],{},"For items to reorder, the quantity is whatever lifts the position to the target, rounded up to whole packs and to at least the supplier's minimum order quantity:",[145,1007,1009],{"className":201,"code":1008,"language":203,"meta":150,"style":150},"def order_qty(row):\n    if not row[\"Reorder\"] or row[\"Daily Usage\"] == 0:\n        return 0\n    need = max(row[\"Target\"] - row[\"Position\"], 0)\n    packs = np.ceil(need \u002F row[\"Pack Size\"])\n    qty = max(packs * row[\"Pack Size\"], row[\"MOQ\"])\n    return int(np.ceil(qty \u002F row[\"Pack Size\"]) * row[\"Pack Size\"])\n\nplan[\"Order Qty\"] = plan.apply(order_qty, axis=1)\nplan[\"Days of Cover\"] = (plan[\"Position\"] \u002F plan[\"Daily Usage\"]).replace(np.inf, np.nan).round(1)\nplan[\"Stock-out Risk\"] = plan[\"Days of Cover\"] \u003C plan[\"Lead Time Days\"]\nprint(plan[[\"SKU\", \"Position\", \"Target\", \"Pack Size\", \"MOQ\", \"Order Qty\", \"Days of Cover\", \"Stock-out Risk\"]].to_string(index=False))\n",[152,1010,1011,1023,1056,1064,1094,1114,1140,1167,1171,1194,1225,1251],{"__ignoreMap":150},[155,1012,1013,1016,1020],{"class":80,"line":157},[155,1014,1015],{"class":210},"def",[155,1017,1019],{"class":1018},"s_Opv"," order_qty",[155,1021,1022],{"class":214},"(row):\n",[155,1024,1025,1028,1031,1034,1036,1038,1041,1043,1045,1047,1050,1053],{"class":80,"line":224},[155,1026,1027],{"class":210},"    if",[155,1029,1030],{"class":210}," not",[155,1032,1033],{"class":214}," row[",[155,1035,818],{"class":164},[155,1037,561],{"class":214},[155,1039,1040],{"class":210},"or",[155,1042,1033],{"class":214},[155,1044,421],{"class":164},[155,1046,561],{"class":214},[155,1048,1049],{"class":210},"==",[155,1051,1052],{"class":399}," 0",[155,1054,1055],{"class":214},":\n",[155,1057,1058,1061],{"class":80,"line":237},[155,1059,1060],{"class":210},"        return",[155,1062,1063],{"class":399}," 0\n",[155,1065,1066,1069,1071,1074,1077,1079,1081,1084,1086,1088,1090,1092],{"class":80,"line":244},[155,1067,1068],{"class":214},"    need ",[155,1070,250],{"class":210},[155,1072,1073],{"class":399}," max",[155,1075,1076],{"class":214},"(row[",[155,1078,631],{"class":164},[155,1080,561],{"class":214},[155,1082,1083],{"class":210},"-",[155,1085,1033],{"class":214},[155,1087,792],{"class":164},[155,1089,391],{"class":214},[155,1091,42],{"class":399},[155,1093,259],{"class":214},[155,1095,1096,1099,1101,1104,1107,1109,1112],{"class":80,"line":262},[155,1097,1098],{"class":214},"    packs ",[155,1100,250],{"class":210},[155,1102,1103],{"class":214}," np.ceil(need ",[155,1105,1106],{"class":210},"\u002F",[155,1108,1033],{"class":214},[155,1110,1111],{"class":164},"\"Pack Size\"",[155,1113,290],{"class":214},[155,1115,1116,1119,1121,1123,1126,1128,1130,1132,1135,1138],{"class":80,"line":293},[155,1117,1118],{"class":214},"    qty ",[155,1120,250],{"class":210},[155,1122,1073],{"class":399},[155,1124,1125],{"class":214},"(packs ",[155,1127,569],{"class":210},[155,1129,1033],{"class":214},[155,1131,1111],{"class":164},[155,1133,1134],{"class":214},"], row[",[155,1136,1137],{"class":164},"\"MOQ\"",[155,1139,290],{"class":214},[155,1141,1142,1145,1148,1151,1153,1155,1157,1159,1161,1163,1165],{"class":80,"line":314},[155,1143,1144],{"class":210},"    return",[155,1146,1147],{"class":399}," int",[155,1149,1150],{"class":214},"(np.ceil(qty ",[155,1152,1106],{"class":210},[155,1154,1033],{"class":214},[155,1156,1111],{"class":164},[155,1158,614],{"class":214},[155,1160,569],{"class":210},[155,1162,1033],{"class":214},[155,1164,1111],{"class":164},[155,1166,290],{"class":214},[155,1168,1169],{"class":80,"line":319},[155,1170,241],{"emptyLinePlaceholder":240},[155,1172,1173,1175,1178,1180,1182,1185,1188,1190,1192],{"class":80,"line":369},[155,1174,555],{"class":214},[155,1176,1177],{"class":164},"\"Order Qty\"",[155,1179,561],{"class":214},[155,1181,250],{"class":210},[155,1183,1184],{"class":214}," plan.apply(order_qty, ",[155,1186,1187],{"class":278},"axis",[155,1189,250],{"class":210},[155,1191,691],{"class":399},[155,1193,259],{"class":214},[155,1195,1196,1198,1201,1203,1205,1208,1210,1212,1214,1216,1218,1221,1223],{"class":80,"line":410},[155,1197,555],{"class":214},[155,1199,1200],{"class":164},"\"Days of Cover\"",[155,1202,561],{"class":214},[155,1204,250],{"class":210},[155,1206,1207],{"class":214}," (plan[",[155,1209,792],{"class":164},[155,1211,561],{"class":214},[155,1213,1106],{"class":210},[155,1215,572],{"class":214},[155,1217,421],{"class":164},[155,1219,1220],{"class":214},"]).replace(np.inf, np.nan).round(",[155,1222,691],{"class":399},[155,1224,259],{"class":214},[155,1226,1227,1229,1232,1234,1236,1238,1240,1242,1245,1247,1249],{"class":80,"line":433},[155,1228,555],{"class":214},[155,1230,1231],{"class":164},"\"Stock-out Risk\"",[155,1233,561],{"class":214},[155,1235,250],{"class":210},[155,1237,572],{"class":214},[155,1239,1200],{"class":164},[155,1241,561],{"class":214},[155,1243,1244],{"class":210},"\u003C",[155,1246,572],{"class":214},[155,1248,584],{"class":164},[155,1250,624],{"class":214},[155,1252,1253,1255,1257,1259,1261,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293,1295],{"class":80,"line":453},[155,1254,436],{"class":399},[155,1256,663],{"class":214},[155,1258,344],{"class":164},[155,1260,275],{"class":214},[155,1262,792],{"class":164},[155,1264,275],{"class":214},[155,1266,631],{"class":164},[155,1268,275],{"class":214},[155,1270,1111],{"class":164},[155,1272,275],{"class":214},[155,1274,1137],{"class":164},[155,1276,275],{"class":214},[155,1278,1177],{"class":164},[155,1280,275],{"class":214},[155,1282,1200],{"class":164},[155,1284,275],{"class":214},[155,1286,1231],{"class":164},[155,1288,868],{"class":214},[155,1290,330],{"class":278},[155,1292,250],{"class":210},[155,1294,701],{"class":399},[155,1296,407],{"class":214},[10,1298,1299,1300,1304],{},"The small brackets, BR-10, need 191 units to reach their target, which rounds up to four packs of 50 — 200, which also meets the supplier's minimum. Rounding up the MOQ to a pack handles suppliers whose minimum is not a multiple of their pack size. Items with no usage are never ordered automatically, even with zero stock: the latch has sold nothing in 90 days, and whether to restock a dead item is a decision for a person, not a formula. ",[1301,1302,1303],"strong",{},"Days of cover"," — how long the stock on hand and on order lasts at the average rate — is the figure buyers read first. A cover shorter than the lead time means the item will probably run out before a new order arrives, whatever is ordered today, so it needs expediting or a faster supplier rather than just a purchase order. Counting stock on order here too keeps an item with a delivery due next week from being flagged needlessly.",[140,1306,1308],{"id":1307},"step-5-write-a-purchase-list-per-supplier","Step 5 — Write a purchase list per supplier",[10,1310,1311],{},"Buyers place orders per supplier, so group the lines to order by supplier, with a sheet each and a summary of everything that needs attention:",[145,1313,1315],{"className":201,"code":1314,"language":203,"meta":150,"style":150},"cols = [\"SKU\", \"Description\", \"On Hand\", \"On Order\", \"Reorder Point\", \"Order Qty\", \"Days of Cover\"]\nto_order = plan[plan[\"Order Qty\"] > 0]\nwith pd.ExcelWriter(\"reorder_2026-09-28.xlsx\", engine=\"xlsxwriter\") as writer:\n    wb = writer.book\n    warn = wb.add_format({\"bg_color\": \"#FFC7CE\", \"font_color\": \"#9C0006\"})\n    plan[[\"Supplier\"] + cols + [\"Stock-out Risk\"]].to_excel(writer, sheet_name=\"All items\", index=False)\n    ws = writer.sheets[\"All items\"]\n    ws.set_column(\"A:C\", 14)\n    ws.conditional_format(1, 0, len(plan), 8, {\"type\": \"formula\", \"criteria\": \"=$I2=TRUE\", \"format\": warn})\n    for supplier, lines in to_order.groupby(\"Supplier\"):\n        lines[cols].to_excel(writer, sheet_name=supplier, index=False)\n        writer.sheets[supplier].set_column(\"A:B\", 14)\n        print(supplier, lines[[\"SKU\", \"Order Qty\"]].values.tolist())\nprint(\"stock-out risk:\", plan.loc[plan[\"Stock-out Risk\"], \"SKU\"].tolist())\nprint(\"no usage, review manually:\", plan.loc[plan[\"Daily Usage\"] == 0, \"SKU\"].tolist())\n",[152,1316,1317,1356,1377,1406,1416,1448,1490,1504,1518,1568,1587,1607,1621,1639,1661],{"__ignoreMap":150},[155,1318,1319,1322,1324,1327,1329,1331,1334,1336,1338,1340,1342,1344,1346,1348,1350,1352,1354],{"class":80,"line":157},[155,1320,1321],{"class":214},"cols ",[155,1323,250],{"class":210},[155,1325,1326],{"class":214}," [",[155,1328,344],{"class":164},[155,1330,275],{"class":214},[155,1332,1333],{"class":164},"\"Description\"",[155,1335,275],{"class":214},[155,1337,801],{"class":164},[155,1339,275],{"class":214},[155,1341,764],{"class":164},[155,1343,275],{"class":214},[155,1345,594],{"class":164},[155,1347,275],{"class":214},[155,1349,1177],{"class":164},[155,1351,275],{"class":214},[155,1353,1200],{"class":164},[155,1355,624],{"class":214},[155,1357,1358,1361,1363,1366,1368,1370,1373,1375],{"class":80,"line":224},[155,1359,1360],{"class":214},"to_order ",[155,1362,250],{"class":210},[155,1364,1365],{"class":214}," plan[plan[",[155,1367,1177],{"class":164},[155,1369,561],{"class":214},[155,1371,1372],{"class":210},">",[155,1374,1052],{"class":399},[155,1376,624],{"class":214},[155,1378,1379,1382,1385,1388,1390,1393,1395,1398,1401,1403],{"class":80,"line":237},[155,1380,1381],{"class":210},"with",[155,1383,1384],{"class":214}," pd.ExcelWriter(",[155,1386,1387],{"class":164},"\"reorder_2026-09-28.xlsx\"",[155,1389,275],{"class":214},[155,1391,1392],{"class":278},"engine",[155,1394,250],{"class":210},[155,1396,1397],{"class":164},"\"xlsxwriter\"",[155,1399,1400],{"class":214},") ",[155,1402,218],{"class":210},[155,1404,1405],{"class":214}," writer:\n",[155,1407,1408,1411,1413],{"class":80,"line":244},[155,1409,1410],{"class":214},"    wb ",[155,1412,250],{"class":210},[155,1414,1415],{"class":214}," writer.book\n",[155,1417,1418,1421,1423,1426,1429,1432,1435,1437,1440,1442,1445],{"class":80,"line":262},[155,1419,1420],{"class":214},"    warn ",[155,1422,250],{"class":210},[155,1424,1425],{"class":214}," wb.add_format({",[155,1427,1428],{"class":164},"\"bg_color\"",[155,1430,1431],{"class":214},": ",[155,1433,1434],{"class":164},"\"#FFC7CE\"",[155,1436,275],{"class":214},[155,1438,1439],{"class":164},"\"font_color\"",[155,1441,1431],{"class":214},[155,1443,1444],{"class":164},"\"#9C0006\"",[155,1446,1447],{"class":214},"})\n",[155,1449,1450,1453,1456,1458,1460,1463,1465,1467,1469,1472,1475,1477,1480,1482,1484,1486,1488],{"class":80,"line":293},[155,1451,1452],{"class":214},"    plan[[",[155,1454,1455],{"class":164},"\"Supplier\"",[155,1457,561],{"class":214},[155,1459,617],{"class":210},[155,1461,1462],{"class":214}," cols ",[155,1464,617],{"class":210},[155,1466,1326],{"class":214},[155,1468,1231],{"class":164},[155,1470,1471],{"class":214},"]].to_excel(writer, ",[155,1473,1474],{"class":278},"sheet_name",[155,1476,250],{"class":210},[155,1478,1479],{"class":164},"\"All items\"",[155,1481,275],{"class":214},[155,1483,330],{"class":278},[155,1485,250],{"class":210},[155,1487,701],{"class":399},[155,1489,259],{"class":214},[155,1491,1492,1495,1497,1500,1502],{"class":80,"line":314},[155,1493,1494],{"class":214},"    ws ",[155,1496,250],{"class":210},[155,1498,1499],{"class":214}," writer.sheets[",[155,1501,1479],{"class":164},[155,1503,624],{"class":214},[155,1505,1506,1509,1512,1514,1516],{"class":80,"line":319},[155,1507,1508],{"class":214},"    ws.set_column(",[155,1510,1511],{"class":164},"\"A:C\"",[155,1513,275],{"class":214},[155,1515,903],{"class":399},[155,1517,259],{"class":214},[155,1519,1520,1523,1525,1527,1529,1531,1533,1536,1539,1542,1545,1547,1550,1552,1555,1557,1560,1562,1565],{"class":80,"line":369},[155,1521,1522],{"class":214},"    ws.conditional_format(",[155,1524,691],{"class":399},[155,1526,275],{"class":214},[155,1528,42],{"class":399},[155,1530,275],{"class":214},[155,1532,442],{"class":399},[155,1534,1535],{"class":214},"(plan), ",[155,1537,1538],{"class":399},"8",[155,1540,1541],{"class":214},", {",[155,1543,1544],{"class":164},"\"type\"",[155,1546,1431],{"class":214},[155,1548,1549],{"class":164},"\"formula\"",[155,1551,275],{"class":214},[155,1553,1554],{"class":164},"\"criteria\"",[155,1556,1431],{"class":214},[155,1558,1559],{"class":164},"\"=$I2=TRUE\"",[155,1561,275],{"class":214},[155,1563,1564],{"class":164},"\"format\"",[155,1566,1567],{"class":214},": warn})\n",[155,1569,1570,1573,1576,1579,1582,1584],{"class":80,"line":410},[155,1571,1572],{"class":210},"    for",[155,1574,1575],{"class":214}," supplier, lines ",[155,1577,1578],{"class":210},"in",[155,1580,1581],{"class":214}," to_order.groupby(",[155,1583,1455],{"class":164},[155,1585,1586],{"class":214},"):\n",[155,1588,1589,1592,1594,1596,1599,1601,1603,1605],{"class":80,"line":433},[155,1590,1591],{"class":214},"        lines[cols].to_excel(writer, ",[155,1593,1474],{"class":278},[155,1595,250],{"class":210},[155,1597,1598],{"class":214},"supplier, ",[155,1600,330],{"class":278},[155,1602,250],{"class":210},[155,1604,701],{"class":399},[155,1606,259],{"class":214},[155,1608,1609,1612,1615,1617,1619],{"class":80,"line":453},[155,1610,1611],{"class":214},"        writer.sheets[supplier].set_column(",[155,1613,1614],{"class":164},"\"A:B\"",[155,1616,275],{"class":214},[155,1618,903],{"class":399},[155,1620,259],{"class":214},[155,1622,1624,1627,1630,1632,1634,1636],{"class":80,"line":1623},13,[155,1625,1626],{"class":399},"        print",[155,1628,1629],{"class":214},"(supplier, lines[[",[155,1631,344],{"class":164},[155,1633,275],{"class":214},[155,1635,1177],{"class":164},[155,1637,1638],{"class":214},"]].values.tolist())\n",[155,1640,1642,1644,1646,1649,1652,1654,1656,1658],{"class":80,"line":1641},14,[155,1643,436],{"class":399},[155,1645,439],{"class":214},[155,1647,1648],{"class":164},"\"stock-out risk:\"",[155,1650,1651],{"class":214},", plan.loc[plan[",[155,1653,1231],{"class":164},[155,1655,391],{"class":214},[155,1657,344],{"class":164},[155,1659,1660],{"class":214},"].tolist())\n",[155,1662,1664,1666,1668,1671,1673,1675,1677,1679,1681,1683,1685],{"class":80,"line":1663},15,[155,1665,436],{"class":399},[155,1667,439],{"class":214},[155,1669,1670],{"class":164},"\"no usage, review manually:\"",[155,1672,1651],{"class":214},[155,1674,421],{"class":164},[155,1676,561],{"class":214},[155,1678,1049],{"class":210},[155,1680,1052],{"class":399},[155,1682,275],{"class":214},[155,1684,344],{"class":164},[155,1686,1660],{"class":214},[20,1688,29,1694,29,1697,29,1700,29,1705,29,1710,29,1716,29,1720,29,1726,29,1730,29,1734,29,1738,29,1742,29,1746,29,1750,29,1754,29,1758,29,1762,29,1766,29,1770,29,1773,29,1775,29,1779,29,1781,29,1784,29,1786,29,1789,29,1791,29,1794,29,1796,29,1799,29,1801,29,1804,29,1806,29,1809,29,1811,29,1815,29,1817,29,1820,29,1822,29,1825,29,1827,29,1829,29,1831,29,1834,29,1837,29,1841,29,1844,29,1848,29,1850,29,1852,29,1854,29,1857,29,1859,29,1862,29,1864,29,1867,29,1869,29,1871,29,1873,29,1876,29,1879,29,1881,29,1883,29,1886,29,1888,29,1891,29,1893,29,1896,29,1898,29,1901,29,1903,29,1906,29,1908,29,1911,29,1914,29,1918,29,1920,29,1922,29,1924,29,1927,29,1929,29,1931,29,1933,29,1935,29,1937,29,1939,29,1941],{"viewBox":1689,"role":23,"ariaLabelledBy":1690,"xmlns":27,"style":1693},"107 0 546 228",[1691,1692],"inv-list-t","inv-list-d","width:100%;max-width:546px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1695,1696],{"id":1691},"The reorder list, one tab per supplier",[35,1698,1699],{"id":1692},"The All items sheet of the reorder report. The small bracket BR-10 has 300 on hand, is ordered 200 and has 13.6 days of cover, shorter than its 14-day lead time, so it is highlighted. BR-20 has 55 on hand and 100 on order and needs nothing. The panel PN-40 is ordered 23 with 28.5 days of cover against a 30-day lead time, also highlighted. The latch LT-02 has no usage and is left for manual review.",[39,1701],{"x":1702,"y":42,"width":1703,"height":1704,"fill":45},"107","546","228",[68,1706,1709],{"x":70,"y":1707,"style":1708},"31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","reorder_2026-09-28.xlsx — All items",[39,1711],{"x":1712,"y":1713,"width":1714,"height":1715,"rx":42,"fill":53,"stroke":54,"style":924},"123.3","48.0","32.0","24.0",[39,1717],{"x":1718,"y":1713,"width":1719,"height":1715,"rx":42,"fill":53,"stroke":54,"style":924},"155.3","81.1",[68,1721,1725],{"x":1722,"y":1723,"style":1724},"195.9","65.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[39,1727],{"x":1728,"y":1713,"width":1729,"height":1715,"rx":42,"fill":53,"stroke":54,"style":924},"236.4","64.7",[68,1731,1733],{"x":1732,"y":1723,"style":1724},"268.8","B",[39,1735],{"x":1736,"y":1713,"width":1737,"height":1715,"rx":42,"fill":53,"stroke":54,"style":924},"301.1","77.1",[68,1739,1741],{"x":1740,"y":1723,"style":1724},"339.6","C",[39,1743],{"x":1744,"y":1713,"width":1745,"height":1715,"rx":42,"fill":53,"stroke":54,"style":924},"378.2","80.0",[68,1747,1749],{"x":1748,"y":1723,"style":1724},"418.2","D",[39,1751],{"x":1752,"y":1713,"width":1753,"height":1715,"rx":42,"fill":53,"stroke":54,"style":924},"458.1","84.4",[68,1755,1757],{"x":1756,"y":1723,"style":1724},"500.3","E",[39,1759],{"x":1760,"y":1713,"width":1761,"height":1715,"rx":42,"fill":53,"stroke":54,"style":924},"542.5","94.1",[68,1763,1765],{"x":1764,"y":1723,"style":1724},"589.6","F",[39,1767],{"x":1712,"y":1768,"width":1714,"height":1769,"rx":42,"fill":53,"stroke":54,"style":924},"72.0","28.0",[68,1771,691],{"x":1772,"y":922,"style":1724},"139.3",[39,1774],{"x":1718,"y":1768,"width":1719,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1776,1778],{"x":1722,"y":922,"style":1777},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Supplier",[39,1780],{"x":1728,"y":1768,"width":1729,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1782,1783],{"x":1732,"y":922,"style":1777},"SKU",[39,1785],{"x":1736,"y":1768,"width":1737,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1787,1788],{"x":1740,"y":922,"style":1777},"On Hand",[39,1790],{"x":1744,"y":1768,"width":1745,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1792,1793],{"x":1748,"y":922,"style":1777},"On Order",[39,1795],{"x":1752,"y":1768,"width":1753,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1797,1798],{"x":1756,"y":922,"style":1777},"Order Qty",[39,1800],{"x":1760,"y":1768,"width":1761,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1802,1803],{"x":1764,"y":922,"style":1777},"Cover days",[39,1805],{"x":1712,"y":48,"width":1714,"height":1769,"rx":42,"fill":53,"stroke":54,"style":924},[68,1807,461],{"x":1772,"y":1808,"style":1724},"119.0",[39,1810],{"x":1718,"y":48,"width":1719,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1812,1814],{"x":1722,"y":1808,"style":1813},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Northline",[39,1816],{"x":1728,"y":48,"width":1729,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1818,1819],{"x":1732,"y":1808,"style":1813},"BR-10",[39,1821],{"x":1736,"y":48,"width":1737,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1823,1824],{"x":1740,"y":1808,"style":1813},"300",[39,1826],{"x":1744,"y":48,"width":1745,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1828,42],{"x":1748,"y":1808,"style":1813},[39,1830],{"x":1752,"y":48,"width":1753,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1832,1833],{"x":1756,"y":1808,"style":1813},"200",[39,1835],{"x":1760,"y":48,"width":1761,"height":1769,"rx":42,"fill":904,"stroke":905,"style":1836},"stroke-width:1.5px",[68,1838,1840],{"x":1764,"y":1808,"style":1839},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","13.6",[39,1842],{"x":1712,"y":1843,"width":1714,"height":1769,"rx":42,"fill":53,"stroke":54,"style":924},"128.0",[68,1845,1847],{"x":1772,"y":1846,"style":1724},"147.0","3",[39,1849],{"x":1718,"y":1843,"width":1719,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1851,1814],{"x":1722,"y":1846,"style":1813},[39,1853],{"x":1728,"y":1843,"width":1729,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1855,1856],{"x":1732,"y":1846,"style":1813},"BR-20",[39,1858],{"x":1736,"y":1843,"width":1737,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1860,1861],{"x":1740,"y":1846,"style":1813},"55",[39,1863],{"x":1744,"y":1843,"width":1745,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1865,1866],{"x":1748,"y":1846,"style":1813},"100",[39,1868],{"x":1752,"y":1843,"width":1753,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1870,42],{"x":1756,"y":1846,"style":1813},[39,1872],{"x":1760,"y":1843,"width":1761,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1874,1875],{"x":1764,"y":1846,"style":1813},"27.4",[39,1877],{"x":1712,"y":1878,"width":1714,"height":1769,"rx":42,"fill":53,"stroke":54,"style":924},"156.0",[68,1880,895],{"x":1772,"y":902,"style":1724},[39,1882],{"x":1718,"y":1878,"width":1719,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1884,1885],{"x":1722,"y":902,"style":1813},"Corwell",[39,1887],{"x":1728,"y":1878,"width":1729,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1889,1890],{"x":1732,"y":902,"style":1813},"PN-40",[39,1892],{"x":1736,"y":1878,"width":1737,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1894,1895],{"x":1740,"y":902,"style":1813},"18",[39,1897],{"x":1744,"y":1878,"width":1745,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1899,1900],{"x":1748,"y":902,"style":1813},"20",[39,1902],{"x":1752,"y":1878,"width":1753,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1904,1905],{"x":1756,"y":902,"style":1813},"23",[39,1907],{"x":1760,"y":1878,"width":1761,"height":1769,"rx":42,"fill":904,"stroke":905,"style":1836},[68,1909,1910],{"x":1764,"y":902,"style":1839},"28.5",[39,1912],{"x":1712,"y":1913,"width":1714,"height":1769,"rx":42,"fill":53,"stroke":54,"style":924},"184.0",[68,1915,1917],{"x":1772,"y":1916,"style":1724},"203.0","5",[39,1919],{"x":1718,"y":1913,"width":1719,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1921,1885],{"x":1722,"y":1916,"style":1813},[39,1923],{"x":1728,"y":1913,"width":1729,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1925,1926],{"x":1732,"y":1916,"style":1813},"LT-02",[39,1928],{"x":1736,"y":1913,"width":1737,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1930,42],{"x":1740,"y":1916,"style":1813},[39,1932],{"x":1744,"y":1913,"width":1745,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1934,42],{"x":1748,"y":1916,"style":1813},[39,1936],{"x":1752,"y":1913,"width":1753,"height":1769,"rx":42,"fill":45,"stroke":54,"style":924},[68,1938,42],{"x":1756,"y":1916,"style":1813},[39,1940],{"x":1760,"y":1913,"width":1761,"height":1769,"rx":42,"fill":93,"stroke":94,"style":1836},[68,1942,1944],{"x":1764,"y":1916,"style":1943},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","no usage",[10,1946,1947],{},"The per-supplier sheets are ready to paste into a purchase order, or to feed a script that raises the orders in the purchasing system. The \"All items\" sheet highlights stock-out risks, so the buyer knows which orders to chase or expedite.",[140,1949,1951],{"id":1950},"common-pitfalls","Common pitfalls",[1953,1954,1955,1962,1968,1974,1980,1986],"ul",{},[1956,1957,1958,1961],"li",{},[1301,1959,1960],{},"Averaging only days with sales."," Reindex over every day in the window, filling zeros.",[1956,1963,1964,1967],{},[1301,1965,1966],{},"Ignoring stock on order."," Compare the reorder point with on hand plus on order.",[1956,1969,1970,1973],{},[1301,1971,1972],{},"Ordering dead stock."," Zero-usage items need a human decision, not an automatic order.",[1956,1975,1976,1979],{},[1301,1977,1978],{},"Forgetting pack sizes and MOQs."," Round up to packs, then to the minimum.",[1956,1981,1982,1985],{},[1301,1983,1984],{},"Stale lead times."," Supplier lead times drift; keep them in the stock file where buyers update them.",[1956,1987,1988,1991],{},[1301,1989,1990],{},"One window for everything."," Seasonal items need a window that reflects the coming season, not the last 90 days.",[140,1993,1995],{"id":1994},"performance-notes","Performance notes",[10,1997,1998,1999,2002,2003,2006,2007,2010,2011,2014],{},"The calculations are vectorised apart from the order sizing, which is fine for thousands of items; for hundreds of thousands, replace the ",[152,2000,2001],{},"apply"," with ",[152,2004,2005],{},"np.ceil"," on whole columns and ",[152,2008,2009],{},"np.maximum"," for the MOQ. The pivot of daily sales is the largest object — SKUs × days — so for very large catalogues aggregate usage with ",[152,2012,2013],{},"groupby"," instead.",[140,2016,2018],{"id":2017},"conclusion","Conclusion",[10,2020,2021],{},"Compute average daily usage and its variation over every day in a recent window, including zero-sale days. Set safety stock from the variation, lead time and service level, and the reorder point as lead-time demand plus safety stock. Compare the reorder point with on hand plus on order, size orders up to a target in whole packs and at least the MOQ, flag items whose cover is shorter than their lead time, and write the purchase list grouped by supplier.",[140,2023,2025],{"id":2024},"frequently-asked-questions","Frequently asked questions",[10,2027,2028,2031],{},[1301,2029,2030],{},"How do I calculate a reorder point in Python?","\nReorder point = average daily usage × lead time in days + safety stock. Compute daily usage from recent sales history with pandas, join the lead times, and compare each item's inventory position — on hand plus on order — with its reorder point.",[10,2033,2034,2037],{},[1301,2035,2036],{},"Why include stock already on order?","\nStock on an open purchase order will arrive within the lead time. Ignoring it makes the report order the same items again every week until the delivery lands. Compare the reorder point with on hand plus on order, not on hand alone.",[10,2039,2040,2043],{},[1301,2041,2042],{},"How do I round order quantities to pack sizes and minimum order quantities?","\nRound the required quantity up to a whole number of packs with ceil(required \u002F pack) × pack, then take the larger of that and the MOQ, rounded up to a pack again if the MOQ is not a multiple of the pack size.",[10,2045,2046,2049],{},[1301,2047,2048],{},"How much safety stock should I hold?","\nA common formula is z × standard deviation of daily demand × √lead time, where z sets the service level — about 1.65 for 95%. It grows with how variable demand is and how long the lead time is.",[140,2051,2053],{"id":2052},"related","Related",[1953,2055,2056,2062,2069,2076],{},[1956,2057,2058,2059,2061],{},"Up one level: ",[14,2060,17],{"href":16}," — the shared report pipeline.",[1956,2063,2064,2068],{},[14,2065,2067],{"href":2066},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fcalculate-moving-averages-with-pandas-rolling\u002F","Calculate Moving Averages with pandas rolling"," — smoothing usage over time.",[1956,2070,2071,2075],{},[14,2072,2074],{"href":2073},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Frun-a-pareto-analysis-on-excel-data-with-pandas\u002F","Run a Pareto Analysis on Excel Data with pandas"," — ranking items by importance.",[1956,2077,2078,2082],{},[14,2079,2081],{"href":2080},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsplit-one-excel-sheet-into-multiple-files-by-value\u002F","Split One Excel Sheet into Multiple Files by Value"," — one file per supplier.",[2084,2085,2086],"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}",{"title":150,"searchDepth":224,"depth":224,"links":2088},[2089,2090,2091,2092,2093,2094,2095,2096,2097,2098,2099],{"id":142,"depth":224,"text":143},{"id":194,"depth":224,"text":195},{"id":477,"depth":224,"text":478},{"id":709,"depth":224,"text":710},{"id":1001,"depth":224,"text":1002},{"id":1307,"depth":224,"text":1308},{"id":1950,"depth":224,"text":1951},{"id":1994,"depth":224,"text":1995},{"id":2017,"depth":224,"text":2018},{"id":2024,"depth":224,"text":2025},{"id":2052,"depth":224,"text":2053},"2026-09-25","Decide what to order this week from a stock export and recent sales: average daily usage, reorder points from lead time and safety stock, stock already on order counted, order quantities rounded up to pack sizes and minimums, days of cover, stock-out risks flagged and a purchase list grouped by supplier.","md",[2104,2106,2108,2110],{"q":2030,"a":2105},"Reorder point = average daily usage × lead time in days + safety stock. Compute daily usage from recent sales history with pandas, join the lead times, and compare each item's inventory position — on hand plus on order — with its reorder point.",{"q":2036,"a":2107},"Stock on an open purchase order will arrive within the lead time. Ignoring it makes the report order the same items again every week until the delivery lands. Compare the reorder point with on hand plus on order, not on hand alone.",{"q":2042,"a":2109},"Round the required quantity up to a whole number of packs with ceil(required \u002F pack) × pack, then take the larger of that and the MOQ, rounded up to a pack again if the MOQ is not a multiple of the pack size.",{"q":2048,"a":2111},"A common formula is z × standard deviation of daily demand × √lead time, where z sets the service level — about 1.65 for 95%. It grows with how variable demand is and how long the lead time is.","build an inventory reorder report",null,{"breadcrumb":2115},[2116,2118,2121],{"name":2117,"item":1106},"Home",{"name":2119,"item":2120},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":2122,"item":16},"Building Finance and Operations Reports in Excel with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report",{"title":2125,"description":2126},"Inventory Reorder Point Report in Excel with Python","Build an inventory reorder report with pandas: daily usage from sales history, reorder point = usage × lead time + safety stock, inventory position with open purchase orders, order-up-to quantities, pack sizes and MOQs, days of cover and per-supplier purchase lists.","build-an-inventory-reorder-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-inventory-reorder-report\u002Findex",[2130,2131,2132],"report-layouts","pivots-and-summaries","pandas","how-to","1RPhgdiHNvvo5JCYfsvzyUMR-K9MSvRJhAYZJIcfxPg",[2136,2140],{"title":2137,"path":2138,"stem":2139,"children":-1},"Build an Intercompany Reconciliation","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-intercompany-reconciliation","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-intercompany-reconciliation\u002Findex",{"title":2141,"path":2142,"stem":2143,"children":-1},"Build an Inventory Turnover and Days on Hand Report","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-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",[2145,2147],{"path":2146,"title":2122},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports",{"path":2148,"title":2119},"\u002Fautomating-reporting-workflows",[],[2151,2155,2158,2161,2164,2167],{"path":2152,"title":2153,"kind":2154},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards","Building Multi-Sheet Excel Dashboards","guide",{"path":2156,"title":2157,"kind":2133},"\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":2159,"title":2160,"kind":2133},"\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":2162,"title":2163,"kind":2133},"\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":2165,"title":2166,"kind":2133},"\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":2168,"title":2169,"kind":2133},"\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",1790972127750]