Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 0 additions & 9 deletions backend/.env

This file was deleted.

46 changes: 43 additions & 3 deletions backend/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
import express from "express";
//Express is the web framework that handles HTTP
import express from "express";
// API testing
import cors from "cors";

const app = express();
// Billing routes
import billingRoutes from "./modules/billing/billing.routes.js";

export default app;
/* creates a basic node.js web server
uses the expres faramework to respond
with a status message when a specific
web address is visited*/

const app = express();
// creates the application , foundation (as the app
//object(var) is used to configure routing, middle ware,server settings)

app.use(express.json());
// enables json parsing, hadles the incoming json data in the body of HTTP request
//converts into a readable JAVASCRIPT OBJECT

app.use(
cors({
//dev origin
origin:"http://localhost:5173"
})
);
// Simple health check
// GET http://localhost:5000/api/v1/health
app.get("/api/v1/health",(req,res)=>{res.json(
{
ok : true
})
//sends the response, returns the data with a success code 200
});
// defines a route, for health check puropses,
// callback function (request(info by the user),
// repsone(send data back to the user))=>{}

app.use("/api/v1/billing", billingRoutes);
// Mounting billing route under /api/v1/billing/items
// So router.get("/items") becomes GET /api/v1/billing/items

export default app;
//export app, server.js would call app.listen()
97 changes: 97 additions & 0 deletions backend/modules/billing/billing.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
>>>READS THE REQUEST
>>> RESPONDS TEH REQUEST
>>> CALLS THE SPECIFICSERVICE BEING CALLED
>>>SEND JSON OR THE ERRPOS ENCOUNTERRED
*/
import { GetItemByCompanyId,CreateOrderForCompany} from "./billing.service.js";
// we are just importing the functio that is talking to the database
/*
* As We might be GET-ting the response i.e /api/v1/building/items
* There could be 3 things that teh controller function would do i.e
*Express always gives as
*req->incoming requests from the user,query,body
*res->object use to send the response
*next-> pass errors
*/
// list items is going to lists the items after retriving tem from postgresql
export async function listItems(req,res) {
// now we may call the getitemfunction but we have to validate the data retreived
//so we are going to validate using try catch
try{
// so the server/user wants to have access to the company id for that we will just declare a company id variable to store the requested id
const companyId=1; // hardcoded value else
// const companyId=req.user.company_id

// Call the service ad wait for the array of items
const items=await GetItemByCompanyId(companyId);

//200 :OK . meaning send json (server is working) to the frontend to parse
// We will gte some Item data from the DATABASE we are goig to save that data to ealiy route taht onto our FRONTEND
return res.status(200).json(
{ items:items,}
);
}
catch(error){
// we will log error as well
console.error("list Item error:",error);
// 500 -> server error
return res.status(500).json(
{
message:"Failed to fetch the menu items",
}
);
}
}



export async function createOrder(req,res){


try{
// const companyId=req.user.company_id;
const companyId=1; // cause we hardcoded the value of company id

// Read cart + user-decided values from the request body
const {items,payment_method,tax_rate,discount_rate,extra_charge}=req.body;
//Validate the cart
if(!items || !Array.isArray(items) || items.length===0){
return res.status(400).json({message:"Cart is empty or invalid"});
}

// Pass the user's tax/discount/add-on choices to the service
const result=await CreateOrderForCompany(companyId,items,{
taxRate:tax_rate,
discountRate:discount_rate,
extraCharge:extra_charge,
paymentMethod:payment_method,
});
// Return the full breakdown to the client
return res.status(201).json({
order_id:result.order_id,
subtotal:result.subtotal,
discount_rate:result.discount_rate,
discount_amount:result.discount_amount,
tax_rate:result.tax_rate,
tax_amount:result.tax_amount,
extra_charge:result.extra_charge,
total:result.total,
payment_method:result.payment_method,
items:result.order_lines,
});
//201-> created successfully
}
catch(error){
console.error("create order error:",error);
// Match the exact messages thrown by the service
if (
error.message === "Cart is Empty!" ||
error.message === "Invalid Cart Item!" ||
error.message === "Items not found or do not belong to the company!"
) {
return res.status(400).json({ message: error.message }); // 400 = bad request
}
return res.status(500).json({ message: "Failed to create order" }); // 500 = server error
}
}
22 changes: 22 additions & 0 deletions backend/modules/billing/billing.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// routing i.e. connecting HTTP method and path to make the controller function
import { Router } from "express";

// Router lets us group the related routes in one file
import { listItems,createOrder } from "./billing.controller.js";


//we will import the controller function for this route
// created a constant variable for to create a mini-app router for billing endpoints
const router=Router();

// when someone sends GET request to get items of a specific company
// full path -- mounted on app.js
router.get("/items",listItems);

//POST request for adding orders i.e POST /api/v1/billing/order
//Process: When someone sends POST to "/orders" then
//we will simulatenouly createOrder function
router.post("/orders",createOrder);

// export so app.js can import and mount this router
export default router;
171 changes: 171 additions & 0 deletions backend/modules/billing/billing.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//run SQL and return rows.
import pool from "../../database.js";
//import the shared postgresql connection pool from database.js
// pool is the name that we will refre to pool in this project
/*GOAL:
* Fetch teh menu items from the DB that belong to one specific company
* Now to access the specific data of a company we eed to have access to
* comapnyId ----> provided my controller (a specific companies POS)
*/
// export async function name(params) { logic}
export async function GetItemByCompanyId(companyId) {
// this function would help us to pass the query in the postgres database
//we will only select the relevant data that we need
// we have defined a query constant variable
// query for selecting the items that the company have
const query=
`SELECT
id,name,price,type
FROM items
WHERE company_id = $1
ORDER BY name ASC`;
// $1 is a placeholder — pool.query replaces it safely with companyId (prevents SQL injection) //ordering ascending order
// now the query works as a SPARK for the database to initiate the reponse
const result = await pool.query(query,[companyId])
//awaits -->pauses until POSTGRES ANSWERES
//pool.query (sql,[values]) replaces placeholder with id safely 8-)
//pool saves the sql row into JSON FORMAT
// i.e result.rows is an array of object (you can take it as a dictionary as well)
/*[{ id:1,
name:"CHB",
price:"12.00",
type:"Full"},....]*/
return result.rows; // returns the array of object
}
/* AS now company has to make a sale,
for calculating a specific cart selected by one customer we will

cartItem=[{item_id:1,quantity:2...}]
options={ taxRate, discountRate, extraCharge, paymentMethod } — decided by the user */

export async function CreateOrderForCompany(companyId,cartItems,options={}){
// Read user-decided values; default to 0 / CASH if missing
const taxRate=Number(options.taxRate)||0; // percent
const discountRate=Number(options.discountRate)||0; // percent
const extraCharge=Number(options.extraCharge)||0; // Rs add-on
const paymentMethod=options.paymentMethod||"CASH";
if (!Array.isArray(cartItems)|| cartItems.length===0){
throw new Error("Cart is Empty!");
}
// if cart is empty we will get the error

for (const line of cartItems){
if(!line.item_id || !line.quantity || line.quantity<=0) {
throw new Error("Invalid Cart Item!")
}
}
// Check for valid IDS Items
//Extract all item ids from the cart
const itemIds=cartItems.map((line) => line.item_id);

//$1 id in the query is replaced by itemIds array
//$2 id in the query is replaced by companyId
// IN regex $1 and $2 are used to replace the placeholders
// in the query with the actual values
const itemQuery=`
SELECT id,name,price,type
FROM items
WHERE id=ANY($1)
AND company_id=$2`;
//query to find the items that are in the cart and belong to the company
const itemsResult=await pool.query(itemQuery,[itemIds,companyId]);
//awaits the query to be executed and returns the result
const database_items=itemsResult.rows;
//database_items is an array of objects
// key value pairs= [{id:1,name:"CHB",price:"12.00",type:"Full"},....]

if (database_items.length !== itemIds.length){
throw new Error("Items not found or do not belong to the company!");
}//If itemid=99 sent but it doesnt exist for the company, reject the order

const itemMap={}; // create a map to store the items by their id
for (const item of database_items){
itemMap[item.id]=item;//set property

// BUILDING A LOOKUP MAP {1:{id,name,price,type},2:{id,name,price,type}...}
}

// Calculate total price
let subtotal=0;
//building the order lines for the order
const orderLines= cartItems.map((line)=>{
const database_item=itemMap[line.item_id];//get verified item from the map
const lineTotal=Number(database_item.price)*line.quantity;// price*quantity
subtotal+=lineTotal;// add to running subtotal

return{
item_id:database_item.id,// accessing the id of the item from the database
// name:database_item.name,
// type:database_item.type,
quantity:line.quantity, // quantity of the item
price:Number(database_item.price),// price of one item -- converting the string to number
line_total:lineTotal,// total price for the line item
};
});
// Discount comes off the subtotal first
const discountAmount=Number((subtotal*(discountRate/100)).toFixed(2));
const taxableBase=subtotal-discountAmount;
// Tax applies to the amount left after discount
const tax=Number((taxableBase*(taxRate/100)).toFixed(2));
// Add-on charge is added on top of everything
const total=Number((taxableBase+tax+extraCharge).toFixed(2));

// Saving the order and order lines to the database
const client=await pool.connect();// get a client from the pool
try{
await client.query("BEGIN"); //Start a transaction
// Order Insert Query — store the full money breakdown
const orderInsertQuery=`
INSERT INTO orders
(company_id, subtotal, discount_rate, discount_amount, tax_rate, tax_amount, extra_charge, total, payment_method)
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id`;
const orderResult=await client.query(orderInsertQuery,[
companyId, subtotal, discountRate, discountAmount,
taxRate, tax, extraCharge, total, paymentMethod,
]);
const orderId=orderResult.rows[0].id;
// get the order id from the result (newly created order
// automatically created by the database)


for (const line of orderLines){
await client.query(
`INSERT INTO order_items(order_id, item_id, quantity) VALUES($1, $2,$3)`,
[orderId,line.item_id,line.quantity])

// .query(function,[values]) executes the query with the provided values
}
await client.query("COMMIT"); // Commit the transaction
//if all queries are successful, save changes to the database.
return{
order_id:orderId,
subtotal,
discount_rate:discountRate,
discount_amount:discountAmount,
tax_rate:taxRate,
tax_amount:tax,
extra_charge:extraCharge,
total,
payment_method:paymentMethod,
order_lines:orderLines,
};
}
catch(error)
{
// If any query fails, rollback the transactions cause we dont want to save partial data ;]
await client.query("ROLLBACK");
throw error; // rethrow the error
} finally
{
//Connecting to the database, storing the data,
//releasing the connection back to the pool for reuse.
client.release();
}
}




// in this file controllers( read,response to a request) are used to initiate the system
// This file is done:)
16 changes: 0 additions & 16 deletions backend/node_modules/.bin/cross-env

This file was deleted.

16 changes: 0 additions & 16 deletions backend/node_modules/.bin/cross-env-shell

This file was deleted.

Loading