[{"data":1,"prerenderedAt":2516},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report":2481,"ancestors:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report":2490,"children:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report":2495,"related:\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report":2496},{"id":4,"title":5,"body":6,"dateModified":2446,"datePublished":2446,"description":2447,"extension":2448,"faq":2449,"keyword":2458,"kind":2459,"meta":2460,"navigation":101,"path":2470,"seo":2471,"slug":2474,"stem":2475,"topics":2476,"type":2479,"__hash__":2480},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report\u002Findex.md","Build a Purchase Order Tracking Report",{"type":7,"value":8,"toc":2434},"minimark",[9,19,24,53,69,73,536,543,547,901,912,1116,1120,1442,1449,1742,1892,1896,2311,2318,2322,2325,2329,2364,2368,2371,2375,2381,2387,2393,2399,2403,2430],[10,11,12,13,18],"p",{},"Once a purchase order is sent, three things have to happen for it to close cleanly: the goods arrive in the right quantity, the supplier invoices for what arrived, and the invoice is paid. Different systems — or different spreadsheets — usually record each step, so the question \"where are we with our orders?\" means joining three exports and working out, line by line, what is still outstanding. Buyers want to know which deliveries are late. Accounts payable wants invoices that have no matching receipt, which should not be paid yet, and receipts with no invoice, which must be accrued at month end. Finance wants the value of open commitments. This page builds one report that answers all of those from PO, receipt and invoice exports with pandas, line by line, and writes a status workbook with a sheet per audience. It belongs to ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002F","Building Finance and Operations Reports",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas openpyxl\n","bash","",[32,33,34],"code",{"__ignoreMap":30},[35,36,39,43,47,50],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," pandas",[35,51,52],{"class":45}," openpyxl\n",[10,54,55,56,60,61,64,65,68],{},"The workbook holds three exports at 30 September 2026: ",[57,58,59],"em",{},"PO lines"," (order, line, supplier, item, order and promised dates, quantity, unit price), ",[57,62,63],{},"Receipts"," (goods received against each line, possibly in several deliveries) and ",[57,66,67],{},"Invoices"," (supplier invoices matched to PO lines). Each export comes from a different place, which is normal.",[20,70,72],{"id":71},"step-1-one-row-per-po-line-with-received-and-invoiced-quantities","Step 1 — One row per PO line with received and invoiced quantities",[25,74,78],{"className":75,"code":76,"language":77,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\nTODAY = pd.Timestamp(\"2026-09-30\")\npo = pd.read_excel(\"purchasing.xlsx\", sheet_name=\"PO lines\")\nrec = pd.read_excel(\"purchasing.xlsx\", sheet_name=\"Receipts\")\ninv = pd.read_excel(\"purchasing.xlsx\", sheet_name=\"Invoices\")\n\nkey = [\"PO\", \"Line\"]\nreceived = rec.groupby(key).agg(**{\"Qty received\": (\"Qty received\", \"sum\"), \"Last receipt\": (\"Received\", \"max\")})\ninvoiced = inv.groupby(key).agg(**{\"Qty invoiced\": (\"Qty invoiced\", \"sum\"), \"Invoiced amount\": (\"Amount\", \"sum\")})\nlines = po.join(received, on=key).join(invoiced, on=key).fillna({\"Qty received\": 0, \"Qty invoiced\": 0, \"Invoiced amount\": 0})\nlines[\"To receive\"] = (lines[\"Qty ordered\"] - lines[\"Qty received\"]).clip(lower=0)\nlines[\"Received not invoiced\"] = (lines[\"Qty received\"] - lines[\"Qty invoiced\"]).clip(lower=0)\nlines[\"Invoiced not received\"] = (lines[\"Qty invoiced\"] - lines[\"Qty received\"]).clip(lower=0)\nprint(lines[[\"PO\", \"Line\", \"Item\", \"Qty ordered\", \"Qty received\", \"Qty invoiced\", \"To receive\", \"Received not invoiced\", \"Invoiced not received\"]].to_string(index=False))\n","python",[32,79,80,96,103,122,151,174,197,202,224,273,315,368,410,444,478],{"__ignoreMap":30},[35,81,82,86,90,93],{"class":37,"line":38},[35,83,85],{"class":84},"s-kum","import",[35,87,89],{"class":88},"skGVy"," pandas ",[35,91,92],{"class":84},"as",[35,94,95],{"class":88}," pd\n",[35,97,99],{"class":37,"line":98},2,[35,100,102],{"emptyLinePlaceholder":101},true,"\n",[35,104,106,110,113,116,119],{"class":37,"line":105},3,[35,107,109],{"class":108},"sP0c6","TODAY",[35,111,112],{"class":84}," =",[35,114,115],{"class":88}," pd.Timestamp(",[35,117,118],{"class":45},"\"2026-09-30\"",[35,120,121],{"class":88},")\n",[35,123,125,128,131,134,137,140,144,146,149],{"class":37,"line":124},4,[35,126,127],{"class":88},"po ",[35,129,130],{"class":84},"=",[35,132,133],{"class":88}," pd.read_excel(",[35,135,136],{"class":45},"\"purchasing.xlsx\"",[35,138,139],{"class":88},", ",[35,141,143],{"class":142},"sa561","sheet_name",[35,145,130],{"class":84},[35,147,148],{"class":45},"\"PO lines\"",[35,150,121],{"class":88},[35,152,154,157,159,161,163,165,167,169,172],{"class":37,"line":153},5,[35,155,156],{"class":88},"rec ",[35,158,130],{"class":84},[35,160,133],{"class":88},[35,162,136],{"class":45},[35,164,139],{"class":88},[35,166,143],{"class":142},[35,168,130],{"class":84},[35,170,171],{"class":45},"\"Receipts\"",[35,173,121],{"class":88},[35,175,177,180,182,184,186,188,190,192,195],{"class":37,"line":176},6,[35,178,179],{"class":88},"inv ",[35,181,130],{"class":84},[35,183,133],{"class":88},[35,185,136],{"class":45},[35,187,139],{"class":88},[35,189,143],{"class":142},[35,191,130],{"class":84},[35,193,194],{"class":45},"\"Invoices\"",[35,196,121],{"class":88},[35,198,200],{"class":37,"line":199},7,[35,201,102],{"emptyLinePlaceholder":101},[35,203,205,208,210,213,216,218,221],{"class":37,"line":204},8,[35,206,207],{"class":88},"key ",[35,209,130],{"class":84},[35,211,212],{"class":88}," [",[35,214,215],{"class":45},"\"PO\"",[35,217,139],{"class":88},[35,219,220],{"class":45},"\"Line\"",[35,222,223],{"class":88},"]\n",[35,225,227,230,232,235,238,241,244,247,249,251,254,257,260,262,265,267,270],{"class":37,"line":226},9,[35,228,229],{"class":88},"received ",[35,231,130],{"class":84},[35,233,234],{"class":88}," rec.groupby(key).agg(",[35,236,237],{"class":84},"**",[35,239,240],{"class":88},"{",[35,242,243],{"class":45},"\"Qty received\"",[35,245,246],{"class":88},": (",[35,248,243],{"class":45},[35,250,139],{"class":88},[35,252,253],{"class":45},"\"sum\"",[35,255,256],{"class":88},"), ",[35,258,259],{"class":45},"\"Last receipt\"",[35,261,246],{"class":88},[35,263,264],{"class":45},"\"Received\"",[35,266,139],{"class":88},[35,268,269],{"class":45},"\"max\"",[35,271,272],{"class":88},")})\n",[35,274,276,279,281,284,286,288,291,293,295,297,299,301,304,306,309,311,313],{"class":37,"line":275},10,[35,277,278],{"class":88},"invoiced ",[35,280,130],{"class":84},[35,282,283],{"class":88}," inv.groupby(key).agg(",[35,285,237],{"class":84},[35,287,240],{"class":88},[35,289,290],{"class":45},"\"Qty invoiced\"",[35,292,246],{"class":88},[35,294,290],{"class":45},[35,296,139],{"class":88},[35,298,253],{"class":45},[35,300,256],{"class":88},[35,302,303],{"class":45},"\"Invoiced amount\"",[35,305,246],{"class":88},[35,307,308],{"class":45},"\"Amount\"",[35,310,139],{"class":88},[35,312,253],{"class":45},[35,314,272],{"class":88},[35,316,318,321,323,326,329,331,334,336,338,341,343,346,349,351,353,355,357,359,361,363,365],{"class":37,"line":317},11,[35,319,320],{"class":88},"lines ",[35,322,130],{"class":84},[35,324,325],{"class":88}," po.join(received, ",[35,327,328],{"class":142},"on",[35,330,130],{"class":84},[35,332,333],{"class":88},"key).join(invoiced, ",[35,335,328],{"class":142},[35,337,130],{"class":84},[35,339,340],{"class":88},"key).fillna({",[35,342,243],{"class":45},[35,344,345],{"class":88},": ",[35,347,348],{"class":108},"0",[35,350,139],{"class":88},[35,352,290],{"class":45},[35,354,345],{"class":88},[35,356,348],{"class":108},[35,358,139],{"class":88},[35,360,303],{"class":45},[35,362,345],{"class":88},[35,364,348],{"class":108},[35,366,367],{"class":88},"})\n",[35,369,371,374,377,380,382,385,388,390,393,396,398,401,404,406,408],{"class":37,"line":370},12,[35,372,373],{"class":88},"lines[",[35,375,376],{"class":45},"\"To receive\"",[35,378,379],{"class":88},"] ",[35,381,130],{"class":84},[35,383,384],{"class":88}," (lines[",[35,386,387],{"class":45},"\"Qty ordered\"",[35,389,379],{"class":88},[35,391,392],{"class":84},"-",[35,394,395],{"class":88}," lines[",[35,397,243],{"class":45},[35,399,400],{"class":88},"]).clip(",[35,402,403],{"class":142},"lower",[35,405,130],{"class":84},[35,407,348],{"class":108},[35,409,121],{"class":88},[35,411,413,415,418,420,422,424,426,428,430,432,434,436,438,440,442],{"class":37,"line":412},13,[35,414,373],{"class":88},[35,416,417],{"class":45},"\"Received not invoiced\"",[35,419,379],{"class":88},[35,421,130],{"class":84},[35,423,384],{"class":88},[35,425,243],{"class":45},[35,427,379],{"class":88},[35,429,392],{"class":84},[35,431,395],{"class":88},[35,433,290],{"class":45},[35,435,400],{"class":88},[35,437,403],{"class":142},[35,439,130],{"class":84},[35,441,348],{"class":108},[35,443,121],{"class":88},[35,445,447,449,452,454,456,458,460,462,464,466,468,470,472,474,476],{"class":37,"line":446},14,[35,448,373],{"class":88},[35,450,451],{"class":45},"\"Invoiced not received\"",[35,453,379],{"class":88},[35,455,130],{"class":84},[35,457,384],{"class":88},[35,459,290],{"class":45},[35,461,379],{"class":88},[35,463,392],{"class":84},[35,465,395],{"class":88},[35,467,243],{"class":45},[35,469,400],{"class":88},[35,471,403],{"class":142},[35,473,130],{"class":84},[35,475,348],{"class":108},[35,477,121],{"class":88},[35,479,481,484,487,489,491,493,495,498,500,502,504,506,508,510,512,514,516,518,520,522,525,528,530,533],{"class":37,"line":480},15,[35,482,483],{"class":108},"print",[35,485,486],{"class":88},"(lines[[",[35,488,215],{"class":45},[35,490,139],{"class":88},[35,492,220],{"class":45},[35,494,139],{"class":88},[35,496,497],{"class":45},"\"Item\"",[35,499,139],{"class":88},[35,501,387],{"class":45},[35,503,139],{"class":88},[35,505,243],{"class":45},[35,507,139],{"class":88},[35,509,290],{"class":45},[35,511,139],{"class":88},[35,513,376],{"class":45},[35,515,139],{"class":88},[35,517,417],{"class":45},[35,519,139],{"class":88},[35,521,451],{"class":45},[35,523,524],{"class":88},"]].to_string(",[35,526,527],{"class":142},"index",[35,529,130],{"class":84},[35,531,532],{"class":108},"False",[35,534,535],{"class":88},"))\n",[10,537,538,539,542],{},"Receipts and invoices are aggregated to one row per PO line ",[57,540,541],{},"before"," joining, because a line can be delivered in several loads — PO-806 arrived in two halves — and invoiced on several invoices; joining the raw tables would duplicate the order line once per delivery and overstate everything. With quantities side by side, three differences tell the whole story: what is still to arrive, what has arrived but not been invoiced, and what has been invoiced but not received. Lines with no receipts or invoices get zero rather than a missing value, because zero is the truth: nothing has happened yet.",[20,544,546],{"id":545},"step-2-a-status-per-line","Step 2 — A status per line",[25,548,550],{"className":75,"code":549,"language":77,"meta":30,"style":30},"def status(r):\n    if r[\"Invoiced not received\"] > 0:\n        return \"Invoice without receipt\"\n    if r[\"To receive\"] == 0 and r[\"Received not invoiced\"] == 0:\n        return \"Complete\"\n    if r[\"To receive\"] == 0:\n        return \"Received, awaiting invoice\"\n    late = r[\"Promised date\"] \u003C TODAY\n    if r[\"Qty received\"] > 0:\n        return \"Part received\" + (\" — late\" if late else \"\")\n    return \"Late\" if late else \"Open\"\n\nlines[\"Status\"] = lines.apply(status, axis=1)\nlines[\"Days late\"] = ((TODAY - lines[\"Promised date\"]).dt.days).where(lines[\"Status\"].str.contains(\"late|Late\"), 0)\nlines[\"Open value\"] = lines[\"To receive\"] * lines[\"Unit price\"]\nlines[\"Accrual\"] = lines[\"Received not invoiced\"] * lines[\"Unit price\"]\nprint(lines[[\"PO\", \"Line\", \"Supplier\", \"Status\", \"Days late\", \"Open value\", \"Accrual\"]].to_string(index=False))\n",[32,551,552,564,585,593,623,630,646,653,673,689,719,736,740,764,804,831,857],{"__ignoreMap":30},[35,553,554,557,561],{"class":37,"line":38},[35,555,556],{"class":84},"def",[35,558,560],{"class":559},"s_Opv"," status",[35,562,563],{"class":88},"(r):\n",[35,565,566,569,572,574,576,579,582],{"class":37,"line":98},[35,567,568],{"class":84},"    if",[35,570,571],{"class":88}," r[",[35,573,451],{"class":45},[35,575,379],{"class":88},[35,577,578],{"class":84},">",[35,580,581],{"class":108}," 0",[35,583,584],{"class":88},":\n",[35,586,587,590],{"class":37,"line":105},[35,588,589],{"class":84},"        return",[35,591,592],{"class":45}," \"Invoice without receipt\"\n",[35,594,595,597,599,601,603,606,608,611,613,615,617,619,621],{"class":37,"line":124},[35,596,568],{"class":84},[35,598,571],{"class":88},[35,600,376],{"class":45},[35,602,379],{"class":88},[35,604,605],{"class":84},"==",[35,607,581],{"class":108},[35,609,610],{"class":84}," and",[35,612,571],{"class":88},[35,614,417],{"class":45},[35,616,379],{"class":88},[35,618,605],{"class":84},[35,620,581],{"class":108},[35,622,584],{"class":88},[35,624,625,627],{"class":37,"line":153},[35,626,589],{"class":84},[35,628,629],{"class":45}," \"Complete\"\n",[35,631,632,634,636,638,640,642,644],{"class":37,"line":176},[35,633,568],{"class":84},[35,635,571],{"class":88},[35,637,376],{"class":45},[35,639,379],{"class":88},[35,641,605],{"class":84},[35,643,581],{"class":108},[35,645,584],{"class":88},[35,647,648,650],{"class":37,"line":199},[35,649,589],{"class":84},[35,651,652],{"class":45}," \"Received, awaiting invoice\"\n",[35,654,655,658,660,662,665,667,670],{"class":37,"line":204},[35,656,657],{"class":88},"    late ",[35,659,130],{"class":84},[35,661,571],{"class":88},[35,663,664],{"class":45},"\"Promised date\"",[35,666,379],{"class":88},[35,668,669],{"class":84},"\u003C",[35,671,672],{"class":108}," TODAY\n",[35,674,675,677,679,681,683,685,687],{"class":37,"line":226},[35,676,568],{"class":84},[35,678,571],{"class":88},[35,680,243],{"class":45},[35,682,379],{"class":88},[35,684,578],{"class":84},[35,686,581],{"class":108},[35,688,584],{"class":88},[35,690,691,693,696,699,702,705,708,711,714,717],{"class":37,"line":275},[35,692,589],{"class":84},[35,694,695],{"class":45}," \"Part received\"",[35,697,698],{"class":84}," +",[35,700,701],{"class":88}," (",[35,703,704],{"class":45},"\" — late\"",[35,706,707],{"class":84}," if",[35,709,710],{"class":88}," late ",[35,712,713],{"class":84},"else",[35,715,716],{"class":45}," \"\"",[35,718,121],{"class":88},[35,720,721,724,727,729,731,733],{"class":37,"line":317},[35,722,723],{"class":84},"    return",[35,725,726],{"class":45}," \"Late\"",[35,728,707],{"class":84},[35,730,710],{"class":88},[35,732,713],{"class":84},[35,734,735],{"class":45}," \"Open\"\n",[35,737,738],{"class":37,"line":370},[35,739,102],{"emptyLinePlaceholder":101},[35,741,742,744,747,749,751,754,757,759,762],{"class":37,"line":412},[35,743,373],{"class":88},[35,745,746],{"class":45},"\"Status\"",[35,748,379],{"class":88},[35,750,130],{"class":84},[35,752,753],{"class":88}," lines.apply(status, ",[35,755,756],{"class":142},"axis",[35,758,130],{"class":84},[35,760,761],{"class":108},"1",[35,763,121],{"class":88},[35,765,766,768,771,773,775,778,780,783,785,787,790,792,795,798,800,802],{"class":37,"line":446},[35,767,373],{"class":88},[35,769,770],{"class":45},"\"Days late\"",[35,772,379],{"class":88},[35,774,130],{"class":84},[35,776,777],{"class":88}," ((",[35,779,109],{"class":108},[35,781,782],{"class":84}," -",[35,784,395],{"class":88},[35,786,664],{"class":45},[35,788,789],{"class":88},"]).dt.days).where(lines[",[35,791,746],{"class":45},[35,793,794],{"class":88},"].str.contains(",[35,796,797],{"class":45},"\"late|Late\"",[35,799,256],{"class":88},[35,801,348],{"class":108},[35,803,121],{"class":88},[35,805,806,808,811,813,815,817,819,821,824,826,829],{"class":37,"line":480},[35,807,373],{"class":88},[35,809,810],{"class":45},"\"Open value\"",[35,812,379],{"class":88},[35,814,130],{"class":84},[35,816,395],{"class":88},[35,818,376],{"class":45},[35,820,379],{"class":88},[35,822,823],{"class":84},"*",[35,825,395],{"class":88},[35,827,828],{"class":45},"\"Unit price\"",[35,830,223],{"class":88},[35,832,834,836,839,841,843,845,847,849,851,853,855],{"class":37,"line":833},16,[35,835,373],{"class":88},[35,837,838],{"class":45},"\"Accrual\"",[35,840,379],{"class":88},[35,842,130],{"class":84},[35,844,395],{"class":88},[35,846,417],{"class":45},[35,848,379],{"class":88},[35,850,823],{"class":84},[35,852,395],{"class":88},[35,854,828],{"class":45},[35,856,223],{"class":88},[35,858,860,862,864,866,868,870,872,875,877,879,881,883,885,887,889,891,893,895,897,899],{"class":37,"line":859},17,[35,861,483],{"class":108},[35,863,486],{"class":88},[35,865,215],{"class":45},[35,867,139],{"class":88},[35,869,220],{"class":45},[35,871,139],{"class":88},[35,873,874],{"class":45},"\"Supplier\"",[35,876,139],{"class":88},[35,878,746],{"class":45},[35,880,139],{"class":88},[35,882,770],{"class":45},[35,884,139],{"class":88},[35,886,810],{"class":45},[35,888,139],{"class":88},[35,890,838],{"class":45},[35,892,524],{"class":88},[35,894,527],{"class":142},[35,896,130],{"class":84},[35,898,532],{"class":108},[35,900,535],{"class":88},[10,902,903,904,907,908,911],{},"The status is decided in order of importance. An invoice for goods that have not arrived comes first, because it must not be paid until they do — this is the three-way match failing. A line fully received and fully invoiced is complete. Fully received but not yet invoiced is waiting for the supplier's invoice. Otherwise something is still to arrive, and the promised date decides whether it is late. ",[57,905,906],{},"Open value"," is the cost of what is still to come — the outstanding commitment — and ",[57,909,910],{},"Accrual"," is the cost of goods received but not yet invoiced, which finance books as a liability at month end so the period carries the cost of what it consumed.",[913,914,922,923,922,927,922,931,922,938,922,948,922,959,922,965,922,969,922,973,922,976,922,981,922,990,922,995,922,1001,922,1007,922,1011,922,1015,922,1020,922,1023,922,1026,922,1030,922,1033,922,1038,922,1045,922,1050,922,1054,922,1057,922,1060,922,1064,922,1067,922,1071,922,1078,922,1082,922,1085,922,1087,922,1090,922,1094,922,1097,922,1102,922,1109,922,1113],"svg",{"viewBox":915,"role":916,"ariaLabelledBy":917,"xmlns":920,"style":921},"4 0 752 290","img",[918,919],"po-status-t","po-status-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  ",[924,925,926],"title",{"id":918},"How each PO line's status is decided",[928,929,930],"desc",{"id":919},"A line invoiced for more than was received is an invoice without receipt and must not be paid. A line fully received and invoiced is complete; fully received but not invoiced awaits the invoice and is accrued. Otherwise goods are still due, and the line is late if its promised date has passed.",[932,933],"rect",{"x":934,"y":348,"width":935,"height":936,"fill":937},"4","752","290","#ffffff",[932,939],{"x":940,"y":941,"width":942,"height":943,"rx":944,"fill":945,"stroke":946,"style":947},"208.6","16.0","342.8","75.5","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[949,950,952],"g",{"transform":951},"translate(368.0,29.0) scale(1.000)",[953,954],"path",{"d":955,"fill":956,"stroke":957,"style":958},"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",[960,961,964],"text",{"x":962,"y":943,"style":963},"380.0","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Compare ordered, received and invoiced",[37,966],{"x1":962,"y1":967,"x2":962,"y2":968,"stroke":946,"style":947},"91.5","111.5",[37,970],{"x1":971,"y1":968,"x2":972,"y2":968,"stroke":946,"style":947},"104.0","656.0",[37,974],{"x1":971,"y1":968,"x2":971,"y2":975,"stroke":946,"style":947},"157.5",[977,978],"polygon",{"points":979,"fill":980},"104.0,161.5 99.0,152.5 109.0,152.5","#5b5cf0",[932,982],{"x":983,"y":984,"width":985,"height":986,"rx":987,"fill":937,"stroke":988,"style":989},"36.9","121.5","134.3","21.0","10","var(--line,#cdd5e6)","stroke-width:1.2px",[960,991,994],{"x":971,"y":992,"style":993},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","invoiced > received",[932,996],{"x":997,"y":998,"width":999,"height":1000,"rx":944,"fill":945,"stroke":946,"style":947},"20.0","163.5","168.0","110.5",[949,1002,1004],{"transform":1003},"translate(92.0,176.5) scale(1.000)",[953,1005],{"d":1006,"fill":956,"stroke":957,"style":958},"M6 11h12v9H6z M8.5 11V8a3.5 3.5 0 0 1 7 0v3",[960,1008,1010],{"x":971,"y":1009,"style":963},"223.0","invoice without",[960,1012,1014],{"x":971,"y":1013,"style":963},"240.5","receipt",[960,1016,1019],{"x":971,"y":1017,"style":1018},"260.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","hold payment",[37,1021],{"x1":1022,"y1":968,"x2":1022,"y2":975,"stroke":946,"style":947},"288.0",[977,1024],{"points":1025,"fill":980},"288.0,161.5 283.0,152.5 293.0,152.5",[932,1027],{"x":1028,"y":984,"width":1029,"height":986,"rx":987,"fill":937,"stroke":988,"style":989},"212.7","150.7",[960,1031,1032],{"x":1022,"y":992,"style":993},"all received + invoiced",[932,1034],{"x":1035,"y":998,"width":999,"height":1000,"rx":944,"fill":1036,"stroke":1037,"style":947},"204.0","#d9f4f1","var(--teal,#0f9488)",[949,1039,1041],{"transform":1040},"translate(276.0,185.2) scale(1.000)",[953,1042],{"d":1043,"fill":956,"stroke":1044,"style":958},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[960,1046,1049],{"x":1022,"y":1047,"style":1048},"231.8","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","complete",[960,1051,1053],{"x":1022,"y":1052,"style":1018},"251.2","close the line",[37,1055],{"x1":1056,"y1":968,"x2":1056,"y2":975,"stroke":946,"style":947},"472.0",[977,1058],{"points":1059,"fill":980},"472.0,161.5 467.0,152.5 477.0,152.5",[932,1061],{"x":1062,"y":984,"width":1063,"height":986,"rx":987,"fill":937,"stroke":988,"style":989},"397.3","149.4",[960,1065,1066],{"x":1056,"y":992,"style":993},"received, not invoiced",[932,1068],{"x":1069,"y":998,"width":999,"height":1000,"rx":944,"fill":1070,"stroke":988,"style":947},"388.0","#e7ebef",[949,1072,1074],{"transform":1073},"translate(460.0,185.2) scale(1.000)",[953,1075],{"d":1076,"fill":956,"stroke":1077,"style":958},"M12 3a9 9 0 1 0 .01 0z M12 7v5l3.5 2","var(--text,#172033)",[960,1079,1081],{"x":1056,"y":1047,"style":1080},"font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","awaiting invoice",[960,1083,1084],{"x":1056,"y":1052,"style":1018},"accrue at month end",[37,1086],{"x1":972,"y1":968,"x2":972,"y2":975,"stroke":946,"style":947},[977,1088],{"points":1089,"fill":980},"656.0,161.5 651.0,152.5 661.0,152.5",[932,1091],{"x":1092,"y":984,"width":1093,"height":986,"rx":987,"fill":937,"stroke":988,"style":989},"605.7","100.7",[960,1095,1096],{"x":972,"y":992,"style":993},"still to receive",[932,1098],{"x":1099,"y":998,"width":999,"height":1000,"rx":944,"fill":1100,"stroke":1101,"style":947},"572.0","#fdefd8","var(--gold,#b4740a)",[949,1103,1105],{"transform":1104},"translate(644.0,185.2) scale(1.000)",[953,1106],{"d":1107,"fill":956,"stroke":1108,"style":958},"M4 5h16v15H4z M4 10h16 M8 3v4 M16 3v4","var(--gold-ink,#7a4e06)",[960,1110,1112],{"x":972,"y":1047,"style":1111},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","open \u002F late",[960,1114,1115],{"x":972,"y":1052,"style":1018},"by promised date",[20,1117,1119],{"id":1118},"step-3-a-view-for-each-audience","Step 3 — A view for each audience",[25,1121,1123],{"className":75,"code":1122,"language":77,"meta":30,"style":30},"buyers = lines[lines[\"To receive\"] > 0].sort_values([\"Days late\", \"Open value\"], ascending=False)[\n    [\"Supplier\", \"PO\", \"Line\", \"Item\", \"Promised date\", \"Days late\", \"To receive\", \"Open value\", \"Status\"]]\nap = lines[lines[\"Status\"].isin([\"Invoice without receipt\", \"Received, awaiting invoice\"])][\n    [\"Supplier\", \"PO\", \"Line\", \"Item\", \"Qty received\", \"Qty invoiced\", \"Accrual\", \"Status\"]]\nby_supplier = lines.groupby(\"Supplier\").agg(Lines=(\"Line\", \"count\"), Open_value=(\"Open value\", \"sum\"),\n                                            Late_lines=(\"Days late\", lambda d: int((d > 0).sum())), Accrual=(\"Accrual\", \"sum\"))\nprint(buyers.to_string(index=False))\nprint(ap.to_string(index=False))\nprint(f\"open commitments {lines['Open value'].sum():,.2f} | received not invoiced (accrual) {lines['Accrual'].sum():,.2f}\")\n",[32,1124,1125,1165,1207,1232,1268,1316,1362,1377,1392],{"__ignoreMap":30},[35,1126,1127,1130,1132,1135,1137,1139,1141,1143,1146,1148,1150,1152,1155,1158,1160,1162],{"class":37,"line":38},[35,1128,1129],{"class":88},"buyers ",[35,1131,130],{"class":84},[35,1133,1134],{"class":88}," lines[lines[",[35,1136,376],{"class":45},[35,1138,379],{"class":88},[35,1140,578],{"class":84},[35,1142,581],{"class":108},[35,1144,1145],{"class":88},"].sort_values([",[35,1147,770],{"class":45},[35,1149,139],{"class":88},[35,1151,810],{"class":45},[35,1153,1154],{"class":88},"], ",[35,1156,1157],{"class":142},"ascending",[35,1159,130],{"class":84},[35,1161,532],{"class":108},[35,1163,1164],{"class":88},")[\n",[35,1166,1167,1170,1172,1174,1176,1178,1180,1182,1184,1186,1188,1190,1192,1194,1196,1198,1200,1202,1204],{"class":37,"line":98},[35,1168,1169],{"class":88},"    [",[35,1171,874],{"class":45},[35,1173,139],{"class":88},[35,1175,215],{"class":45},[35,1177,139],{"class":88},[35,1179,220],{"class":45},[35,1181,139],{"class":88},[35,1183,497],{"class":45},[35,1185,139],{"class":88},[35,1187,664],{"class":45},[35,1189,139],{"class":88},[35,1191,770],{"class":45},[35,1193,139],{"class":88},[35,1195,376],{"class":45},[35,1197,139],{"class":88},[35,1199,810],{"class":45},[35,1201,139],{"class":88},[35,1203,746],{"class":45},[35,1205,1206],{"class":88},"]]\n",[35,1208,1209,1212,1214,1216,1218,1221,1224,1226,1229],{"class":37,"line":105},[35,1210,1211],{"class":88},"ap ",[35,1213,130],{"class":84},[35,1215,1134],{"class":88},[35,1217,746],{"class":45},[35,1219,1220],{"class":88},"].isin([",[35,1222,1223],{"class":45},"\"Invoice without receipt\"",[35,1225,139],{"class":88},[35,1227,1228],{"class":45},"\"Received, awaiting invoice\"",[35,1230,1231],{"class":88},"])][\n",[35,1233,1234,1236,1238,1240,1242,1244,1246,1248,1250,1252,1254,1256,1258,1260,1262,1264,1266],{"class":37,"line":124},[35,1235,1169],{"class":88},[35,1237,874],{"class":45},[35,1239,139],{"class":88},[35,1241,215],{"class":45},[35,1243,139],{"class":88},[35,1245,220],{"class":45},[35,1247,139],{"class":88},[35,1249,497],{"class":45},[35,1251,139],{"class":88},[35,1253,243],{"class":45},[35,1255,139],{"class":88},[35,1257,290],{"class":45},[35,1259,139],{"class":88},[35,1261,838],{"class":45},[35,1263,139],{"class":88},[35,1265,746],{"class":45},[35,1267,1206],{"class":88},[35,1269,1270,1273,1275,1278,1280,1283,1286,1288,1291,1293,1295,1298,1300,1303,1305,1307,1309,1311,1313],{"class":37,"line":153},[35,1271,1272],{"class":88},"by_supplier ",[35,1274,130],{"class":84},[35,1276,1277],{"class":88}," lines.groupby(",[35,1279,874],{"class":45},[35,1281,1282],{"class":88},").agg(",[35,1284,1285],{"class":142},"Lines",[35,1287,130],{"class":84},[35,1289,1290],{"class":88},"(",[35,1292,220],{"class":45},[35,1294,139],{"class":88},[35,1296,1297],{"class":45},"\"count\"",[35,1299,256],{"class":88},[35,1301,1302],{"class":142},"Open_value",[35,1304,130],{"class":84},[35,1306,1290],{"class":88},[35,1308,810],{"class":45},[35,1310,139],{"class":88},[35,1312,253],{"class":45},[35,1314,1315],{"class":88},"),\n",[35,1317,1318,1321,1323,1325,1327,1329,1332,1335,1338,1341,1343,1345,1348,1350,1352,1354,1356,1358,1360],{"class":37,"line":176},[35,1319,1320],{"class":142},"                                            Late_lines",[35,1322,130],{"class":84},[35,1324,1290],{"class":88},[35,1326,770],{"class":45},[35,1328,139],{"class":88},[35,1330,1331],{"class":84},"lambda",[35,1333,1334],{"class":88}," d: ",[35,1336,1337],{"class":108},"int",[35,1339,1340],{"class":88},"((d ",[35,1342,578],{"class":84},[35,1344,581],{"class":108},[35,1346,1347],{"class":88},").sum())), ",[35,1349,910],{"class":142},[35,1351,130],{"class":84},[35,1353,1290],{"class":88},[35,1355,838],{"class":45},[35,1357,139],{"class":88},[35,1359,253],{"class":45},[35,1361,535],{"class":88},[35,1363,1364,1366,1369,1371,1373,1375],{"class":37,"line":199},[35,1365,483],{"class":108},[35,1367,1368],{"class":88},"(buyers.to_string(",[35,1370,527],{"class":142},[35,1372,130],{"class":84},[35,1374,532],{"class":108},[35,1376,535],{"class":88},[35,1378,1379,1381,1384,1386,1388,1390],{"class":37,"line":204},[35,1380,483],{"class":108},[35,1382,1383],{"class":88},"(ap.to_string(",[35,1385,527],{"class":142},[35,1387,130],{"class":84},[35,1389,532],{"class":108},[35,1391,535],{"class":88},[35,1393,1394,1396,1398,1401,1404,1407,1409,1412,1415,1418,1421,1424,1426,1428,1431,1433,1435,1437,1440],{"class":37,"line":226},[35,1395,483],{"class":108},[35,1397,1290],{"class":88},[35,1399,1400],{"class":84},"f",[35,1402,1403],{"class":45},"\"open commitments ",[35,1405,240],{"class":1406},"sSjpA",[35,1408,373],{"class":88},[35,1410,1411],{"class":45},"'Open value'",[35,1413,1414],{"class":88},"].sum()",[35,1416,1417],{"class":84},":,.2f",[35,1419,1420],{"class":1406},"}",[35,1422,1423],{"class":45}," | received not invoiced (accrual) ",[35,1425,240],{"class":1406},[35,1427,373],{"class":88},[35,1429,1430],{"class":45},"'Accrual'",[35,1432,1414],{"class":88},[35,1434,1417],{"class":84},[35,1436,1420],{"class":1406},[35,1438,1439],{"class":45},"\"",[35,1441,121],{"class":88},[10,1443,1444,1445,18],{},"Buyers get the lines still to arrive, most overdue first, with what is missing and how late it is: Acme's nuts arrived two thousand short and are fifteen days late, Granite's drill sets were due yesterday and nothing has arrived, and the laptops from Fjord are not yet due. Accounts payable gets the two kinds of exception: Granite's invoice for drill sets that have not arrived, which must be held, and the goods received but not yet invoiced — Corex's boxes and the second half of their tape, and Delta's paper — which make up the month-end accrual of 2,485. Open commitments, the cost of everything still to arrive, come to 8,400. The drill-set line shows zero days late because its invoice exception takes precedence in the status; it still appears on the buyers' sheet, because the goods are still to arrive. The supplier view rolls everything up for supplier reviews. Supplier delivery performance over time is covered in ",[14,1446,1448],{"href":1447},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-on-time-delivery-report\u002F","Build an On-Time Delivery Report",[913,1450,922,1456,922,1459,922,1462,922,1467,922,1473,922,1477,922,1483,922,1487,922,1491,922,1495,922,1499,922,1503,922,1507,922,1511,922,1515,922,1519,922,1523,922,1527,922,1531,922,1534,922,1538,922,1540,922,1543,922,1545,922,1548,922,1550,922,1553,922,1555,922,1558,922,1560,922,1563,922,1566,922,1569,922,1571,922,1575,922,1577,922,1580,922,1582,922,1585,922,1587,922,1589,922,1591,922,1593,922,1595,922,1599,922,1602,922,1606,922,1608,922,1611,922,1613,922,1616,922,1618,922,1620,922,1622,922,1625,922,1627,922,1629,922,1631,922,1635,922,1638,922,1641,922,1643,922,1646,922,1648,922,1651,922,1653,922,1656,922,1658,922,1660,922,1662,922,1664,922,1668,922,1672,922,1675,922,1679,922,1681,922,1684,922,1686,922,1689,922,1691,922,1694,922,1696,922,1698,922,1700,922,1702,922,1704,922,1708,922,1711,922,1715,922,1717,922,1720,922,1722,922,1725,922,1727,922,1729,922,1731,922,1733,922,1735,922,1737,922,1739],{"viewBox":1451,"role":916,"ariaLabelledBy":1452,"xmlns":920,"style":1455},"54 0 651 224",[1453,1454],"po-lines-t","po-lines-d","width:100%;max-width:651px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[924,1457,1458],{"id":1453},"The PO lines with their status",[928,1460,1461],{"id":1454},"Bolts are complete. Nuts are part received and late. Boxes are received and awaiting invoice. Drill sets are invoiced without a receipt. Paper is received and awaiting invoice. Laptops are open and not yet due. Tape is received, with half not yet invoiced.",[932,1463],{"x":1464,"y":348,"width":1465,"height":1466,"fill":937},"54","651","224",[932,1468],{"x":1469,"y":941,"width":1470,"height":1471,"rx":348,"fill":1070,"stroke":988,"style":1472},"71.0","32.0","24.0","stroke-width:1px",[932,1474],{"x":1475,"y":941,"width":1476,"height":1471,"rx":348,"fill":1070,"stroke":988,"style":1472},"103.0","83.3",[960,1478,1482],{"x":1479,"y":1480,"style":1481},"144.6","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[932,1484],{"x":1485,"y":941,"width":1486,"height":1471,"rx":348,"fill":1070,"stroke":988,"style":1472},"186.3","81.0",[960,1488,1490],{"x":1489,"y":1480,"style":1481},"226.8","B",[932,1492],{"x":1493,"y":941,"width":1494,"height":1471,"rx":348,"fill":1070,"stroke":988,"style":1472},"267.3","74.9",[960,1496,1498],{"x":1497,"y":1480,"style":1481},"304.7","C",[932,1500],{"x":1501,"y":941,"width":1502,"height":1471,"rx":348,"fill":1070,"stroke":988,"style":1472},"342.1","81.1",[960,1504,1506],{"x":1505,"y":1480,"style":1481},"382.7","D",[932,1508],{"x":1509,"y":941,"width":1510,"height":1471,"rx":348,"fill":1070,"stroke":988,"style":1472},"423.3","76.9",[960,1512,1514],{"x":1513,"y":1480,"style":1481},"461.7","E",[932,1516],{"x":1517,"y":941,"width":1518,"height":1471,"rx":348,"fill":1070,"stroke":988,"style":1472},"500.2","188.8",[960,1520,1522],{"x":1521,"y":1480,"style":1481},"594.6","F",[932,1524],{"x":1469,"y":1525,"width":1470,"height":1526,"rx":348,"fill":1070,"stroke":988,"style":1472},"40.0","28.0",[960,1528,761],{"x":1529,"y":1530,"style":1481},"87.0","59.0",[932,1532],{"x":1475,"y":1525,"width":1476,"height":1526,"rx":348,"fill":945,"stroke":946,"style":1533},"stroke-width:1.5px",[960,1535,1537],{"x":1479,"y":1530,"style":1536},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","PO line",[932,1539],{"x":1485,"y":1525,"width":1486,"height":1526,"rx":348,"fill":945,"stroke":946,"style":1533},[960,1541,1542],{"x":1489,"y":1530,"style":1536},"Item",[932,1544],{"x":1493,"y":1525,"width":1494,"height":1526,"rx":348,"fill":945,"stroke":946,"style":1533},[960,1546,1547],{"x":1497,"y":1530,"style":1536},"Ordered",[932,1549],{"x":1501,"y":1525,"width":1502,"height":1526,"rx":348,"fill":945,"stroke":946,"style":1533},[960,1551,1552],{"x":1505,"y":1530,"style":1536},"Received",[932,1554],{"x":1509,"y":1525,"width":1510,"height":1526,"rx":348,"fill":945,"stroke":946,"style":1533},[960,1556,1557],{"x":1513,"y":1530,"style":1536},"Invoiced",[932,1559],{"x":1517,"y":1525,"width":1518,"height":1526,"rx":348,"fill":945,"stroke":946,"style":1533},[960,1561,1562],{"x":1521,"y":1530,"style":1536},"Status",[932,1564],{"x":1469,"y":1565,"width":1470,"height":1526,"rx":348,"fill":1070,"stroke":988,"style":1472},"68.0",[960,1567,1568],{"x":1529,"y":1529,"style":1481},"2",[932,1570],{"x":1475,"y":1565,"width":1476,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1572,1574],{"x":1479,"y":1529,"style":1573},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","PO-801\u002F1",[932,1576],{"x":1485,"y":1565,"width":1486,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1578,1579],{"x":1489,"y":1529,"style":1573},"Bolts M8",[932,1581],{"x":1493,"y":1565,"width":1494,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1583,1584],{"x":1497,"y":1529,"style":1573},"5000",[932,1586],{"x":1501,"y":1565,"width":1502,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1588,1584],{"x":1505,"y":1529,"style":1573},[932,1590],{"x":1509,"y":1565,"width":1510,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1592,1584],{"x":1513,"y":1529,"style":1573},[932,1594],{"x":1517,"y":1565,"width":1518,"height":1526,"rx":348,"fill":1036,"stroke":1037,"style":1533},[960,1596,1598],{"x":1521,"y":1529,"style":1597},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Complete",[932,1600],{"x":1469,"y":1601,"width":1470,"height":1526,"rx":348,"fill":1070,"stroke":988,"style":1472},"96.0",[960,1603,1605],{"x":1529,"y":1604,"style":1481},"115.0","3",[932,1607],{"x":1475,"y":1601,"width":1476,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1609,1610],{"x":1479,"y":1604,"style":1573},"PO-801\u002F2",[932,1612],{"x":1485,"y":1601,"width":1486,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1614,1615],{"x":1489,"y":1604,"style":1573},"Nuts M8",[932,1617],{"x":1493,"y":1601,"width":1494,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1619,1584],{"x":1497,"y":1604,"style":1573},[932,1621],{"x":1501,"y":1601,"width":1502,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1623,1624],{"x":1505,"y":1604,"style":1573},"3000",[932,1626],{"x":1509,"y":1601,"width":1510,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1628,1624],{"x":1513,"y":1604,"style":1573},[932,1630],{"x":1517,"y":1601,"width":1518,"height":1526,"rx":348,"fill":1100,"stroke":1101,"style":1533},[960,1632,1634],{"x":1521,"y":1604,"style":1633},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Part received — late",[932,1636],{"x":1469,"y":1637,"width":1470,"height":1526,"rx":348,"fill":1070,"stroke":988,"style":1472},"124.0",[960,1639,934],{"x":1529,"y":1640,"style":1481},"143.0",[932,1642],{"x":1475,"y":1637,"width":1476,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1644,1645],{"x":1479,"y":1640,"style":1573},"PO-803\u002F1",[932,1647],{"x":1485,"y":1637,"width":1486,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1649,1650],{"x":1489,"y":1640,"style":1573},"Drill sets",[932,1652],{"x":1493,"y":1637,"width":1494,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1654,1655],{"x":1497,"y":1640,"style":1573},"40",[932,1657],{"x":1501,"y":1637,"width":1502,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1659,348],{"x":1505,"y":1640,"style":1573},[932,1661],{"x":1509,"y":1637,"width":1510,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1663,1655],{"x":1513,"y":1640,"style":1573},[932,1665],{"x":1517,"y":1637,"width":1518,"height":1526,"rx":348,"fill":1666,"stroke":1667,"style":1533},"#fce9e9","var(--danger,#dc2626)",[960,1669,1671],{"x":1521,"y":1640,"style":1670},"font-size:12px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Invoice without receipt",[932,1673],{"x":1469,"y":1674,"width":1470,"height":1526,"rx":348,"fill":1070,"stroke":988,"style":1472},"152.0",[960,1676,1678],{"x":1529,"y":1677,"style":1481},"171.0","5",[932,1680],{"x":1475,"y":1674,"width":1476,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1682,1683],{"x":1479,"y":1677,"style":1573},"PO-804\u002F1",[932,1685],{"x":1485,"y":1674,"width":1486,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1687,1688],{"x":1489,"y":1677,"style":1573},"Paper A4",[932,1690],{"x":1493,"y":1674,"width":1494,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1692,1693],{"x":1497,"y":1677,"style":1573},"200",[932,1695],{"x":1501,"y":1674,"width":1502,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1697,1693],{"x":1505,"y":1677,"style":1573},[932,1699],{"x":1509,"y":1674,"width":1510,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1701,348],{"x":1513,"y":1677,"style":1573},[932,1703],{"x":1517,"y":1674,"width":1518,"height":1526,"rx":348,"fill":1070,"stroke":988,"style":1533},[960,1705,1707],{"x":1521,"y":1677,"style":1706},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Received, awaiting invoice",[932,1709],{"x":1469,"y":1710,"width":1470,"height":1526,"rx":348,"fill":1070,"stroke":988,"style":1472},"180.0",[960,1712,1714],{"x":1529,"y":1713,"style":1481},"199.0","6",[932,1716],{"x":1475,"y":1710,"width":1476,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1718,1719],{"x":1479,"y":1713,"style":1573},"PO-805\u002F1",[932,1721],{"x":1485,"y":1710,"width":1486,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1723,1724],{"x":1489,"y":1713,"style":1573},"Laptops",[932,1726],{"x":1493,"y":1710,"width":1494,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1728,1714],{"x":1497,"y":1713,"style":1573},[932,1730],{"x":1501,"y":1710,"width":1502,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1732,348],{"x":1505,"y":1713,"style":1573},[932,1734],{"x":1509,"y":1710,"width":1510,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1736,348],{"x":1513,"y":1713,"style":1573},[932,1738],{"x":1517,"y":1710,"width":1518,"height":1526,"rx":348,"fill":937,"stroke":988,"style":1472},[960,1740,1741],{"x":1521,"y":1713,"style":1573},"Open",[913,1743,922,1748,922,1751,922,1754,922,1757,922,1761,922,1767,922,1772,922,1776,922,1780,922,1785,922,1788,922,1791,922,1797,922,1801,922,1805,922,1808,922,1812,922,1815,922,1818,922,1823,922,1826,922,1830,922,1834,922,1838,922,1842,922,1845,922,1848,922,1853,922,1857,922,1860,922,1863,922,1866,922,1870,922,1873,922,1876,922,1882,922,1886,922,1889],{"viewBox":1744,"role":916,"ariaLabelledBy":1745,"xmlns":920,"style":921},"4 0 752 160",[1746,1747],"po-flow-t","po-flow-d",[924,1749,1750],{"id":1746},"Building the PO tracking report",[928,1752,1753],{"id":1747},"Aggregate receipts and invoices to one row per PO line, join them to the order lines, compute quantities to receive, received not invoiced and invoiced not received, assign a status, value open commitments and accruals, and write views for buyers, accounts payable and supplier reviews.",[932,1755],{"x":934,"y":348,"width":935,"height":1756,"fill":937},"160",[932,1758],{"x":997,"y":941,"width":1759,"height":1760,"rx":944,"fill":1070,"stroke":988,"style":947},"115.2","128.0",[949,1762,1764],{"transform":1763},"translate(65.6,37.8) scale(1.000)",[953,1765],{"d":1766,"fill":956,"stroke":1077,"style":958},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5",[960,1768,1771],{"x":1769,"y":1770,"style":1080},"77.6","84.2","Aggregate",[960,1773,1775],{"x":1769,"y":1774,"style":1018},"103.8","receipts and",[960,1777,1779],{"x":1769,"y":1778,"style":1018},"121.2","invoices per line",[37,1781],{"x1":1782,"y1":1783,"x2":1784,"y2":1783,"stroke":946,"style":947},"140.2","80.0","162.2",[977,1786],{"points":1787,"fill":980},"166.2,80.0 157.2,85.0 157.2,75.0",[932,1789],{"x":1790,"y":941,"width":1759,"height":1760,"rx":944,"fill":945,"stroke":946,"style":947},"171.2",[949,1792,1794],{"transform":1793},"translate(216.8,37.8) scale(1.000)",[953,1795],{"d":1796,"fill":956,"stroke":957,"style":958},"M6 4v5a5 5 0 0 0 5 5h7 M6 20v-5 M15 11l3 3-3 3",[960,1798,1800],{"x":1799,"y":1770,"style":963},"228.8","Join to PO",[960,1802,1804],{"x":1799,"y":1803,"style":963},"101.8","lines",[960,1806,1807],{"x":1799,"y":1778,"style":1018},"PO + line",[37,1809],{"x1":1810,"y1":1783,"x2":1811,"y2":1783,"stroke":946,"style":947},"291.4","313.4",[977,1813],{"points":1814,"fill":980},"317.4,80.0 308.4,85.0 308.4,75.0",[932,1816],{"x":1817,"y":941,"width":1759,"height":1760,"rx":944,"fill":1100,"stroke":1101,"style":947},"322.4",[949,1819,1820],{"transform":951},[953,1821],{"d":1822,"fill":956,"stroke":1108,"style":958},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16",[960,1824,1825],{"x":962,"y":943,"style":1111},"Three",[960,1827,1829],{"x":962,"y":1828,"style":1111},"93.0","differences",[960,1831,1833],{"x":962,"y":1832,"style":1018},"112.5","to receive, RNI,",[960,1835,1837],{"x":962,"y":1836,"style":1018},"130.0","INR",[37,1839],{"x1":1840,"y1":1783,"x2":1841,"y2":1783,"stroke":946,"style":947},"442.6","464.6",[977,1843],{"points":1844,"fill":980},"468.6,80.0 459.6,85.0 459.6,75.0",[932,1846],{"x":1847,"y":941,"width":1759,"height":1760,"rx":944,"fill":1036,"stroke":1037,"style":947},"473.6",[949,1849,1851],{"transform":1850},"translate(519.2,29.0) scale(1.000)",[953,1852],{"d":1043,"fill":956,"stroke":1044,"style":958},[960,1854,1856],{"x":1855,"y":943,"style":1048},"531.2","Status +",[960,1858,1859],{"x":1855,"y":1828,"style":1048},"values",[960,1861,1862],{"x":1855,"y":1832,"style":1018},"open value,",[960,1864,1865],{"x":1855,"y":1836,"style":1018},"accrual",[37,1867],{"x1":1868,"y1":1783,"x2":1869,"y2":1783,"stroke":946,"style":947},"593.8","615.8",[977,1871],{"points":1872,"fill":980},"619.8,80.0 610.8,85.0 610.8,75.0",[932,1874],{"x":1875,"y":941,"width":1759,"height":1760,"rx":944,"fill":1070,"stroke":988,"style":947},"624.8",[949,1877,1879],{"transform":1878},"translate(670.4,37.8) scale(1.000)",[953,1880],{"d":1881,"fill":956,"stroke":1077,"style":958},"M9 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z M2.5 20c0-3.6 2.9-6 6.5-6s6.5 2.4 6.5 6 M16 4.5a3.3 3.3 0 0 1 0 6.3 M18 14.3c2.2.7 3.5 2.8 3.5 5.7",[960,1883,1885],{"x":1884,"y":1770,"style":1080},"682.4","Views",[960,1887,1888],{"x":1884,"y":1774,"style":1018},"buyers, AP,",[960,1890,1891],{"x":1884,"y":1778,"style":1018},"suppliers",[20,1893,1895],{"id":1894},"step-4-write-the-workbook","Step 4 — Write the workbook",[25,1897,1899],{"className":75,"code":1898,"language":77,"meta":30,"style":30},"from openpyxl import load_workbook\nfrom openpyxl.formatting.rule import FormulaRule\nfrom openpyxl.styles import PatternFill\n\npath = \"po_tracking_2026-09-30.xlsx\"\nwith pd.ExcelWriter(path, engine=\"openpyxl\", date_format=\"dd mmm yyyy\", datetime_format=\"dd mmm yyyy\") as xw:\n    lines.to_excel(xw, sheet_name=\"All lines\", index=False)\n    buyers.to_excel(xw, sheet_name=\"Buyers - to receive\", index=False)\n    ap.to_excel(xw, sheet_name=\"AP - exceptions\", index=False)\n    by_supplier.to_excel(xw, sheet_name=\"By supplier\")\nwb = load_workbook(path)\nws = wb[\"All lines\"]\nstatus_col = [c.value for c in ws[1]].index(\"Status\") + 1\nletter = ws.cell(1, status_col).column_letter\narea = f\"A2:{ws.cell(1, ws.max_column).column_letter}{ws.max_row}\"\nfor text, colour in [(\"Invoice without receipt\", \"FFC7CE\"), (\"late\", \"FFEB9C\"), (\"Late\", \"FFEB9C\"), (\"Complete\", \"E2EFDA\")]:\n    ws.conditional_formatting.add(area, FormulaRule(formula=[f'ISNUMBER(SEARCH(\"{text}\",${letter}2))'], fill=PatternFill(\"solid\", fgColor=colour)))\nwb.save(path)\nprint(wb.sheetnames)\n",[32,1900,1901,1914,1926,1938,1942,1952,1995,2017,2039,2061,2075,2085,2099,2136,2151,2185,2237,2297,2303],{"__ignoreMap":30},[35,1902,1903,1906,1909,1911],{"class":37,"line":38},[35,1904,1905],{"class":84},"from",[35,1907,1908],{"class":88}," openpyxl ",[35,1910,85],{"class":84},[35,1912,1913],{"class":88}," load_workbook\n",[35,1915,1916,1918,1921,1923],{"class":37,"line":98},[35,1917,1905],{"class":84},[35,1919,1920],{"class":88}," openpyxl.formatting.rule ",[35,1922,85],{"class":84},[35,1924,1925],{"class":88}," FormulaRule\n",[35,1927,1928,1930,1933,1935],{"class":37,"line":105},[35,1929,1905],{"class":84},[35,1931,1932],{"class":88}," openpyxl.styles ",[35,1934,85],{"class":84},[35,1936,1937],{"class":88}," PatternFill\n",[35,1939,1940],{"class":37,"line":124},[35,1941,102],{"emptyLinePlaceholder":101},[35,1943,1944,1947,1949],{"class":37,"line":153},[35,1945,1946],{"class":88},"path ",[35,1948,130],{"class":84},[35,1950,1951],{"class":45}," \"po_tracking_2026-09-30.xlsx\"\n",[35,1953,1954,1957,1960,1963,1965,1968,1970,1973,1975,1978,1980,1983,1985,1987,1990,1992],{"class":37,"line":176},[35,1955,1956],{"class":84},"with",[35,1958,1959],{"class":88}," pd.ExcelWriter(path, ",[35,1961,1962],{"class":142},"engine",[35,1964,130],{"class":84},[35,1966,1967],{"class":45},"\"openpyxl\"",[35,1969,139],{"class":88},[35,1971,1972],{"class":142},"date_format",[35,1974,130],{"class":84},[35,1976,1977],{"class":45},"\"dd mmm yyyy\"",[35,1979,139],{"class":88},[35,1981,1982],{"class":142},"datetime_format",[35,1984,130],{"class":84},[35,1986,1977],{"class":45},[35,1988,1989],{"class":88},") ",[35,1991,92],{"class":84},[35,1993,1994],{"class":88}," xw:\n",[35,1996,1997,2000,2002,2004,2007,2009,2011,2013,2015],{"class":37,"line":199},[35,1998,1999],{"class":88},"    lines.to_excel(xw, ",[35,2001,143],{"class":142},[35,2003,130],{"class":84},[35,2005,2006],{"class":45},"\"All lines\"",[35,2008,139],{"class":88},[35,2010,527],{"class":142},[35,2012,130],{"class":84},[35,2014,532],{"class":108},[35,2016,121],{"class":88},[35,2018,2019,2022,2024,2026,2029,2031,2033,2035,2037],{"class":37,"line":204},[35,2020,2021],{"class":88},"    buyers.to_excel(xw, ",[35,2023,143],{"class":142},[35,2025,130],{"class":84},[35,2027,2028],{"class":45},"\"Buyers - to receive\"",[35,2030,139],{"class":88},[35,2032,527],{"class":142},[35,2034,130],{"class":84},[35,2036,532],{"class":108},[35,2038,121],{"class":88},[35,2040,2041,2044,2046,2048,2051,2053,2055,2057,2059],{"class":37,"line":226},[35,2042,2043],{"class":88},"    ap.to_excel(xw, ",[35,2045,143],{"class":142},[35,2047,130],{"class":84},[35,2049,2050],{"class":45},"\"AP - exceptions\"",[35,2052,139],{"class":88},[35,2054,527],{"class":142},[35,2056,130],{"class":84},[35,2058,532],{"class":108},[35,2060,121],{"class":88},[35,2062,2063,2066,2068,2070,2073],{"class":37,"line":275},[35,2064,2065],{"class":88},"    by_supplier.to_excel(xw, ",[35,2067,143],{"class":142},[35,2069,130],{"class":84},[35,2071,2072],{"class":45},"\"By supplier\"",[35,2074,121],{"class":88},[35,2076,2077,2080,2082],{"class":37,"line":317},[35,2078,2079],{"class":88},"wb ",[35,2081,130],{"class":84},[35,2083,2084],{"class":88}," load_workbook(path)\n",[35,2086,2087,2090,2092,2095,2097],{"class":37,"line":370},[35,2088,2089],{"class":88},"ws ",[35,2091,130],{"class":84},[35,2093,2094],{"class":88}," wb[",[35,2096,2006],{"class":45},[35,2098,223],{"class":88},[35,2100,2101,2104,2106,2109,2112,2115,2118,2121,2123,2126,2128,2130,2133],{"class":37,"line":412},[35,2102,2103],{"class":88},"status_col ",[35,2105,130],{"class":84},[35,2107,2108],{"class":88}," [c.value ",[35,2110,2111],{"class":84},"for",[35,2113,2114],{"class":88}," c ",[35,2116,2117],{"class":84},"in",[35,2119,2120],{"class":88}," ws[",[35,2122,761],{"class":108},[35,2124,2125],{"class":88},"]].index(",[35,2127,746],{"class":45},[35,2129,1989],{"class":88},[35,2131,2132],{"class":84},"+",[35,2134,2135],{"class":108}," 1\n",[35,2137,2138,2141,2143,2146,2148],{"class":37,"line":446},[35,2139,2140],{"class":88},"letter ",[35,2142,130],{"class":84},[35,2144,2145],{"class":88}," ws.cell(",[35,2147,761],{"class":108},[35,2149,2150],{"class":88},", status_col).column_letter\n",[35,2152,2153,2156,2158,2161,2164,2166,2169,2171,2174,2177,2180,2182],{"class":37,"line":480},[35,2154,2155],{"class":88},"area ",[35,2157,130],{"class":84},[35,2159,2160],{"class":84}," f",[35,2162,2163],{"class":45},"\"A2:",[35,2165,240],{"class":1406},[35,2167,2168],{"class":88},"ws.cell(",[35,2170,761],{"class":108},[35,2172,2173],{"class":88},", ws.max_column).column_letter",[35,2175,2176],{"class":1406},"}{",[35,2178,2179],{"class":88},"ws.max_row",[35,2181,1420],{"class":1406},[35,2183,2184],{"class":45},"\"\n",[35,2186,2187,2189,2192,2194,2197,2199,2201,2204,2207,2210,2212,2215,2217,2220,2222,2224,2226,2229,2231,2234],{"class":37,"line":833},[35,2188,2111],{"class":84},[35,2190,2191],{"class":88}," text, colour ",[35,2193,2117],{"class":84},[35,2195,2196],{"class":88}," [(",[35,2198,1223],{"class":45},[35,2200,139],{"class":88},[35,2202,2203],{"class":45},"\"FFC7CE\"",[35,2205,2206],{"class":88},"), (",[35,2208,2209],{"class":45},"\"late\"",[35,2211,139],{"class":88},[35,2213,2214],{"class":45},"\"FFEB9C\"",[35,2216,2206],{"class":88},[35,2218,2219],{"class":45},"\"Late\"",[35,2221,139],{"class":88},[35,2223,2214],{"class":45},[35,2225,2206],{"class":88},[35,2227,2228],{"class":45},"\"Complete\"",[35,2230,139],{"class":88},[35,2232,2233],{"class":45},"\"E2EFDA\"",[35,2235,2236],{"class":88},")]:\n",[35,2238,2239,2242,2245,2247,2250,2252,2255,2257,2259,2261,2264,2266,2269,2271,2274,2276,2279,2281,2284,2287,2289,2292,2294],{"class":37,"line":859},[35,2240,2241],{"class":88},"    ws.conditional_formatting.add(area, FormulaRule(",[35,2243,2244],{"class":142},"formula",[35,2246,130],{"class":84},[35,2248,2249],{"class":88},"[",[35,2251,1400],{"class":84},[35,2253,2254],{"class":45},"'ISNUMBER(SEARCH(\"",[35,2256,240],{"class":1406},[35,2258,960],{"class":88},[35,2260,1420],{"class":1406},[35,2262,2263],{"class":45},"\",$",[35,2265,240],{"class":1406},[35,2267,2268],{"class":88},"letter",[35,2270,1420],{"class":1406},[35,2272,2273],{"class":45},"2))'",[35,2275,1154],{"class":88},[35,2277,2278],{"class":142},"fill",[35,2280,130],{"class":84},[35,2282,2283],{"class":88},"PatternFill(",[35,2285,2286],{"class":45},"\"solid\"",[35,2288,139],{"class":88},[35,2290,2291],{"class":142},"fgColor",[35,2293,130],{"class":84},[35,2295,2296],{"class":88},"colour)))\n",[35,2298,2300],{"class":37,"line":2299},18,[35,2301,2302],{"class":88},"wb.save(path)\n",[35,2304,2306,2308],{"class":37,"line":2305},19,[35,2307,483],{"class":108},[35,2309,2310],{"class":88},"(wb.sheetnames)\n",[10,2312,2313,2314,2317],{},"The ",[57,2315,2316],{},"All lines"," sheet is the complete picture with rows coloured by status — red for invoices without receipt, amber for anything late, green for complete — using conditional formatting so the colours follow if someone edits a status. Each audience has its own sheet, sorted for the decision it supports. Run daily from the three exports, the report also shows how the picture changes: a late line that disappears from the buyers' sheet has arrived, and an exception that leaves the AP sheet has been resolved.",[20,2319,2321],{"id":2320},"keeping-the-three-sources-honest","Keeping the three sources honest",[10,2323,2324],{},"The report is only as good as the joins, and the joins depend on PO and line numbers being recorded consistently in all three systems. The commonest problems are receipts or invoices booked against the wrong line, or without a PO reference at all. Add two checks before building the report: receipts and invoices whose PO and line do not exist in the PO export — the anti-join of each against the PO lines — and lines where more has been received than was ordered, which may be a legitimate over-delivery or a receipt booked to the wrong line. Both lists belong on an exceptions sheet of their own. And because unit prices on invoices can differ from the PO, add a price check to the AP view — invoice amount against invoiced quantity times PO price — so price variances are caught alongside quantity mismatches.",[20,2326,2328],{"id":2327},"common-pitfalls","Common pitfalls",[2330,2331,2332,2340,2346,2352,2358],"ul",{},[2333,2334,2335,2339],"li",{},[2336,2337,2338],"strong",{},"Joining raw receipts and invoices."," Aggregate to one row per PO line first.",[2333,2341,2342,2345],{},[2336,2343,2344],{},"Paying invoices without receipts."," Flag them and hold payment until goods arrive.",[2333,2347,2348,2351],{},[2336,2349,2350],{},"Missing the accrual."," Received-not-invoiced goods are a month-end liability.",[2333,2353,2354,2357],{},[2336,2355,2356],{},"Lateness from order date."," Measure against the promised date.",[2333,2359,2360,2363],{},[2336,2361,2362],{},"Ignoring orphan receipts and invoices."," List those whose PO line does not exist.",[20,2365,2367],{"id":2366},"conclusion","Conclusion",[10,2369,2370],{},"To track purchase orders, aggregate receipts and invoices to one row per PO line, join them to the order lines, and compute quantities still to receive, received but not invoiced, and invoiced but not received. Assign a status in order of importance, value open commitments and the received-not-invoiced accrual, and write a workbook with all lines colour-coded by status plus separate sheets for buyers, accounts payable and supplier reviews.",[20,2372,2374],{"id":2373},"frequently-asked-questions","Frequently asked questions",[10,2376,2377,2380],{},[2336,2378,2379],{},"How do I track open purchase orders in Excel with Python?","\nJoin purchase order lines to aggregated receipts and invoices by PO and line number, then compute the quantity still to receive and compare promised dates with today.",[10,2382,2383,2386],{},[2336,2384,2385],{},"What is received not invoiced?","\nGoods that have arrived but for which no supplier invoice has been booked; their cost is accrued as a liability at month end.",[10,2388,2389,2392],{},[2336,2390,2391],{},"How do I find invoices that should not be paid yet?","\nFlag PO lines where the invoiced quantity exceeds the received quantity; those invoices fail the three-way match until the goods arrive.",[10,2394,2395,2398],{},[2336,2396,2397],{},"Why are my PO totals doubled after joining receipts?","\nA line with several deliveries matches several receipt rows; aggregate receipts per PO line before joining.",[20,2400,2402],{"id":2401},"related","Related",[2330,2404,2405,2411,2416,2423],{},[2333,2406,2407,2408,2410],{},"Up one level: ",[14,2409,17],{"href":16}," — finance reports from Python.",[2333,2412,2413,2415],{},[14,2414,1448],{"href":1447}," — supplier performance.",[2333,2417,2418,2422],{},[14,2419,2421],{"href":2420},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-accruals-and-prepayments-schedule\u002F","Build an Accruals and Prepayments Schedule"," — booking the accrual.",[2333,2424,2425,2429],{},[14,2426,2428],{"href":2427},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-an-aged-creditors-report\u002F","Build an Aged Creditors Report"," — what is owed once invoiced.",[2431,2432,2433],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}",{"title":30,"searchDepth":98,"depth":98,"links":2435},[2436,2437,2438,2439,2440,2441,2442,2443,2444,2445],{"id":22,"depth":98,"text":23},{"id":71,"depth":98,"text":72},{"id":545,"depth":98,"text":546},{"id":1118,"depth":98,"text":1119},{"id":1894,"depth":98,"text":1895},{"id":2320,"depth":98,"text":2321},{"id":2327,"depth":98,"text":2328},{"id":2366,"depth":98,"text":2367},{"id":2373,"depth":98,"text":2374},{"id":2401,"depth":98,"text":2402},"2026-10-02","Track every purchase order line from order to receipt to invoice with pandas: combine PO, goods-receipt and supplier-invoice exports, compute what is still to arrive and what has been received but not invoiced, flag late deliveries and invoices without receipts, value the open commitments, and write a status workbook buyers and accounts payable can both work from.","md",[2450,2452,2454,2456],{"q":2379,"a":2451},"Join purchase order lines to aggregated receipts and invoices by PO and line number, then compute the quantity still to receive and compare promised dates with today.",{"q":2385,"a":2453},"Goods that have arrived but for which no supplier invoice has been booked; their cost is accrued as a liability at month end.",{"q":2391,"a":2455},"Flag PO lines where the invoiced quantity exceeds the received quantity; those invoices fail the three-way match until the goods arrive.",{"q":2397,"a":2457},"A line with several deliveries matches several receipt rows; aggregate receipts per PO line before joining.","purchase order tracking report python",null,{"breadcrumb":2461},[2462,2465,2468],{"name":2463,"item":2464},"Home","\u002F",{"name":2466,"item":2467},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":2469,"item":16},"Building Finance and Operations Reports in Excel with Python","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report",{"title":2472,"description":2473},"Build a Purchase Order Tracking Report in Excel with Python","PO tracking with pandas: purchase order lines joined to receipts and invoices, outstanding and received-not-invoiced quantities, late lines against promised dates, three-way match exceptions, open commitment value and an Excel status report.","build-a-purchase-order-tracking-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-purchase-order-tracking-report\u002Findex",[2477,2478],"report-layouts","dates-and-times","how-to","aRbO6lU0Q8cNHGhbALitRe75dzDPJXOC8ZgS0Eoy-04",[2482,2486],{"title":2483,"path":2484,"stem":2485,"children":-1},"Build a Project Budget Burn Report","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-project-budget-burn-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-project-budget-burn-report\u002Findex",{"title":2487,"path":2488,"stem":2489,"children":-1},"Build a Returns Analysis Report","\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-returns-analysis-report","automating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-returns-analysis-report\u002Findex",[2491,2493],{"path":2492,"title":2469},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports",{"path":2494,"title":2466},"\u002Fautomating-reporting-workflows",[],[2497,2501,2504,2507,2510,2513],{"path":2498,"title":2499,"kind":2500},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards","Building Multi-Sheet Excel Dashboards","guide",{"path":2502,"title":2503,"kind":2479},"\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":2505,"title":2506,"kind":2479},"\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":2508,"title":2509,"kind":2479},"\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":2511,"title":2512,"kind":2479},"\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":2514,"title":2515,"kind":2479},"\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",1790972127088]