Reduce your business workload: purchasing manager

If you have a company that requires physical inputs or raw materials, you will know better than Enterprise Statistics that organizing stocks means avoiding unpleasant surprises. Thanks to programming, you can save man-hours of the secretary or the purchasing manager. If you, the owner or entrepreneur, deal directly with purchasing, I see a problem, but this is part of another discussion. In this article I will not talk about statistics, I will mention the R language, which comes from statistics. What follows has more to do with computer science, or if we really want to be pompous, with an artificial intelligence that is part of the domain of expert systems.

Clearly, to know when to shop, we need a statistical data dashboard, or at least a table indicating “critical quantities”.

On spreadsheets like Excel or Google Sheets we have macros: an automation that repeats actions we have recorded. Clearly they make sense if we have recorded repetitive actions or operations . If we want to automate actions outside those two environments, for example on a web browser (Chrome, Firefox, Safari, etc.), we can use a tool that allows us to schedule actions. I am referring to the Selenium library, which on R we find under the name Rselenium. It also exists on Python, Javascript. On the last language, there is a more appreciated library named Cypress.

 

Selenium has a definition that makes it hard to find for a layman: browser automation. Cypress has one that is even harder to connect to the goal of this article: web application test automation.

 

With Selenium we will program various operations, reduced from a more real-world case, that taken individually do not suggest intelligent behavior:

  1. Enter the links (URLs) of the products we buy most often. Adding alternatives would complicate the process.
  2. Open the links by checking the availability of each product.
  3. Click “Add to cart” for each product that has passed the previous check.
  4. Make the payment. And here comes the trickiest part from a security standpoint.

# ideally one needs to integrate this code with a data waiter (API)
# that calls this services when "critical" numbers are met
# on the stocks, present on a google sheet for example
# or and ERP 
library("RSelenium")

### Easy part ----

# create driver object
driver<-rsDriver(browser="firefox", port=4550L, verbose=F, chromever = NULL) 
# I choose Firefox for a specific reason. If you don't have it, install it
# clean memory, console, objects before restart it

# Open Amazon page, obviously business.amazon fits this purpose more
driver$client$navigate("https://www.amazon.it")

# here you'll insert recurring purchases
all_urls <- c("https://www.amazon.it/Farina-Caputo-Pizzeria-Kg-Cartone/dp/B07QCGS8JH/ref=sr_1_1?__mk_it_IT=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=3B57LQMB2P209&dib=eyJ2IjoiMSJ9.wbge344vLMdlohBrasE6P5cgk0DxKFEdlZI221RPOpbuSpxfeg7dGptWwuWs45OnC2H-L6mebdBXIpll_YtSZp8WA7T-hgl6TZYJDqMrb74zxmI6WT-qkpG2oHG-np1P_vtLO_ya3I1vu5ixh1LOnjR3rxYgaDjUIRzXIr1vzuofYfJG8kIDpV_AYul6ZEn6RMyXHYL04yTknESQC2cLk8YOmFZyoxBUC8SVnrAvAvQbFcsjtp5wg0KU_pYXGyt4SyTzdsEhKbLMnpHG1ArDuQFhu4feKDKd_Xq0dPksp68.7fV-948QxQrzI9iQpHOl6MO4ceXpYwefuI1fLc86ZNQ&dib_tag=se&keywords=farina+caputo&qid=1714286670&sprefix=farina+caputo%2Caps%2C144&sr=8-1",
                "https://www.amazon.it/MOZZARELLA-PIZZA-MONTANINA-5Kg-ABBASCIANO/dp/B0C4LHYSC1/ref=sr_1_10?crid=3HF5ELPOZ6IEA&dib=eyJ2IjoiMSJ9.ygT1FDeOXWue8yX7pOXfhsoPpyeyXA0M3mWx27Xuu0uyyBq616MVGfeV1wAoxVYYGX8s65Rd3rzHe6Ppg_UjafvjEPWR5OSM4Kl3FbGyNmn554Jf3-zH5mBdzdzeqMmNDLYk_SWYiBIJdNT7oFOOelSWST79OFRp7omrvkqqO5r44dAVMpv3T5pgV6e2nltSdP-n_QX2hAdFag8vrGbkco1bisyHMUyx0O8I6JXMu9C6xyDrbwgAPfiJf8nlK7PJVYzgL2VOUSPQmoZekQJBgGs9oZsb1mDM6zcQZ0dbREo.KqyR7iA-HqWodH2tXWptupGqyDkGcJ1wqSfZj1il_nQ&dib_tag=se&keywords=mozzarella+per+pizza&qid=1714286706&sprefix=mozzare%2Caps%2C115&sr=8-10",
                #"https://www.amazon.it/Passata-pomodoro-vetro-Mutti-cartone/dp/B09JT1L18P/ref=sr_1_3?crid=215UVAXCMH621&dib=eyJ2IjoiMSJ9.XcufPe7nGvkWcVUSWjPNgEZpKl9NxuMA_qjs1rWcy2MP7l_C_HQVgTVicAebBJ4irRuDLg8PUBmd7oqHa-K_bQIBS3wgewJtYCORNgBCTi09zvnFTqrcDTqtXrrFaZJQbOrn9GZ-0S6JQga7TfUNo3c2pVuVeFx_f5L2y3hEoeD807m1ohfTMXaLZGOqXPKaeH-ll6X9nCLVaI2pViL7Djxe25deRW1zj-Er8uZ4KYqFqqLnCSgVXd8Xnas6guaAaz7OrSrYwbakERkiNjEatRs-4JsjBDyjFjizCdrmO6o.fISYDOKOZJrzmsVZUebDCulWZs8-5YtBxKWIVh6QV5w&dib_tag=se&keywords=pomodoro+mutti&qid=1714288485&sprefix=pomodoro+%2Caps%2C92&sr=8-3",
                "https://www.amazon.it/Calabrese-Valle-Crati-Condimenti-Bottiglie/dp/B081M6Y24Q/ref=sr_1_20?__mk_it_IT=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1BS9YFSTOBUQM&dib=eyJ2IjoiMSJ9.gR3VrzQm2azxCS9X4X_pEZsc3_FehjXEVzcVQB2-FtRjXhBfrjFYyv33pQV-s__ENG85pnDDQhjVW82oRLw1TW58LgFspBgJ9fG1bjB3gAcKqpaCfTk4r4luFxbbpCWMqM2iwNqnr8lrT8iy36zdqXqaPuCYXA2I8pxTORNp9J8ZCeUx7zjfuuNBtviIl6IluYjvcgCe5pVlan6-BqlR5QL2wcAgXoqn96oKi7SMFeZPg0huRjZb_aCL0pBVmUFOWNyrVNtTQZLDNBxVJWN8FWmwN0Rw-LyMOXMvzOj-8xI.EMTmlgRjOBTuy95M02VaIErMj4IBtWBHuABjsPO8kBQ&dib_tag=se&keywords=bottiglia+pomodoro&qid=1714288621&sprefix=bottiglia+pomodoro%2Caps%2C101&sr=8-20")
for(urls in all_urls)
{
  text<- ""
  
  for(url in urls)
  {   
    # Open the page and waits that it fully loads
    driver$client$navigate(url)
    Sys.sleep(2) # 2 secondi, occhio
    
    # Search div tag that indicates the availability of the raw material
    div <- try(driver$client$findElement(using = "id", value = "availability"))  
    # watch out, the name of that "value" CAN change
    
    # IF the page doesn't have that tag, presume not available. Strong hypothesis
    if(class(div) == "try-error")
      next
    else
    {   
      # Get text from div tag
      text<- div$getElementText()[[1]]
      break
    }
  }
  
  if(text== "Available") 
    # watch out, that text value could change. Es. "Available 2" or anyway a number
  {  
    
    add_to_cart <- driver$client$findElement(using = "id", value = "add-to-cart-button")
    add_to_cart$clickElement()
    
  }
  
  Sys.sleep(5)
  
}

### Delicate part ----
# NEVER write credentials inside a script
# There exists various ways to store secrets: Keyring, creation of specific files, etc.

# Go to cart page
driver$client$navigate("https://www.amazon.it/gp/cart/view.html?ref_=nav_cart")


# Find the button "Proceed" 
proceed_with_order<- driver$client$findElement(using = "name", value = "proceedToRetailCheckout")

# Click it
proceed_with_order$clickElement()

# Find box to insert username
username_input <- driver$client$findElement(using = "id", value = "ap_email")

# Insert username 
username_input$sendKeysToElement(list("USER_SECRET")) #WATCHOUT

# Submit username
username_input$submitElement()


# Find box to insert password
password_input <- driver$client$findElement(using = "id", value = "ap_password")

# Insert password
password_input$sendKeysToElement(list("PASSWORD_SECRET")) #WATCHOUT

# Submit password
password_input$submitElement()

# Wait the loading of the password
Sys.sleep(2)

All good so far, eh? On paper we have a code that reduces the secretary or purchasing manager’s buying process to handfuls of seconds. On paper…on screen… Too bad we have something immutable, the code, doing something on something mutable, the Amazon site (or front-end). If tomorrow morning someone gets up, at Amazon, and decides that immediate availability becomes “Available Now!”, that code no longer works. Same thing if the “Add to Cart” button changes slightly. You have to anticipate that event and have the foresight to handle what computer scientists call an exception: if the code fails, you can, for example, send an email to the programmer, owner, etc. Think some managers get production bonuses because they can save the company some money because they don’t have such pieces of code inserted…..

So this supposed expert system or AI of purchases has no adaptability unless the programmer touches the code. There are already more adaptable systems that integrate statistical linguistic models (LLM), but this patch creates another hole.

 

I come back to point 4 because that is also where reputations and careers can be at stake: there are best practices for storing sensitive data, something I have called a chamber of secrets in the past.

Want to find out if it’s worth automating a set of purchases, depending on the man-hours/years needed? Let’s hear from you in a free call.

You can also access the code above in a different, more insider format.

 

Privacy Policy