Skip to content

Commit 63f1b16

Browse files
committed
initial_push
1 parent 573bb1e commit 63f1b16

File tree

1,736 files changed

+145513
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,736 files changed

+145513
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.pyc
2+
/__pycache__
3+
.env
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Invoice-Billing-System
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
from flask import Flask, render_template, request, redirect, abort, flash, session ,url_for
2+
from werkzeug.exceptions import HTTPException
3+
from firebase import Firebase
4+
import json
5+
from datetime import datetime
6+
from db import products_collection, invoices_collection, users_collection
7+
from helper import *
8+
import os
9+
10+
app = Flask(__name__)
11+
app.secret_key = os.environ['APP_SECRET']
12+
13+
config = {
14+
"apiKey": os.environ.get("FIREBASE_APIKEY"),
15+
"authDomain": os.environ.get("FIREBASE_AUTHDOMAIN"),
16+
"databaseURL": os.environ.get("FIREBASE_DATABASEURL"),
17+
"projectId": os.environ.get("FIREBASE_PROJECT_ID"),
18+
"storageBucket": os.environ.get("FIREBASE_STORAGE_BUCKET"),
19+
"messagingSenderId": os.environ.get("FIREBASE_MESSAGING_SENDER_ID"),
20+
"appId": os.environ.get("FIREBASE_APP_ID"),
21+
"measurementId": os.environ.get("FIREBASE_MEASUREMENT_ID")
22+
}
23+
24+
firebase = Firebase(config)
25+
db = firebase.database()
26+
auth = firebase.auth()
27+
28+
exempted_endpoints = ['signup','login','static']
29+
30+
@app.route("/signup", methods = ['GET','POST'])
31+
def signup():
32+
if request.method=='POST':
33+
name = request.form.get("name")
34+
username = request.form.get("email")
35+
password = request.form.get("password")
36+
repassword = request.form.get("repassword")
37+
user_details = {"name": name,"email": username}
38+
if password == repassword:
39+
if len(password)>=6:
40+
try:
41+
_user_ = auth.create_user_with_email_and_password(username ,password)
42+
auth.send_email_verification(_user_['idToken'])
43+
user_details['merchant_id'] = _user_['localId']
44+
users_collection.insert_one(user_details)
45+
return render_template("success.html")
46+
except Exception as e:
47+
return redirect(url_for('login'))
48+
else:
49+
flash('Password is less than 6 characters!')
50+
return redirect("/signup")
51+
else:
52+
flash('Both Passwords do not match!')
53+
return redirect("/signup")
54+
return render_template("signup.html")
55+
56+
@app.route("/login",methods = ['GET','POST'] )
57+
def login():
58+
if request.method == 'POST':
59+
data = dict(request.form)
60+
email = data.get("email")
61+
password = data.get("password")
62+
user_details = users_collection.find_one({"email": email},{"_id": 0})
63+
if user_details:
64+
user = auth.sign_in_with_email_and_password(email ,password)
65+
access_token = user['idToken']
66+
acc_info = auth.get_account_info(access_token)
67+
print(acc_info)
68+
if not acc_info.get("users")[0].get("emailVerified"):
69+
abort(500,{"message": f"{email} is not verified!"})
70+
user_details = users_collection.find_one({"email": email, "merchant_id": user['localId']},{"_id": 0})
71+
if user_details:
72+
session['user'] = user['localId']
73+
return redirect("/")
74+
else:
75+
abort(500,{"message": "Username or Password is Invalid!!"})
76+
77+
else:
78+
flash("User doesn't exist!")
79+
return redirect("/login")
80+
if 'user' in session:
81+
return redirect("/")
82+
return render_template("login.html")
83+
84+
85+
@app.route("/",methods = ['GET','POST'])
86+
def start():
87+
products = list(products_collection.find({"merchant_id": session.get('user')},{"_id":0,"created_on": 0}))
88+
return render_template("index.html",_products_=json.dumps(products))
89+
90+
@app.route("/checkout",methods = ['POST'])
91+
def checkout():
92+
bill_data = dict(request.form)
93+
dt_string = datetime.now()
94+
bill_data['created_on'] = dt_string
95+
bill_data['merchant_id'] = session.get('user')
96+
bill_data['selected_products'] = json.loads(bill_data['selected_products'])
97+
unavailable_items = []
98+
for product in bill_data['selected_products']:
99+
item_id = product['item_id']
100+
qty = int(product['qty'])
101+
product_details = products_collection.find_one({"merchant_id": session.get('user'),"item_id": item_id})
102+
current_stock = product_details.get("item_stock")
103+
item_name = product_details.get("name")
104+
if int(current_stock)<=int(qty):
105+
unavailable_items.append(item_name)
106+
if unavailable_items == []:
107+
for product in bill_data['selected_products']:
108+
item_id = product['item_id']
109+
qty = int(product['qty'])
110+
products_collection.update_one({"merchant_id":session.get('user'),"item_id":item_id},{"$inc":{"item_stock": -qty}})
111+
# return bill_data
112+
bill_id = random_id(10)
113+
if bill_data.get("payment_status") == "paid":
114+
bill_data['amount_to_be_collected'] = "0"
115+
bill_data['bill_id'] = bill_id
116+
invoices_collection.insert_one(bill_data)
117+
return redirect(f"/bill/{bill_id}")
118+
else:
119+
for item in unavailable_items:
120+
flash(f"{item} is out of stock!")
121+
return redirect("/")
122+
123+
@app.route("/bill/<string:bill_key>", methods=['GET','POST'])
124+
def bill(bill_key):
125+
if request.method == 'POST':
126+
incoming_updates = dict(request.form)
127+
if incoming_updates['payment_status'] == 'paid':
128+
incoming_updates['amount_to_be_collected'] = "0"
129+
incoming_updates['paid_amount'] = incoming_updates['checkout_amount'].replace(",",'')
130+
elif incoming_updates['payment_status'] == 'unpaid':
131+
incoming_updates['amount_to_be_collected'] = incoming_updates['checkout_amount'].replace(",",'')
132+
incoming_updates['paid_amount'] = "0"
133+
invoices_collection.update_one({"merchant_id":session.get('user'),"bill_id": bill_key},{"$set":incoming_updates})
134+
return redirect(f"/bill/{bill_key}")
135+
try:
136+
bill_details = invoices_collection.find_one({"merchant_id": session.get('user'),"bill_id": bill_key},{"_id":0,"created_on": 0})
137+
except:
138+
bill_details = None
139+
if bill_details is not None:
140+
return render_template("bill.html",bill_key=bill_key, bill_details=json.dumps(bill_details),bill_details_json=bill_details)
141+
else:
142+
abort(404,
143+
{
144+
"message": f"Bill with Bill ID '{bill_key}' doesn't exist"
145+
})
146+
147+
@app.route("/products", methods = ['GET','POST'])
148+
def products():
149+
if request.method == 'POST':
150+
item_data = dict(request.form)
151+
item_data['item_stock'] = int(item_data['item_stock'])
152+
dt_string = datetime.now()
153+
item_data['created_on'] = dt_string
154+
item_data['merchant_id'] = session.get('user')
155+
item_data['item_id'] = random_id(10)
156+
products_collection.insert_one(item_data)
157+
return redirect("/products/0")
158+
return redirect('/products/0')
159+
160+
@app.route("/products/<int:page_index>", methods = ['GET','POST'])
161+
def products_idx(page_index):
162+
if request.method == "POST":
163+
query = request.form.get("search")
164+
page_size = 10
165+
pipeline = [{"$match":{"merchant_id": session.get('user')}},{"$project":{"_id":0}}]
166+
res= []
167+
products = list(products_collection.aggregate(pipeline))
168+
for product in products:
169+
if query in product.get("name").lower():
170+
res.append(product)
171+
return render_template("products.html",products=res,total_pages=0,page_size=page_size, page_index=0,merchant_id=session.get('user'), handle_catch=handle_catch)
172+
page_size = 10
173+
pipeline = [{"$match":{"merchant_id": session.get('user')}},{"$project":{"_id":0}},{'$skip': int(page_index)*page_size}, {'$limit': page_size}]
174+
products = products_collection.aggregate(pipeline)
175+
temp = len(list(products_collection.find({"merchant_id":session.get("user")},{"_id":0})))
176+
if temp%page_size == 0:
177+
total_pages = temp//page_size
178+
else:
179+
total_pages = temp//page_size + 1
180+
181+
return render_template("products.html",products=products,total_pages=total_pages,page_size=page_size, page_index=page_index,merchant_id=session.get('user'), handle_catch=handle_catch)
182+
183+
@app.route("/products/<string:merchant_id>/<string:item_id>/<string:page_index>", methods = ['POST'])
184+
def products_update(merchant_id, item_id,page_index):
185+
item_data = dict(request.form)
186+
item_data['item_stock'] = int(item_data['item_stock'] )
187+
products_collection.update_one({"item_id": item_id,"merchant_id": merchant_id},{"$set":item_data})
188+
return redirect(f"/products/{page_index}")
189+
190+
@app.route("/products_delete/<string:merchant_id>/<string:item_id>/<string:page_index>", methods = ['POST'])
191+
def products_delete(merchant_id, item_id,page_index):
192+
products_collection.delete_one({"item_id": item_id,"merchant_id": merchant_id})
193+
return redirect(f"/products/{page_index}")
194+
195+
@app.route("/bills", methods = ['GET','POST'])
196+
def bills_temp():
197+
return redirect("/bills/0")
198+
199+
@app.route("/bills/<int:page_index>", methods = ['GET','POST'])
200+
def bills(page_index):
201+
if request.method == "POST":
202+
payment_status_filter = str(request.form.get("payment_status"))
203+
page_size = 10
204+
if payment_status_filter!="":
205+
pipeline = [{"$match":{"merchant_id": session.get('user'),"payment_status":payment_status_filter}},{"$project":{"_id":0}}]
206+
else:
207+
return redirect("/bills/0")
208+
all_bills = list(invoices_collection.aggregate(pipeline))
209+
return render_template("bills.html",all_bills=all_bills,total_pages=0,page_size=page_size, page_index=0,merchant_id=session.get('user'), handle_catch=handle_catch)
210+
page_size = 10
211+
pipeline = [{"$match":{"merchant_id": session.get('user')}},{"$project":{"_id":0}},{'$skip': int(page_index)*page_size}, {'$limit': page_size}]
212+
all_bills = invoices_collection.aggregate(pipeline)
213+
temp = len(list(invoices_collection.find({"merchant_id":session.get("user")},{"_id":0})))
214+
if temp%page_size == 0:
215+
total_pages = temp//page_size
216+
else:
217+
total_pages = temp//page_size +1
218+
219+
# all_bills = invoices_collection.find({"merchant_id":session.get('user')},{"_id":0})
220+
return render_template("bills.html", all_bills=all_bills,total_pages=total_pages,page_index=page_index, page_size=page_size,handle_catch=handle_catch)
221+
222+
@app.route("/logout", methods = ['GET','POST'])
223+
def logout():
224+
session.pop('user')
225+
return redirect("/login")
226+
227+
@app.before_request
228+
def before_request_func():
229+
if request.endpoint in exempted_endpoints:
230+
return
231+
if 'user' not in session:
232+
return redirect(url_for('login'))
233+
234+
235+
if __name__ == '__main__':
236+
app.run(debug=True, port=5000)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import os
2+
import pymongo
3+
4+
ENVIRONMENT = os.environ["ENVIRONMENT"]
5+
if ENVIRONMENT == "local":
6+
connection_string = "mongodb://localhost:27017"
7+
DB_NAME = "billing_system"
8+
else:
9+
MONGO_CLUSTER = os.environ["MONGO_URI"]
10+
MONGO_USERNAME = os.environ["MONGO_USERNAME"]
11+
MONGO_PASSWORD = os.environ["MONGO_PASSWORD"]
12+
DB_NAME = os.environ["DB_NAME"]
13+
connection_string = f"mongodb+srv://{MONGO_USERNAME}:{MONGO_PASSWORD}@{MONGO_CLUSTER}/?retryWrites=true&ssl=true&ssl_cert_reqs=CERT_NONE&w=majority"
14+
15+
16+
db_client = pymongo.MongoClient(connection_string)
17+
db_client = db_client.get_database(DB_NAME)
18+
19+
products_collection = db_client['products']
20+
invoices_collection = db_client['invoices']
21+
users_collection = db_client['users']
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import string, random
2+
3+
def random_id(N):
4+
res = ''.join(random.choices(string.ascii_lowercase +
5+
string.ascii_uppercase, k=N))
6+
return res
7+
8+
def handle_catch(caller, on_exception):
9+
try:
10+
return caller()
11+
except:
12+
return on_exception
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Flask==2.0.1
2+
pymongo[srv]==4.3.3
3+
firebase==3.0.1
4+
python-jwt==4.0.0
5+
gcloud==0.18.3
6+
sseclient==0.0.27
7+
pycryptodome==3.18.0
8+
requests-toolbelt==0.10.1
9+
urllib3==1.26.15
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
source .env
2+
python app.py

FLASK PROJECTS/Inventory Billing Management Suystem using Flask/static/css/bootstrap.min.css

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
html{
2+
height: 100%;
3+
width: 100%;
4+
overflow-x: hidden;
5+
}
6+
7+
main {
8+
padding: 1em 0;
9+
}
10+
11+
main * {
12+
font-family: Comic Sans MS;
13+
}
14+
15+
#project-title {
16+
text-shadow: 3px 3px 7px #000;
17+
padding: 0.1em 0.1em !important;
18+
color: white;
19+
font-size: 3em;
20+
padding-bottom: 0.5em !important;
21+
}
22+
23+
24+
/* Loader */
25+
26+
#pre-loader {
27+
position: absolute;
28+
width: 100%;
29+
height: 100%;
30+
top: 0;
31+
left: 0;
32+
backdrop-filter: brightness(.5);
33+
display: flex;
34+
align-items: center;
35+
justify-content: center;
36+
z-index: 99;
37+
}
38+
39+
.lds-hourglass {
40+
display: inline-block;
41+
position: relative;
42+
width: 80px;
43+
height: 80px;
44+
}
45+
46+
.lds-hourglass:after {
47+
content: " ";
48+
display: block;
49+
border-radius: 50%;
50+
width: 0;
51+
height: 0;
52+
margin: 8px;
53+
box-sizing: border-box;
54+
border: 32px solid #fff;
55+
border-color: #fff transparent #fff transparent;
56+
animation: lds-hourglass 1.2s infinite;
57+
}
58+
59+
@keyframes lds-hourglass {
60+
0% {
61+
transform: rotate(0);
62+
animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
63+
}
64+
50% {
65+
transform: rotate(900deg);
66+
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
67+
}
68+
100% {
69+
transform: rotate(1800deg);
70+
}
71+
}
72+
73+
#product-result {
74+
max-height: 43vh;
75+
z-index: 2;
76+
}
77+
78+
#product-result:empty:after {
79+
content: "No Result";
80+
width: 100%;
81+
text-align: center;
82+
}

0 commit comments

Comments
 (0)