RESTful API app for inventory management system POS used by multi-outlets restaurant chain
Product: consists of name and price, belongs to one Category, and has many Ingredients
Product would represent a food or drink in the restaurant's menu. One product would have a defined price tag on it, and would have to be cooked or served using a defined set of raw ingredients.
Ingredient: consists of name, unitPrice, stock, and can be a part of many Products
Ingredient represents the individual raw ingredients that would have been consumed when a product dish is served. An ingredient has its own unit price, and the stock number tells how much does the restaurant has left of an ingredient.
Category: only has name
Category is the variety of product set that the restaurant has to offer.
User: contain a unique username, password, name, and email
User represents the restaurant staff or owner who uses this system. This model is purely for authentication purposes.
/user/register
-
[
POST] register a new user account for api access authenticationJson parameters[required]:
username:string(allow A-Z, a-z, 0-9, more than 4 characters)password:string(allow A-Z, a-z, 0-9, special chars in [#$@!%&*?], 4-30 characters)name:string(allow A-Z, a-z, spaces, special chars in ['.-], more than 2 characters)email:stringReturn: Json response on success (201) with new user data, or bad input and other errors (400)
/user/auth
-
[
POST] authenticate the registered user's credentials to retrieve the authentication tokenJson parameters[required]:
username:stringpassword:stringReturn: Json response on success (200) with user data and authentication
token, or HttpUnauthorizedError (401) for invalid credentials
Return example:
{
"success": true,
"message": "Login user user1 successful",
"token": "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjoxLCJ1c2VybmFtZSI6InVzZXIxIiwicGFzc3dvcmQiOiIkMmEkMTAkNEtHLkdQRDAuSFFPd0EzeVlFVXQzT2Y0ZkNxeXE2NEpORFF0dkRSVUhrYVdPbVh1ZnQ0aHEiLCJuYW1lIjoiVXNlciBPbmUiLCJlbWFpbCI6InVzZXJAbGluZmluaS50ZSIsImNyZWF0ZWRBdCI6IjIwMTctMTAtMjlUMTg6MTY6NTQuMDEwWiIsInVwZGF0ZWRBdCI6IjIwMTctMTAtMjlUMTg6MTY6NTQuMDEwWiJ9LCJpYXQiOjE1MDkzMTE2MTgsImV4cCI6MTUwOTkxNjQxOH0.Vc0nDjY81sshXdKTTiY1xK_i1iVuVYVsW1-qwv8AuBo",
"data": {
"id": 1,
"username": "user1",
"name": "User One",
"email": "user@linfini.te",
"createdAt": "2017-10-29T18:16:54.010Z",
"updatedAt": "2017-10-29T18:16:54.010Z"
}
}After authenticating with /user/auth , include the token value at the header of every requests to the /api routes.
Example:
curl -G <--hostname-->/api/category --header "Authentication:<--Your token here-->"All of the following routes are secured. Authentication token is required to use, otherwise HttpUnathorized (401) will be returned
/api/category
-
[
GET] get all categoriesParameters: None
Return: Json response on success (200)
-
[
POST] add one categoryJson parameter[required]:
name:stringReturn: Json response on success (201) with category data, or error (400)
/api/category/:_id
-
[
GET] get a single categoryUrl parameter:
_id:integerReturn: Json response on success (200) with category data, or HttpNotFound(404)
-
[
PUT] update a single categoryUrl parameter:
_id:integerJson parameter[required]:
name:stringReturn: Json response on success (200) with category data, or HttpNotFound (404)
-
[
DELETE] delete a single categoryUrl parameter:
_id:integerReturn: Response code (204) on success with empty body, or HttpNotFound (404)
Note: Delete may not be possible if the category is associated to a product
/api/ingredient
-
[
GET] get all ingredients Parameters: None
Return: Json response on success (200)
-
[
POST] add one ingredients Json parameters[required]:
name:stringunitPrice:floatstock:float Return: Json response on success (201) with ingredient data, or error (400)
/api/ingredient/:_id
-
[
GET] get a single ingredient Url parameter:
_id:integer Return: Json response on success (200) with ingredient data, or HttpNotFound(404)
-
[
PUT] update a single ingredientUrl parameterr:
_id:integerJson parameters[required]:
name:stringunitPrice:floatstock:floatReturn: Json response on success (200) with ingredient data, or HttpNotFound(404)
-
[
DELETE] delete an ingredientUrl parameter:
_id:integerReturn: Response code (204) on success with empty body, or HttpNotFound (404)
Note: Delete may not be possible if the ingredient is associated to a product
/api/product
-
[
GET] get the list of all products Parameters: None
Return: Json response on success (200)
-
[
POST] add one new productJson parameter[required]:
name:stringprice:floatCategory_id:CategoryReturn: Json response on success (201) with new product data, or error (400)
Note: Category_id must have already been created
/api/product/:_id
-
[
GET] get one productUrl parameter:
_id:integerReturn: Json response on success (200) with product data, or HttpNotFound(404)
-
[
PUT] update a single productUrl parameter:
_id:integerJson parameter[required]:
name:stringprice:floatCategory_id:Category**Return: Json response on success (200) with new product data, or error (400)
Node: Category_id must have already been created
-
[
DELETE] delete one productUrl parameter:
_id:integerReturn: Response code (204) on success with empty body, or HttpNotFound (404)
Note: Delete may not be possible if the product is associated to a ingredient
/api/product/:_id/ingredient/:Ing_id
-
[
PUT] create association between product and ingredient Url parameter:
_id:integerIng_id:integer Return: Json response on success (200) with new association data, or HttpNotFound (400) if one of the object cannot be found
-
[
DELETE] delete association between product and ingredientUrl parameter:
_id:integerIng_id:integerReturn: Response code (204) on success with empty body, or HttpNotFound (404)
After installing npm and git in your machine, clone the repository and do npm install as follow:
git clone https://github.com/tysonlin/ims-pos.git
cd ims-pos
npm installLoad your database connection string into the environment variable, and do sequelize db:migrate to initialize the database tables:
DATABASE_URL=your_database_url
"./node_modules/.bin/sequelize" db:migrate --env productionThe app loads the environment variable by reading .env file in the root directory of the project. Create .env file with environment variable in line with following:
touch .env# .env file
NODE_ENV=production
LOG_LEVEL=verbose
LOG_DIR=./log
PORT=80
DATABASE_URL=your_database_url
SECRET=some_random_key_string
NODE_ENV (required, prefer production) indicates the environment context which the app would execute on
DATABASE_URL (required) is your database connection string
LOG_LEVEL filters the log level the app would put in the log file and console. The level follows that of the npm log level: [silly|debug|verbose|info|warn|error]
LOG_DIR is the location the log files would be created at, defaults at ./log
PORT is the port number that the app would run on, defaults at 8000
SECRET is the random string to seed the encryption process in authentication
After .env file is setup, start app by using:
npm start
- Several value-added functions that has real business use cases, such as:
/api/product/margin/which would calculate the basic profit margin of each product (product price minus sum of all ingredients' price)/api/product/stock/which would calculate how many more servings of each product the restaurant has left, based on the amount of the product's ingredient
- Organize
Product/Ingredientjson output to not include the join table (Product_Ingredient) - Build the front-end (Angular, React) to interface with the API
- Get unit test (mocha, chai-http) working fully