0% found this document useful (0 votes)
37 views9 pages

Simple Pos System (React + Node + Sqlite)

This document describes a minimal Point of Sale (POS) system project built with Node.js, Express, and SQLite for the backend, and React for the frontend. It includes features like product listing, cart management, checkout functionality, and stock adjustments, along with a project structure and instructions for running the application locally. Future improvements suggested include user authentication, receipt printing, and support for multiple database systems.

Uploaded by

richard.claritoi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views9 pages

Simple Pos System (React + Node + Sqlite)

This document describes a minimal Point of Sale (POS) system project built with Node.js, Express, and SQLite for the backend, and React for the frontend. It includes features like product listing, cart management, checkout functionality, and stock adjustments, along with a project structure and instructions for running the application locally. Future improvements suggested include user authentication, receipt printing, and support for multiple database systems.

Uploaded by

richard.claritoi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Simple POS System — Full project

This repository is a minimal, ready-to-run Point of Sale (POS) system built


with [Link] + Express + SQLite for the backend and a compact React
frontend. It is intended as a starting point you can run locally and extend.

Features
 Product listing (CRUD endpoints in the API)
 Add products to cart on the front end
 Simple checkout that records a sale and sale_items in SQLite
 Stock quantity adjustment on checkout
 Basic search and quantity controls

Project structure (single-folder quick-start)


pos-project/
├─ server/
│ ├─ [Link]
│ ├─ [Link]
│ ├─ db_init.sql
│ └─ [Link] (created at runtime)
├─ client/
│ ├─ [Link]
│ ├─ public/[Link]
│ └─ src/[Link]
└─ [Link]

Backend — server/[Link]
// server/[Link]
const express = require('express');
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const bodyParser = require('body-parser');

const DB_PATH = [Link](__dirname, '[Link]');


const db = new [Link](DB_PATH);

const app = express();


[Link]([Link]());

// Allow CORS for local dev


[Link]((req, res, next)=>{
[Link]('Access-Control-Allow-Origin', '*');
[Link]('Access-Control-Allow-Methods',
'GET,POST,PUT,DELETE,OPTIONS');
[Link]('Access-Control-Allow-Headers', 'Content-Type');
next();
});

// Initialize tables if not exist


const initSql = `
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sku TEXT UNIQUE,
name TEXT NOT NULL,
price REAL NOT NULL DEFAULT 0,
stock INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS sales (


id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TEXT DEFAULT (datetime('now','localtime')),
total REAL NOT NULL
);

CREATE TABLE IF NOT EXISTS sale_items (


id INTEGER PRIMARY KEY AUTOINCREMENT,
sale_id INTEGER,
product_id INTEGER,
qty INTEGER,
price REAL,
FOREIGN KEY (sale_id) REFERENCES sales(id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
`;

[Link](initSql, (err)=>{
if(err) [Link]('DB init error', err);
});

// Seed sample products if empty


[Link]('SELECT COUNT(*) as c FROM products', (err, row) => {
if(!err && row.c === 0){
const stmt = [Link]('INSERT INTO products
(sku,name,price,stock) VALUES (?,?,?,?)');
const sample = [
['P001','Plain Coffee',2.5,50],
['P002','Blue Bottle',5.0,30],
['P003','Sandwich',3.75,20]
];
for(const s of sample) [Link](s);
[Link]();
[Link]('Seeded sample products');
}
});

// API: GET /api/products


[Link]('/api/products', (req, res) => {
const q = [Link].q || '';
const sql = `SELECT * FROM products WHERE name LIKE ? OR sku LIKE ?
ORDER BY name`;
[Link](sql, [`%${q}%`,`%${q}%`], (err, rows)=>{
if(err) return [Link](500).json({error: [Link]});
[Link](rows);
});
});

// API: POST /api/products (create)


[Link]('/api/products', (req, res) => {
const {sku, name, price, stock} = [Link];
const sql = 'INSERT INTO products (sku,name,price,stock) VALUES
(?,?,?,?)';
[Link](sql, [sku,name,price,stock], function(err){
if(err) return [Link](400).json({error: [Link]});
[Link]('SELECT * FROM products WHERE id=?', [[Link]],
(e,row)=>{
[Link](row);
});
});
});

// API: PUT /api/products/:id (update)


[Link]('/api/products/:id', (req,res)=>{
const id = [Link];
const {sku,name,price,stock} = [Link];
[Link]('UPDATE products SET sku=?,name=?,price=?,stock=? WHERE
id=?', [sku,name,price,stock,id], function(err){
if(err) return [Link](400).json({error: [Link]});
[Link]('SELECT * FROM products WHERE id=?', [id],
(e,row)=>[Link](row));
});
});

// API: POST /api/checkout


// body: { items: [{product_id, qty, price}], total }
[Link]('/api/checkout', (req,res)=>{
const {items, total} = [Link];
if(!items || ![Link](items) || [Link]===0) return
[Link](400).json({error:'No items'});
[Link](()=>{
[Link]('BEGIN TRANSACTION');
[Link]('INSERT INTO sales (total) VALUES (?)', [total],
function(err){
if(err){ [Link]('ROLLBACK'); return
[Link](500).json({error:[Link]}); }
const saleId = [Link];
const insertItem = [Link]('INSERT INTO sale_items
(sale_id,product_id,qty,price) VALUES (?,?,?,?)');
const updateStock = [Link]('UPDATE products SET stock =
stock - ? WHERE id = ?');

for(const it of items){
[Link]([saleId, it.product_id, [Link], [Link]]);
[Link]([[Link], it.product_id]);
}
[Link]();
[Link]();
[Link]('COMMIT', (cErr)=>{
if(cErr){ [Link]('ROLLBACK'); return
[Link](500).json({error:[Link]}); }
[Link]({sale_id: saleId});
});
});
});
});

// API: GET /api/sales (list recent sales)


[Link]('/api/sales', (req,res)=>{
[Link]('SELECT * FROM sales ORDER BY created_at DESC LIMIT 50',
(err,rows)=>{
if(err) return [Link](500).json({error:[Link]});
[Link](rows);
});
});

// Start server
const PORT = [Link] || 4000;
[Link](PORT, ()=>[Link]('Server listening on', PORT));

DB init SQL (server/db_init.sql)


-- db_init.sql
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sku TEXT UNIQUE,
name TEXT NOT NULL,
price REAL NOT NULL DEFAULT 0,
stock INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS sales (


id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TEXT DEFAULT (datetime('now','localtime')),
total REAL NOT NULL
);

CREATE TABLE IF NOT EXISTS sale_items (


id INTEGER PRIMARY KEY AUTOINCREMENT,
sale_id INTEGER,
product_id INTEGER,
qty INTEGER,
price REAL
);

Backend [Link] (server/[Link])


{
"name": "pos-server",
"version": "1.0.0",
"main": "[Link]",
"scripts": {
"start": "node [Link]"
},
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2",
"sqlite3": "^5.1.6"
}
}

Frontend — React single-file client/src/[Link]


// client/src/[Link]
import React, {useEffect, useState} from 'react';

const API = (path)=>`[Link]

export default function App(){


const [products,setProducts] = useState([]);
const [cart,setCart] = useState([]);
const [q,setQ] = useState('');

useEffect(()=>{ fetchProducts(); }, []);


function fetchProducts(){
fetch(API('products?q=' + encodeURIComponent(q)))
.then(r=>[Link]()).then(setProducts).catch([Link]);
}

function addToCart(p){
const existing = [Link](c=>c.product_id===[Link]);
if(existing){
setCart([Link](c=> c.product_id===[Link] ? {...c, qty:
[Link]([Link], [Link]+1)} : c));
} else {
setCart([...cart, {product_id: [Link], name: [Link], price:
[Link], qty: 1, stock: [Link]}]);
}
}

function changeQty(product_id, qty){


setCart([Link](c=> c.product_id===product_id ? {...c, qty:
[Link](1, [Link]([Link], qty))} : c));
}

function removeItem(product_id){
setCart([Link](c=>c.product_id!==product_id)); }

function total(){ return [Link]((s,i)=> s + [Link] * [Link],


0).toFixed(2); }

function checkout(){
const items = [Link](i=> ({product_id: i.product_id, qty: [Link],
price: [Link]}));
fetch(API('checkout'), {
method: 'POST', headers: {'Content-Type':'application/json'},
body: [Link]({items, total: parseFloat(total())})
}).then(r=>[Link]()).then((res)=>{
alert('Sale recorded — ID: ' + res.sale_id);
setCart([]);
fetchProducts();
}).catch(err=>{ [Link](err); alert('Checkout failed'); });
}

return (
<div style={{fontFamily:'Arial', padding:20}}>
<h1>Simple POS</h1>

<div style={{display:'flex',gap:20}}>
<div style={{flex:2}}>
<div style={{marginBottom:10}}>
<input placeholder="Search products" value={q}
onChange={e=>setQ([Link])} />
<button onClick={fetchProducts}>Search</button>
</div>

<div>
<h3>Products</h3>
<table style={{width:'100%', borderCollapse:'collapse'}}>
<thead>

<tr><th>Name</th><th>Price</th><th>Stock</th><th></th></tr>
</thead>
<tbody>
{[Link](p=> (
<tr key={[Link]}>
<td>{[Link]}</td>
<td>{[Link](2)}</td>
<td>{[Link]}</td>
<td><button disabled={[Link]<=0}
onClick={()=>addToCart(p)}>Add</button></td>
</tr>
))}
</tbody>
</table>
</div>
</div>

<div style={{flex:1, borderLeft:'1px solid #ddd',


paddingLeft:20}}>
<h3>Cart</h3>
{[Link]===0 ? <p>Cart empty</p> : (
<div>
{[Link](i=> (
<div key={i.product_id}
style={{display:'flex',justifyContent:'space-between',
marginBottom:8}}>
<div style={{flex:1}}>{[Link]} x
<input style={{width:50, marginLeft:8}}
type="number" value={[Link]} min={1} max={[Link]}
onChange={e=>changeQty(i.product_id,
parseInt([Link]||1))} />
</div>
<div style={{width:120, textAlign:'right'}}>
{([Link] * [Link]).toFixed(2)}
<button style={{marginLeft:8}}
onClick={()=>removeItem(i.product_id)}>Remove</button>
</div>
</div>
))}

<div style={{borderTop:'1px solid #eee', paddingTop:10,


marginTop:10}}>
<strong>Total: ₱{total()}</strong>
<div style={{marginTop:10}}>
<button onClick={checkout}>Checkout</button>
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
}

Frontend client/[Link]
{
"name": "pos-client",
"version": "1.0.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vite": "^5.0.0"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"start": "vite preview --port 3000"
}
}

public/[Link] should be a minimal HTML wrapper for the Vite app.

How to run (local dev)


1. Clone/copy the project into pos-project.

2. Start backend:

cd pos-project/server
npm install
node [Link]
# or: npm start

server listens on port 4000

3. Start frontend:
cd pos-project/client
npm install
npm run dev

open [Link] (Vite default) or the address shown in


terminal.

Next steps & improvements


 Add user authentication (cashier accounts)
 Add receipts printing or PDF export
 Add payments (cash, card) and payment records
 Add product categories, discounts, inventory reports
 Add barcode scanning support (use device camera or USB scanner)
 Replace SQLite with PostgreSQL or MySQL for multi-user deployment
 Add real-time updates with WebSockets if multiple terminals are used

If you’d like, I can:


 produce a ZIP of the project you can download,
 scaffold this as a GitHub repo with README and commit-ready files,
 add user authentication and role permissions,
 wire up receipts or a printable sales invoice.
Tell me which of those you’d like and I’ll modify the project accordingly.

You might also like