AhmadYarAI commited on
Commit
6153aab
·
1 Parent(s): 2dd49f5

feat: add invoice creation logic with SQLAlchemy

Browse files
Files changed (4) hide show
  1. api/invoices.py +55 -0
  2. core/database.py +21 -0
  3. main.py +17 -23
  4. models/invoice.py +29 -0
api/invoices.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Depends, HTTPException
2
+ from sqlalchemy.orm import Session
3
+ from core.database import get_db
4
+ from models.invoice import Invoice, InvoiceItem
5
+ from pydantic import BaseModel
6
+ from typing import List
7
+
8
+ router = APIRouter(prefix="/invoices", tags=["Invoices"])
9
+
10
+ # Pydantic Schemas for validation
11
+ class ItemCreate(BaseModel):
12
+ description: str
13
+ quantity: int
14
+ price_per_unit: float
15
+
16
+ class InvoiceCreate(BaseModel):
17
+ invoice_no: str
18
+ doctor_name: str
19
+ clinic_name: str
20
+ patient_name: str
21
+ shade: str
22
+ received_amount: float
23
+ items: List[ItemCreate]
24
+
25
+ @router.post("/")
26
+ def create_invoice(data: InvoiceCreate, db: Session = Depends(get_db)):
27
+ # Calculate totals
28
+ total = sum(item.quantity * item.price_per_unit for item in data.items)
29
+
30
+ new_invoice = Invoice(
31
+ invoice_no=data.invoice_no,
32
+ doctor_name=data.doctor_name,
33
+ clinic_name=data.clinic_name,
34
+ patient_name=data.patient_name,
35
+ shade=data.shade,
36
+ total_amount=total,
37
+ received_amount=data.received_amount,
38
+ remaining_balance=total - data.received_amount
39
+ )
40
+
41
+ db.add(new_invoice)
42
+ db.flush() # Gets the ID without committing yet
43
+
44
+ for item in data.items:
45
+ db_item = InvoiceItem(
46
+ invoice_id=new_invoice.id,
47
+ description=item.description,
48
+ quantity=item.quantity,
49
+ price_per_unit=item.price_per_unit,
50
+ total_price=item.quantity * item.price_per_unit
51
+ )
52
+ db.add(db_item)
53
+
54
+ db.commit()
55
+ return {"status": "success", "invoice_id": new_invoice.id}
core/database.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from sqlalchemy import create_engine
3
+ from sqlalchemy.ext.declarative import declarative_base
4
+ from sqlalchemy.orm import sessionmaker
5
+ from dotenv import load_dotenv
6
+
7
+ load_dotenv()
8
+
9
+ DATABASE_URL = os.getenv("DATABASE_URL")
10
+
11
+ engine = create_engine(DATABASE_URL)
12
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
13
+ Base = declarative_base()
14
+
15
+ # Dependency to get DB session
16
+ def get_db():
17
+ db = SessionLocal()
18
+ try:
19
+ yield db
20
+ finally:
21
+ db.close()
main.py CHANGED
@@ -1,29 +1,23 @@
1
- import os
2
  from fastapi import FastAPI
3
- from sqlalchemy import create_engine
4
- from sqlalchemy.orm import sessionmaker
5
- from dotenv import load_dotenv
6
- from fastapi.responses import RedirectResponse
7
 
8
- # This will pick up the secret you saved in Hugging Face
9
- load_dotenv()
10
 
11
- DATABASE_URL = os.getenv("DATABASE_URL")
12
 
13
- # Create the SQLAlchemy engine using the modern psycopg driver
14
- engine = create_engine(DATABASE_URL)
15
- SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
 
 
 
16
 
17
- app = FastAPI()
 
18
 
19
- @app.get("/", include_in_schema=False)
20
- def root():
21
- # This automatically moves the user from / to /docs
22
- return RedirectResponse(url="/docs")
23
-
24
- @app.get("/health")
25
- def health_check():
26
- # This checks if the URL was loaded correctly from the secrets
27
- if DATABASE_URL:
28
- return {"status": "Database URL is loaded"}
29
- return {"status": "Error: DATABASE_URL not found"}
 
 
1
  from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from core.database import engine, Base
4
+ from api.invoices import router as invoice_router
 
5
 
6
+ # Create tables in Neon
7
+ Base.metadata.create_all(bind=engine)
8
 
9
+ app = FastAPI(title="SmiloCAD Invoice API")
10
 
11
+ app.add_middleware(
12
+ CORSMiddleware,
13
+ allow_origins=["*"],
14
+ allow_methods=["*"],
15
+ allow_headers=["*"],
16
+ )
17
 
18
+ # Include the routes from the api folder
19
+ app.include_router(invoice_router)
20
 
21
+ @app.get("/")
22
+ def home():
23
+ return {"message": "Welcome to SmiloCAD API"}
 
 
 
 
 
 
 
 
models/invoice.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey
2
+ from sqlalchemy.orm import relationship
3
+ from datetime import datetime
4
+ from core.database import Base # We will create this next
5
+
6
+ class Invoice(Base):
7
+ __tablename__ = "invoices"
8
+ id = Column(Integer, primary_key=True, index=True)
9
+ invoice_no = Column(String, unique=True, index=True)
10
+ doctor_name = Column(String)
11
+ clinic_name = Column(String)
12
+ patient_name = Column(String)
13
+ shade = Column(String)
14
+ total_amount = Column(Float)
15
+ received_amount = Column(Float)
16
+ remaining_balance = Column(Float)
17
+
18
+ items = relationship("InvoiceItem", back_populates="invoice", cascade="all, delete-orphan")
19
+
20
+ class InvoiceItem(Base):
21
+ __tablename__ = "invoice_items"
22
+ id = Column(Integer, primary_key=True, index=True)
23
+ invoice_id = Column(Integer, ForeignKey("invoices.id"))
24
+ description = Column(String)
25
+ quantity = Column(Integer)
26
+ price_per_unit = Column(Float)
27
+ total_price = Column(Float)
28
+
29
+ invoice = relationship("Invoice", back_populates="items")