Basic Store App using go.
Tags: beginners, go, programming
Published: 2023-01-10 21:10:14

## Introduction:

This lesson will show you how to write a simple store application in the Go programming language. In the store, there is a product and a car as an example product, both of which are defined using structs, as well as methods for interacting with the store. A car's model, brand, year, color, condition, and mileage are all stored in the Car struct. The Product struct contains product information such as its ID, name, price, quantity, and status, as well as an embedding of the Car struct. The store struct keeps track of all the products in the store as well as all the products sold.

There are methods to interact with the store such as _AddProduct_, _UpdateProduct_, _AddProducts_, _ListProducts_, _SellProduct_, _ListSoldProducts_, and _TotalSold_.

- The AddProduct method adds a new product to the store.
- The UpdateProduct method updates an existing product in the store.
- The AddProducts method adds multiple products to the store. - The ListProducts method returns a list of all products that are currently available for purchase in the store.
- The SellProduct method allows you to sell a product and deduct the amount sold from the product's quantity. It also adds the sold item to the store's sold product list. 
- The ListSoldProducts method returns a list of all sold products as well as the total ProductPrice of all sold products. 
- The TotalSold method displays the total ProductPrice of all sold products.


## Prerequisites:

- A basic understanding of Go.
- A Go runtime environment installation on the machine that will run it.
- This lesson requires no additional packages or libraries other than the built-in fmt package. The code provided should work as is because the fmt package is used for input and output operations such as the Println function and others.


## Let's Start Coding Step by Step:

Let's start by creating a package `shop.go`.
Inside the shop.go file that we created in the previous step, we start with the following starter code at the top of the file.

```go
package shop

import "fmt"
```

Define the car struct
```go
type Car struct {
	Model   string
	Brand   string
	Year    int
	Color   string
	IsUsed  bool
	Mileage int
}
```

We'd also define the Product struct which embeds the Car struct:

```go
type Product struct {
	Id              int
	Name            string
	ProductPrice    float64
	ProductQuantity int
	ProductStatus   bool
	Car
}
```

Define the store struct, which will hold slices of Product structs for products in the store and products that have been sold:
```go
type store struct {
	products []Product
	sold     []Product
}
```
Define a function NewShop which creates a new shop by returning a pointer to a store struct, this function is used as a constructor for the store struct:
```go
func NewShop() *store {
	return &store{}
}
```

Define a method updateStatus to update the ProductStatus field of the product struct:
```go
func (p *Product) updateStatus(status bool) {
	p.ProductStatus = status
}
```

**Let's continue by adding all the methods necessary to make the store functional.**

Add a method **AddProduct** to add a new product to the store, it takes a pointer to a Product struct as an argument and checks whether the product already exists and if so it updates the product:
```go
func (s *store) AddProduct(p *Product) {
	//check if product already exists and update it
	for _, prod := range s.products {
		if prod.Name == p.Name {
			fmt.Println("Product already exists")
			fmt.Println("Updating product")
			s.UpdateProduct(p)
			return
		}
	}
	s.products = append(s.products, *p)
}
```

Add a method **UpdateProduct** to update an existing product in the store, it takes a pointer to a Product struct as an argument and update the product with the same name as the input:

```go
func (s *store) UpdateProduct(p *Product) {
	for i, prod := range s.products {
		if prod.Name == p.Name {
			s.products[i] = *p
			return
		}
	}
}
```

Add a method **AddProducts** to add multiple products to the store, it takes a variable number of pointers to Product structs as arguments:

```go
func (s *store) AddProducts(ps ...*Product) {
	for _, p := range ps {
		s.AddProduct(p)
	}
}
```

Add a method **ListProducts** to list all products that are currently in the store and still for sale:

```go
func (s *store) ListProducts() {
    //check if store has products
    if len(s.products) == 0 {
        fmt.Println("No products in store")    
    } else {
        fmt.Println("All Available Products:")

	for _, p := range s.products {
		p.Display()
	}
	fmt.Println("====================================")
    }
 }
```

Add a method **SellProduct** to sell a product and reduce the product's quantity by the quantity sold. This method also add the sold item to the store's list of sold products:

```go
func (s *store) SellProduct(id int, q int) {
    for i, p := range s.products {
    if p.Id == id {
        if p.ProductStatus {
            if p.ProductQuantity >= q {
                p.ProductQuantity -= q
                s.products[i] = p
                    s.checkSoldList(&p, q)
                } else {
                    fmt.Println("Not enough quantity")
                }
                // check if product quantity is 0, if yes, update product status to false
                if p.ProductQuantity == 0 {
                p.updateStatus(false)
                }
                return
            } else {
                fmt.Println("Product out of stock")
                return
            }
        }
    }
    fmt.Println("Product not found")
}
```

Add a method **checkSoldList** to check if the sold list has a product, if yes, add the quantity sold to the product in the list else add the product and the quantity sold to the list:
```go
func (s *store) checkSoldList(p *Product, q int) {
	for i, prod := range s.sold {
		if prod.Name == p.Name {
			s.sold[i].ProductQuantity += q
			return
		}
	}
	*p = Product{Id: p.Id, Name: p.Name, ProductPrice: p.ProductPrice, ProductQuantity: q}
	s.sold = append(s.sold, *p)
}
```

Add a method **ListSoldProducts** to list all products that have been sold and also the total ProductPrice of all the products sold:
```go
func (s *store) ListSoldProducts() {
	// check if store has products
	if len(s.sold) == 0 {
		fmt.Println("No products sold")
		return
	} else {
		fmt.Println("All Sold Products:")
		for _, p := range s.sold {
			fmt.Println(p.Name, "Quantity:", p.ProductQuantity)
		}
		fmt.Println("Total Sold:", s.TotalSold())
	}
}
```

Add a method **TotalSold** to show the total ProductPrice of all products sold:
```go
func (s *store) TotalSold() float64 {
	var total float64
	for _, p := range s.sold {
	total += p.ProductPrice * float64(p.ProductQuantity)
        }
        return total
}
```

We also have a helper function `Display` to display things on the console for us so that we can see what's going on for now

```go
func (p *Product) Display() {
	fmt.Printf("Product: %s, ProductPrice: %.2f, ProductQuantity: %d", p.Name, p.ProductPrice, p.ProductQuantity)
	fmt.Println()
}
```


So that is the end of the shop package.

We'd quickly have a look at the main package to see how we call a new shop instance and run the methods
_AddProduct_,
_UpdateProduct_, 
_AddProducts_, 
_ListProducts_, 
_SellProduct_, 
_ListSoldProducts_, 
and _TotalSold_.

We start by importing the necessary packages and then defining a main function.
In this case, we are importing the shop package we created earlier.
We are also importing the fmt package to use mostly in printing to the console.

```go
package main
import (
	"fmt"

	"github.com/codeflames/AutoShopProject/shop"
)

func main() {
//create a new shop instance
shop1 := shop.NewShop()
 
}
```

Inside the main function, we now create two new items/products (car product) that we can add to the shop we created earlier `shop1`.

You can create as much product you want here.

```go

	item1 := &shop.Product{
		Id:              1,
		Name:            "Civic 2019",
		ProductPrice:    100.00,
		ProductQuantity: 10,
		ProductStatus:   true,
		Car: shop.Car{
			Model:   "Civic",
			Brand:   "Honda",
			Year:    2019,
			Color:   "Red",
			IsUsed:  false,
			Mileage: 0,
		},
	}

	item2 := &shop.Product{
		Id:              2,
		Name:            "Corolla 2018",
		ProductPrice:    200.00,
		ProductQuantity: 5,
		ProductStatus:   true,
		Car: shop.Car{
			Model:   "Corolla",
			Brand:   "Toyota",
			Year:    2018,
			Color:   "Blue",
			IsUsed:  false,
			Mileage: 0,
		},
	}
```

We can now continue to use the methods we created in the `shop package` in the main function.

```go
        //Add the products to the shop (shop1)
	shop1.AddProducts(item1, item2)
        
        //List all sold products ==> null
	shop1.ListSoldProducts()

	fmt.Printf("Total unsold products price: %v", shop1.TotalUnsoldProductsPrice())
	fmt.Println()

	shop1.ListProducts()

	shop1.SellProduct(1, 2)

	shop1.ListSoldProducts()

	shop1.ListProducts()

	fmt.Println(shop1.TotalSales())
```


After this, we can save our work and run the go file using the  `go run filename` command.

In our case, we are running the main file, so it'd be
`go run main.go`
and we'd see the details on the console.

## Conclusion:
In summary, this lesson simulates a car shop where you can add products (cars), list all the sold and unsold products, sell products and check for the total sales.