pharmacy-erp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +34 -0
- package/LICENSE +21 -0
- package/README.md +479 -0
- package/backend/Dockerfile +44 -0
- package/backend/app/__init__.py +1 -0
- package/backend/app/core/__init__.py +1 -0
- package/backend/app/core/audit.py +33 -0
- package/backend/app/core/config.py +16 -0
- package/backend/app/core/database.py +26 -0
- package/backend/app/core/deps.py +49 -0
- package/backend/app/core/exceptions.py +43 -0
- package/backend/app/core/security.py +32 -0
- package/backend/app/main.py +54 -0
- package/backend/app/modules/__init__.py +0 -0
- package/backend/app/modules/admin/__init__.py +1 -0
- package/backend/app/modules/admin/models.py +15 -0
- package/backend/app/modules/admin/router.py +353 -0
- package/backend/app/modules/auth/__init__.py +0 -0
- package/backend/app/modules/auth/models.py +16 -0
- package/backend/app/modules/auth/router.py +73 -0
- package/backend/app/modules/auth/schemas.py +29 -0
- package/backend/app/modules/cash_sessions/__init__.py +0 -0
- package/backend/app/modules/cash_sessions/models.py +19 -0
- package/backend/app/modules/cash_sessions/router.py +85 -0
- package/backend/app/modules/cash_sessions/schemas.py +31 -0
- package/backend/app/modules/customers/__init__.py +0 -0
- package/backend/app/modules/customers/models.py +37 -0
- package/backend/app/modules/customers/router.py +296 -0
- package/backend/app/modules/customers/schemas.py +72 -0
- package/backend/app/modules/inventory/__init__.py +0 -0
- package/backend/app/modules/inventory/models.py +16 -0
- package/backend/app/modules/inventory/router.py +150 -0
- package/backend/app/modules/inventory/schemas.py +24 -0
- package/backend/app/modules/medicines/__init__.py +0 -0
- package/backend/app/modules/medicines/models.py +54 -0
- package/backend/app/modules/medicines/router.py +631 -0
- package/backend/app/modules/medicines/schemas.py +153 -0
- package/backend/app/modules/medicines/template_generator.py +118 -0
- package/backend/app/modules/notifications/__init__.py +1 -0
- package/backend/app/modules/notifications/models.py +15 -0
- package/backend/app/modules/notifications/router.py +89 -0
- package/backend/app/modules/notifications/schemas.py +22 -0
- package/backend/app/modules/organizations/__init__.py +0 -0
- package/backend/app/modules/organizations/models.py +16 -0
- package/backend/app/modules/organizations/router.py +49 -0
- package/backend/app/modules/organizations/schemas.py +31 -0
- package/backend/app/modules/reports/__init__.py +0 -0
- package/backend/app/modules/reports/router.py +409 -0
- package/backend/app/modules/roles/__init__.py +1 -0
- package/backend/app/modules/roles/models.py +43 -0
- package/backend/app/modules/roles/router.py +89 -0
- package/backend/app/modules/roles/schemas.py +55 -0
- package/backend/app/modules/sales/__init__.py +0 -0
- package/backend/app/modules/sales/models.py +44 -0
- package/backend/app/modules/sales/router.py +63 -0
- package/backend/app/modules/sales/schemas.py +42 -0
- package/backend/app/modules/sales/service.py +128 -0
- package/backend/app/modules/suppliers/__init__.py +0 -0
- package/backend/app/modules/suppliers/models.py +14 -0
- package/backend/app/modules/suppliers/router.py +56 -0
- package/backend/app/modules/suppliers/schemas.py +27 -0
- package/backend/app/modules/users/__init__.py +0 -0
- package/backend/app/modules/users/router.py +74 -0
- package/backend/app/seed.py +275 -0
- package/backend/data/.gitkeep +1 -0
- package/backend/requirements.txt +17 -0
- package/bin/cli.js +288 -0
- package/docker-compose.dev.yml +42 -0
- package/docker-compose.yml +59 -0
- package/frontend/Dockerfile +56 -0
- package/frontend/next.config.ts +19 -0
- package/frontend/package.json +60 -0
- package/frontend/pnpm-lock.yaml +5678 -0
- package/frontend/pnpm-workspace.yaml +6 -0
- package/frontend/postcss.config.mjs +6 -0
- package/frontend/public/logo.png +0 -0
- package/frontend/src/app/[locale]/(app)/admin/page.tsx +1426 -0
- package/frontend/src/app/[locale]/(app)/catalog/products/[id]/page.tsx +505 -0
- package/frontend/src/app/[locale]/(app)/catalog/products/page.tsx +753 -0
- package/frontend/src/app/[locale]/(app)/customers/[id]/page.tsx +500 -0
- package/frontend/src/app/[locale]/(app)/customers/page.tsx +538 -0
- package/frontend/src/app/[locale]/(app)/dashboard/page.tsx +175 -0
- package/frontend/src/app/[locale]/(app)/inventory/page.tsx +765 -0
- package/frontend/src/app/[locale]/(app)/layout.tsx +60 -0
- package/frontend/src/app/[locale]/(app)/purchasing/page.tsx +1 -0
- package/frontend/src/app/[locale]/(app)/reports/page.tsx +794 -0
- package/frontend/src/app/[locale]/(app)/sales/page.tsx +296 -0
- package/frontend/src/app/[locale]/(auth)/login/page.tsx +100 -0
- package/frontend/src/app/[locale]/(pos)/layout.tsx +33 -0
- package/frontend/src/app/[locale]/(pos)/pos/page.tsx +463 -0
- package/frontend/src/app/[locale]/(pos)/pos/pos-data.ts +89 -0
- package/frontend/src/app/[locale]/layout.tsx +30 -0
- package/frontend/src/app/[locale]/page.tsx +6 -0
- package/frontend/src/app/globals.css +77 -0
- package/frontend/src/app/layout.tsx +23 -0
- package/frontend/src/app/print.css +18 -0
- package/frontend/src/components/pos/CartPanel.tsx +525 -0
- package/frontend/src/components/pos/CashSessionGuard.tsx +255 -0
- package/frontend/src/components/pos/CloseSessionModal.tsx +160 -0
- package/frontend/src/components/pos/PaymentModal.tsx +266 -0
- package/frontend/src/components/pos/PaymentSettingsModal.tsx +472 -0
- package/frontend/src/components/pos/ProductCatalog.tsx +140 -0
- package/frontend/src/components/pos/SaleReceipt.tsx +133 -0
- package/frontend/src/components/ui/badge.tsx +28 -0
- package/frontend/src/components/ui/bulk-import-modal.tsx +400 -0
- package/frontend/src/components/ui/button.tsx +60 -0
- package/frontend/src/components/ui/card.tsx +13 -0
- package/frontend/src/components/ui/confirm-dialog.tsx +50 -0
- package/frontend/src/components/ui/input.tsx +32 -0
- package/frontend/src/components/ui/notification-center.tsx +228 -0
- package/frontend/src/components/ui/select.tsx +59 -0
- package/frontend/src/components/ui/sidebar.tsx +91 -0
- package/frontend/src/components/ui/slide-over.tsx +43 -0
- package/frontend/src/components/ui/stat-card.tsx +40 -0
- package/frontend/src/components/ui/switch.tsx +38 -0
- package/frontend/src/components/ui/topbar.tsx +97 -0
- package/frontend/src/i18n/request.ts +13 -0
- package/frontend/src/i18n/routing.ts +9 -0
- package/frontend/src/lib/api.ts +240 -0
- package/frontend/src/lib/constants.ts +30 -0
- package/frontend/src/lib/utils.ts +32 -0
- package/frontend/src/messages/ar.json +106 -0
- package/frontend/src/messages/en.json +106 -0
- package/frontend/src/middleware.ts +8 -0
- package/frontend/src/store/app-store.ts +37 -0
- package/frontend/src/store/auth-store.ts +52 -0
- package/frontend/src/store/payment-methods-store.ts +144 -0
- package/frontend/src/store/pos-store.ts +344 -0
- package/frontend/tailwind.config.ts +85 -0
- package/frontend/tsconfig.json +27 -0
- package/img/backgraund-logo.png +0 -0
- package/img/full-logo.png +0 -0
- package/img/logo.png +0 -0
- package/nginx/nginx.conf +67 -0
- package/package.json +88 -0
- package/start.bat +90 -0
- package/start.sh +64 -0
package/.env.example
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# Pharmacy ERP & POS โ Environment Variables
|
|
3
|
+
# Copy this to .env and update values
|
|
4
|
+
# ============================================================
|
|
5
|
+
|
|
6
|
+
# Application
|
|
7
|
+
APP_NAME=Pharmacy ERP
|
|
8
|
+
APP_ENV=development
|
|
9
|
+
DEBUG=true
|
|
10
|
+
|
|
11
|
+
# Database
|
|
12
|
+
# For local development (SQLite โ no setup needed):
|
|
13
|
+
DATABASE_URL=sqlite+aiosqlite:///./data/pharmacy.db
|
|
14
|
+
|
|
15
|
+
# For production (PostgreSQL):
|
|
16
|
+
# DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/pharmacy_erp
|
|
17
|
+
|
|
18
|
+
# Security
|
|
19
|
+
SECRET_KEY=your-secret-key-change-this-in-production-min-32-chars
|
|
20
|
+
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
|
21
|
+
REFRESH_TOKEN_EXPIRE_DAYS=7
|
|
22
|
+
|
|
23
|
+
# CORS
|
|
24
|
+
CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
|
|
25
|
+
|
|
26
|
+
# Frontend
|
|
27
|
+
NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
|
|
28
|
+
|
|
29
|
+
# Redis (optional โ for production)
|
|
30
|
+
# REDIS_URL=redis://localhost:6379/0
|
|
31
|
+
|
|
32
|
+
# File Storage
|
|
33
|
+
UPLOAD_DIR=./data/uploads
|
|
34
|
+
MAX_UPLOAD_SIZE_MB=10
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pharmacy ERP Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="img/logo.png" alt="Pharmacy ERP Logo" width="120" />
|
|
4
|
+
|
|
5
|
+
# Pharmacy ERP & POS System
|
|
6
|
+
|
|
7
|
+
**A modern, open-source Pharmacy ERP & Point of Sale system built for independent pharmacies, chains, and hospitals.**
|
|
8
|
+
|
|
9
|
+
*Full Arabic (RTL) & English support ยท Designed for the Gulf/MENA market*
|
|
10
|
+
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://github.com/omersx/pharmacy-erp/actions/workflows/ci.yml)
|
|
13
|
+
[](https://hub.docker.com/u/omersx)
|
|
14
|
+
[](https://www.npmjs.com/package/pharmacy-erp)
|
|
15
|
+
[](https://python.org)
|
|
16
|
+
[](https://nodejs.org)
|
|
17
|
+
[](https://fastapi.tiangolo.com)
|
|
18
|
+
[](https://nextjs.org)
|
|
19
|
+
[](CONTRIBUTING.md)
|
|
20
|
+
|
|
21
|
+
[Features](#-features) ยท [Quick Start](#-quick-start) ยท [Screenshots](#-screenshots) ยท [Architecture](#-architecture) ยท [API Docs](#-api-documentation) ยท [Docker](#-docker-deployment) ยท [Contributing](#-contributing)
|
|
22
|
+
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## โจ Features
|
|
28
|
+
|
|
29
|
+
<table>
|
|
30
|
+
<tr>
|
|
31
|
+
<td width="50%">
|
|
32
|
+
|
|
33
|
+
### ๐ช Point of Sale
|
|
34
|
+
- Fullscreen, keyboard-first, touch-friendly POS
|
|
35
|
+
- Barcode/search product lookup
|
|
36
|
+
- Hold & recall sales
|
|
37
|
+
- Split payments (cash, card, credit)
|
|
38
|
+
- Receipt printing (PDF export)
|
|
39
|
+
|
|
40
|
+
</td>
|
|
41
|
+
<td width="50%">
|
|
42
|
+
|
|
43
|
+
### ๐ Medicine Management
|
|
44
|
+
- Complete product catalog with categories
|
|
45
|
+
- Batch tracking with expiry dates
|
|
46
|
+
- FEFO (First Expiry, First Out) allocation
|
|
47
|
+
- CSV/Excel bulk import
|
|
48
|
+
- Low stock & expiry alerts
|
|
49
|
+
|
|
50
|
+
</td>
|
|
51
|
+
</tr>
|
|
52
|
+
<tr>
|
|
53
|
+
<td width="50%">
|
|
54
|
+
|
|
55
|
+
### ๐ฆ Inventory & Purchasing
|
|
56
|
+
- Append-only inventory ledger
|
|
57
|
+
- Real-time stock levels per branch
|
|
58
|
+
- Purchase orders & goods receipt
|
|
59
|
+
- Supplier management & tracking
|
|
60
|
+
- Automated stock alerts
|
|
61
|
+
|
|
62
|
+
</td>
|
|
63
|
+
<td width="50%">
|
|
64
|
+
|
|
65
|
+
### ๐ฐ Sales & Cash Management
|
|
66
|
+
- Sales invoices & returns
|
|
67
|
+
- Cash session management (open/close)
|
|
68
|
+
- Opening float & blind close
|
|
69
|
+
- X-Report & Z-Report generation
|
|
70
|
+
- Customer credit tracking
|
|
71
|
+
|
|
72
|
+
</td>
|
|
73
|
+
</tr>
|
|
74
|
+
<tr>
|
|
75
|
+
<td width="50%">
|
|
76
|
+
|
|
77
|
+
### ๐ Reports & Analytics
|
|
78
|
+
- Sales reports (daily, weekly, monthly)
|
|
79
|
+
- Inventory valuation reports
|
|
80
|
+
- Profit & margin analysis
|
|
81
|
+
- Top-selling products
|
|
82
|
+
- Exportable to Excel/PDF
|
|
83
|
+
|
|
84
|
+
</td>
|
|
85
|
+
<td width="50%">
|
|
86
|
+
|
|
87
|
+
### ๐ Enterprise Features
|
|
88
|
+
- **Bilingual**: Full Arabic (RTL) + English
|
|
89
|
+
- **Dark/Light Theme**: Premium UI design
|
|
90
|
+
- **RBAC**: Role-based access control
|
|
91
|
+
- **Multi-branch**: Branch management
|
|
92
|
+
- **Responsive**: Desktop, tablet, & mobile
|
|
93
|
+
|
|
94
|
+
</td>
|
|
95
|
+
</tr>
|
|
96
|
+
</table>
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## ๐ธ Screenshots
|
|
101
|
+
|
|
102
|
+
> **Coming soon** โ Screenshots of the Dashboard, POS, Inventory, and Reports pages will be added here.
|
|
103
|
+
>
|
|
104
|
+
> Want to see the app in action? Follow the [Quick Start](#-quick-start) guide to run it locally in under 5 minutes!
|
|
105
|
+
|
|
106
|
+
<!-- Uncomment and add your screenshots:
|
|
107
|
+
<div align="center">
|
|
108
|
+
|
|
109
|
+
| Dashboard | Point of Sale |
|
|
110
|
+
|:---------:|:------------:|
|
|
111
|
+
|  |  |
|
|
112
|
+
|
|
113
|
+
| Inventory | Reports |
|
|
114
|
+
|:---------:|:-------:|
|
|
115
|
+
|  |  |
|
|
116
|
+
|
|
117
|
+
</div>
|
|
118
|
+
-->
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## ๐ Quick Start
|
|
123
|
+
|
|
124
|
+
### Prerequisites
|
|
125
|
+
|
|
126
|
+
| Tool | Version | Download |
|
|
127
|
+
|------|---------|----------|
|
|
128
|
+
| **Node.js** | 18+ | [nodejs.org](https://nodejs.org) |
|
|
129
|
+
| **pnpm** | 9+ | `npm install -g pnpm` |
|
|
130
|
+
| **Python** | 3.12+ | [python.org](https://python.org) |
|
|
131
|
+
|
|
132
|
+
### Option A: One-Command Setup
|
|
133
|
+
|
|
134
|
+
<details>
|
|
135
|
+
<summary><b>๐ช Windows</b></summary>
|
|
136
|
+
|
|
137
|
+
```batch
|
|
138
|
+
git clone https://github.com/omersx/pharmacy-erp.git
|
|
139
|
+
cd pharmacy-erp
|
|
140
|
+
start.bat
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
This will automatically:
|
|
144
|
+
1. Create the `.env` config file
|
|
145
|
+
2. Install all frontend & backend dependencies
|
|
146
|
+
3. Seed the database with demo data
|
|
147
|
+
4. Start both servers
|
|
148
|
+
|
|
149
|
+
</details>
|
|
150
|
+
|
|
151
|
+
<details>
|
|
152
|
+
<summary><b>๐ง Linux / ๐ macOS</b></summary>
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
git clone https://github.com/omersx/pharmacy-erp.git
|
|
156
|
+
cd pharmacy-erp
|
|
157
|
+
chmod +x start.sh
|
|
158
|
+
./start.sh
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
</details>
|
|
162
|
+
|
|
163
|
+
### Option B: Step-by-Step
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# 1. Clone the repository
|
|
167
|
+
git clone https://github.com/omersx/pharmacy-erp.git
|
|
168
|
+
cd pharmacy-erp
|
|
169
|
+
|
|
170
|
+
# 2. Install root dependencies (concurrently)
|
|
171
|
+
pnpm install
|
|
172
|
+
|
|
173
|
+
# 3. Setup frontend
|
|
174
|
+
pnpm run setup:frontend
|
|
175
|
+
|
|
176
|
+
# 4. Setup backend (auto-detects Windows/Linux/macOS)
|
|
177
|
+
pnpm run setup:backend
|
|
178
|
+
|
|
179
|
+
# 5. Configure environment
|
|
180
|
+
cp .env.example .env # Linux/macOS
|
|
181
|
+
# copy .env.example .env # Windows
|
|
182
|
+
|
|
183
|
+
# 6. Seed demo data
|
|
184
|
+
pnpm run seed
|
|
185
|
+
|
|
186
|
+
# 7. Start development servers
|
|
187
|
+
pnpm dev
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### ๐ You're Ready!
|
|
191
|
+
|
|
192
|
+
| Service | URL |
|
|
193
|
+
|---------|-----|
|
|
194
|
+
| ๐ **Frontend** | [http://localhost:3000](http://localhost:3000) |
|
|
195
|
+
| ๐ง **Backend API** | [http://localhost:8000](http://localhost:8000) |
|
|
196
|
+
| ๐ **API Docs (Swagger)** | [http://localhost:8000/docs](http://localhost:8000/docs) |
|
|
197
|
+
|
|
198
|
+
### Default Login
|
|
199
|
+
|
|
200
|
+
| Role | Email | Password |
|
|
201
|
+
|------|-------|----------|
|
|
202
|
+
| Super Admin | `admin@pharmacy.com` | `admin123` |
|
|
203
|
+
|
|
204
|
+
> โ ๏ธ **Change the default credentials** before deploying to production!
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## ๐๏ธ Architecture
|
|
209
|
+
|
|
210
|
+
```mermaid
|
|
211
|
+
graph TB
|
|
212
|
+
subgraph Client
|
|
213
|
+
A[Browser] -->|HTTP/HTTPS| B[Next.js Frontend]
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
subgraph Frontend["Frontend โ Next.js 15"]
|
|
217
|
+
B --> C[App Router]
|
|
218
|
+
C --> D[POS Module]
|
|
219
|
+
C --> E[ERP Dashboard]
|
|
220
|
+
C --> F[Auth Pages]
|
|
221
|
+
B --> G[Zustand Store]
|
|
222
|
+
B --> H[next-intl i18n]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
subgraph Backend["Backend โ FastAPI"]
|
|
226
|
+
I[API Gateway] --> J[Auth & RBAC]
|
|
227
|
+
I --> K[Medicines]
|
|
228
|
+
I --> L[Inventory]
|
|
229
|
+
I --> M[Sales & POS]
|
|
230
|
+
I --> N[Purchases]
|
|
231
|
+
I --> O[Customers]
|
|
232
|
+
I --> P[Cash Sessions]
|
|
233
|
+
I --> Q[Reports]
|
|
234
|
+
I --> R[Admin & Audit]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
subgraph Data["Data Layer"]
|
|
238
|
+
S[(SQLite / PostgreSQL)]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
B -->|REST API| I
|
|
242
|
+
J --> S
|
|
243
|
+
K --> S
|
|
244
|
+
L --> S
|
|
245
|
+
M --> S
|
|
246
|
+
N --> S
|
|
247
|
+
O --> S
|
|
248
|
+
P --> S
|
|
249
|
+
Q --> S
|
|
250
|
+
R --> S
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Project Structure
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
pharmacy-erp/
|
|
257
|
+
โโโ frontend/ # Next.js 15 (App Router)
|
|
258
|
+
โ โโโ src/
|
|
259
|
+
โ โ โโโ app/ # Routes & pages
|
|
260
|
+
โ โ โ โโโ [locale]/ # i18n routing (en, ar)
|
|
261
|
+
โ โ โ โโโ (app)/ # ERP dashboard pages
|
|
262
|
+
โ โ โ โโโ (auth)/ # Login / register
|
|
263
|
+
โ โ โ โโโ (pos)/ # Point of Sale
|
|
264
|
+
โ โ โโโ components/ # Reusable UI components
|
|
265
|
+
โ โ โ โโโ ui/ # Base components (Radix UI)
|
|
266
|
+
โ โ โ โโโ pos/ # POS-specific components
|
|
267
|
+
โ โ โโโ lib/ # API client, utilities
|
|
268
|
+
โ โ โโโ store/ # Zustand state stores
|
|
269
|
+
โ โ โโโ messages/ # i18n translations (en.json, ar.json)
|
|
270
|
+
โ โ โโโ i18n/ # i18n configuration
|
|
271
|
+
โ โโโ Dockerfile
|
|
272
|
+
โ โโโ package.json
|
|
273
|
+
โ
|
|
274
|
+
โโโ backend/ # FastAPI (Python 3.12)
|
|
275
|
+
โ โโโ app/
|
|
276
|
+
โ โ โโโ core/ # Config, database, security, exceptions
|
|
277
|
+
โ โ โโโ modules/ # Business logic modules
|
|
278
|
+
โ โ โ โโโ auth/ # Authentication & JWT
|
|
279
|
+
โ โ โ โโโ users/ # User management
|
|
280
|
+
โ โ โ โโโ roles/ # RBAC roles & permissions
|
|
281
|
+
โ โ โ โโโ organizations/# Branch management
|
|
282
|
+
โ โ โ โโโ medicines/ # Product catalog & batches
|
|
283
|
+
โ โ โ โโโ inventory/ # Stock movements & ledger
|
|
284
|
+
โ โ โ โโโ sales/ # Invoices, returns, held sales
|
|
285
|
+
โ โ โ โโโ purchases/ # Purchase orders
|
|
286
|
+
โ โ โ โโโ suppliers/ # Supplier management
|
|
287
|
+
โ โ โ โโโ customers/ # Customer & credit management
|
|
288
|
+
โ โ โ โโโ cash_sessions/# Cash register sessions
|
|
289
|
+
โ โ โ โโโ reports/ # Analytics & reporting
|
|
290
|
+
โ โ โ โโโ notifications/# System notifications
|
|
291
|
+
โ โ โ โโโ admin/ # Audit logs & admin tools
|
|
292
|
+
โ โ โโโ main.py # FastAPI application entry
|
|
293
|
+
โ โ โโโ seed.py # Demo data seeder
|
|
294
|
+
โ โโโ Dockerfile
|
|
295
|
+
โ โโโ requirements.txt
|
|
296
|
+
โ
|
|
297
|
+
โโโ nginx/ # Nginx reverse proxy config
|
|
298
|
+
โโโ img/ # Logo and brand assets
|
|
299
|
+
โโโ docker-compose.yml # Production deployment
|
|
300
|
+
โโโ package.json # Root orchestration scripts
|
|
301
|
+
โโโ start.bat # One-click setup (Windows)
|
|
302
|
+
โโโ start.sh # One-click setup (Linux/macOS)
|
|
303
|
+
โโโ .env.example # Environment template
|
|
304
|
+
โโโ README.md
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## ๐ฅ๏ธ Tech Stack
|
|
310
|
+
|
|
311
|
+
| Layer | Technologies |
|
|
312
|
+
|-------|-------------|
|
|
313
|
+
| **Frontend** | [Next.js 15](https://nextjs.org) ยท [React 19](https://react.dev) ยท [TypeScript](https://typescriptlang.org) ยท [Tailwind CSS](https://tailwindcss.com) |
|
|
314
|
+
| **UI Components** | [Radix UI](https://radix-ui.com) ยท [Lucide Icons](https://lucide.dev) ยท [Framer Motion](https://framer.com/motion) ยท [Recharts](https://recharts.org) |
|
|
315
|
+
| **State & i18n** | [Zustand](https://zustand-demo.pmnd.rs) ยท [next-intl](https://next-intl-docs.vercel.app) |
|
|
316
|
+
| **Backend** | [FastAPI](https://fastapi.tiangolo.com) ยท [Python 3.12](https://python.org) ยท [Pydantic v2](https://docs.pydantic.dev) ยท [SQLAlchemy 2](https://sqlalchemy.org) |
|
|
317
|
+
| **Database** | [SQLite](https://sqlite.org) (dev) ยท [PostgreSQL](https://postgresql.org) (production) |
|
|
318
|
+
| **Auth** | JWT (httpOnly cookies) ยท [Argon2](https://github.com/P-H-C/phc-winner-argon2) password hashing |
|
|
319
|
+
| **DevOps** | [Docker](https://docker.com) ยท [Nginx](https://nginx.org) ยท [Uvicorn](https://uvicorn.org) |
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## ๐ API Documentation
|
|
324
|
+
|
|
325
|
+
Once the backend is running, interactive API documentation is available at:
|
|
326
|
+
|
|
327
|
+
| Format | URL |
|
|
328
|
+
|--------|-----|
|
|
329
|
+
| **Swagger UI** | [http://localhost:8000/docs](http://localhost:8000/docs) |
|
|
330
|
+
| **ReDoc** | [http://localhost:8000/redoc](http://localhost:8000/redoc) |
|
|
331
|
+
|
|
332
|
+
### API Modules
|
|
333
|
+
|
|
334
|
+
| Endpoint Prefix | Module | Description |
|
|
335
|
+
|----------------|--------|-------------|
|
|
336
|
+
| `/api/v1/auth` | Authentication | Login, register, refresh tokens |
|
|
337
|
+
| `/api/v1/medicines` | Medicines | CRUD, batches, categories, import |
|
|
338
|
+
| `/api/v1/inventory` | Inventory | Stock movements, alerts, ledger |
|
|
339
|
+
| `/api/v1/sales` | Sales | Invoices, returns, held sales |
|
|
340
|
+
| `/api/v1/cash-sessions` | Cash Sessions | Open, close, X/Z reports |
|
|
341
|
+
| `/api/v1/customers` | Customers | Customer management, credit |
|
|
342
|
+
| `/api/v1/suppliers` | Suppliers | Supplier directory |
|
|
343
|
+
| `/api/v1/branches` | Branches | Multi-branch management |
|
|
344
|
+
| `/api/v1/reports` | Reports | Sales, inventory, profit analytics |
|
|
345
|
+
| `/api/v1/users` | Users | User management |
|
|
346
|
+
| `/api/v1/roles` | Roles | RBAC roles & permissions |
|
|
347
|
+
| `/api/v1/admin` | Admin | Audit logs, system admin |
|
|
348
|
+
| `/api/v1/notifications` | Notifications | System notifications |
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## ๐ณ Docker Deployment
|
|
353
|
+
|
|
354
|
+
### Quick Deploy (from Docker Hub)
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
# Pull pre-built images and start
|
|
358
|
+
docker compose up -d
|
|
359
|
+
|
|
360
|
+
# This starts:
|
|
361
|
+
# - FastAPI backend โ port 8000
|
|
362
|
+
# - Next.js frontend โ port 3000
|
|
363
|
+
# - Nginx proxy โ port 80
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Or pull images individually:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
docker pull omersx/pharmacy-erp-backend:latest
|
|
370
|
+
docker pull omersx/pharmacy-erp-frontend:latest
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Build from Source (for contributors)
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
docker compose -f docker-compose.dev.yml up --build
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Production Checklist
|
|
380
|
+
|
|
381
|
+
- [ ] Change `SECRET_KEY` to a strong random value (32+ characters)
|
|
382
|
+
- [ ] Change default admin credentials
|
|
383
|
+
- [ ] Set `APP_ENV=production` and `DEBUG=false`
|
|
384
|
+
- [ ] Switch database to PostgreSQL
|
|
385
|
+
- [ ] Configure proper `CORS_ORIGINS`
|
|
386
|
+
- [ ] Enable HTTPS via reverse proxy
|
|
387
|
+
- [ ] Set up automated backups
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## โ๏ธ Environment Variables
|
|
392
|
+
|
|
393
|
+
| Variable | Description | Default |
|
|
394
|
+
|----------|-------------|---------|
|
|
395
|
+
| `APP_NAME` | Application name | `Pharmacy ERP` |
|
|
396
|
+
| `APP_ENV` | Environment (`development` / `production`) | `development` |
|
|
397
|
+
| `DEBUG` | Enable debug mode | `true` |
|
|
398
|
+
| `DATABASE_URL` | Database connection string | `sqlite+aiosqlite:///./data/pharmacy.db` |
|
|
399
|
+
| `SECRET_KEY` | JWT signing key (change in production!) | `dev-secret-key-...` |
|
|
400
|
+
| `ACCESS_TOKEN_EXPIRE_MINUTES` | Access token TTL | `30` |
|
|
401
|
+
| `REFRESH_TOKEN_EXPIRE_DAYS` | Refresh token TTL | `7` |
|
|
402
|
+
| `CORS_ORIGINS` | Allowed CORS origins (comma-separated) | `http://localhost:3000` |
|
|
403
|
+
| `NEXT_PUBLIC_API_URL` | API URL for the frontend | `http://localhost:8000/api/v1` |
|
|
404
|
+
| `REDIS_URL` | Redis URL (optional, for production) | โ |
|
|
405
|
+
| `UPLOAD_DIR` | File upload directory | `./data/uploads` |
|
|
406
|
+
| `MAX_UPLOAD_SIZE_MB` | Max upload file size | `10` |
|
|
407
|
+
|
|
408
|
+
See [`.env.example`](.env.example) for the full template.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## ๐ Internationalization
|
|
413
|
+
|
|
414
|
+
Pharmacy ERP ships with complete support for:
|
|
415
|
+
|
|
416
|
+
| Language | Direction | Status |
|
|
417
|
+
|----------|-----------|--------|
|
|
418
|
+
| ๐ฌ๐ง English | LTR (Left-to-Right) | โ
Complete |
|
|
419
|
+
| ๐ธ๐ฆ ุงูุนุฑุจูุฉ (Arabic) | RTL (Right-to-Left) | โ
Complete |
|
|
420
|
+
|
|
421
|
+
Switch languages from any page โ the entire UI, including layout direction, updates instantly.
|
|
422
|
+
|
|
423
|
+
**Adding a new language:**
|
|
424
|
+
|
|
425
|
+
1. Create a new translation file: `frontend/src/messages/<locale>.json`
|
|
426
|
+
2. Add the locale to `frontend/src/i18n/` configuration
|
|
427
|
+
3. All strings are externalized โ no hardcoded text in components
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## ๐ค Contributing
|
|
432
|
+
|
|
433
|
+
We love contributions! Whether it's fixing bugs, adding features, improving docs, or suggesting ideas โ all contributions are welcome.
|
|
434
|
+
|
|
435
|
+
1. **Fork** the repository
|
|
436
|
+
2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
|
|
437
|
+
3. **Commit** your changes: `git commit -m 'feat: add amazing feature'`
|
|
438
|
+
4. **Push** to the branch: `git push origin feature/amazing-feature`
|
|
439
|
+
5. **Open** a Pull Request
|
|
440
|
+
|
|
441
|
+
Please read our [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) before getting started.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## ๐บ๏ธ Roadmap
|
|
446
|
+
|
|
447
|
+
- [ ] ๐ฑ Mobile app (React Native)
|
|
448
|
+
- [ ] ๐ Barcode scanner hardware integration
|
|
449
|
+
- [ ] ๐ Advanced analytics dashboard
|
|
450
|
+
- [ ] ๐ฅ Insurance claims module
|
|
451
|
+
- [ ] ๐ณ Payment gateway integration
|
|
452
|
+
- [ ] ๐ง Email notifications & alerts
|
|
453
|
+
- [ ] ๐ Real-time sync across branches
|
|
454
|
+
- [ ] ๐ Prescription management
|
|
455
|
+
- [ ] ๐งช Lab integration module
|
|
456
|
+
|
|
457
|
+
Have a feature idea? [Open a feature request!](../../issues/new?template=feature_request.md)
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## โญ Show Your Support
|
|
462
|
+
|
|
463
|
+
If this project helps you, give it a โญ on GitHub โ it means a lot and helps others discover it!
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## ๐ License
|
|
468
|
+
|
|
469
|
+
This project is licensed under the **MIT License** โ see the [LICENSE](LICENSE) file for details.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
<div align="center">
|
|
474
|
+
|
|
475
|
+
**Built with โค๏ธ for pharmacies everywhere**
|
|
476
|
+
|
|
477
|
+
[Report Bug](../../issues/new?template=bug_report.md) ยท [Request Feature](../../issues/new?template=feature_request.md) ยท [Contribute](CONTRIBUTING.md)
|
|
478
|
+
|
|
479
|
+
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
FROM python:3.12-slim AS base
|
|
2
|
+
|
|
3
|
+
LABEL org.opencontainers.image.title="Pharmacy ERP Backend"
|
|
4
|
+
LABEL org.opencontainers.image.description="FastAPI backend for Pharmacy ERP & POS System"
|
|
5
|
+
LABEL org.opencontainers.image.source="https://github.com/omersx/pharmacy-erp"
|
|
6
|
+
LABEL org.opencontainers.image.licenses="MIT"
|
|
7
|
+
|
|
8
|
+
# โโ Dependencies stage โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
9
|
+
FROM base AS deps
|
|
10
|
+
|
|
11
|
+
WORKDIR /app
|
|
12
|
+
|
|
13
|
+
COPY requirements.txt .
|
|
14
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
15
|
+
|
|
16
|
+
# โโ Production stage โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
17
|
+
FROM base AS runner
|
|
18
|
+
|
|
19
|
+
WORKDIR /app
|
|
20
|
+
|
|
21
|
+
# Create non-root user
|
|
22
|
+
RUN groupadd --system --gid 1001 appuser && \
|
|
23
|
+
useradd --system --uid 1001 --gid appuser appuser
|
|
24
|
+
|
|
25
|
+
# Copy installed packages from deps stage
|
|
26
|
+
COPY --from=deps /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
|
27
|
+
COPY --from=deps /usr/local/bin /usr/local/bin
|
|
28
|
+
|
|
29
|
+
# Copy application code
|
|
30
|
+
COPY . .
|
|
31
|
+
|
|
32
|
+
# Create data directory
|
|
33
|
+
RUN mkdir -p /app/data && chown -R appuser:appuser /app/data
|
|
34
|
+
|
|
35
|
+
# Switch to non-root user
|
|
36
|
+
USER appuser
|
|
37
|
+
|
|
38
|
+
EXPOSE 8000
|
|
39
|
+
|
|
40
|
+
# Health check
|
|
41
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
42
|
+
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')" || exit 1
|
|
43
|
+
|
|
44
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
2
|
+
from app.modules.admin.models import AuditLog
|
|
3
|
+
from app.modules.auth.models import User
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
async def log_action(
|
|
7
|
+
db: AsyncSession,
|
|
8
|
+
action: str,
|
|
9
|
+
module: str,
|
|
10
|
+
details: str,
|
|
11
|
+
user: Optional[User] = None,
|
|
12
|
+
entity_type: Optional[str] = None,
|
|
13
|
+
entity_id: Optional[str] = None,
|
|
14
|
+
ip_address: Optional[str] = None
|
|
15
|
+
):
|
|
16
|
+
"""Log an audit action to the database."""
|
|
17
|
+
log = AuditLog(
|
|
18
|
+
user_id=user.id if user else None,
|
|
19
|
+
user_email=user.email if user else "System",
|
|
20
|
+
action=action,
|
|
21
|
+
module=module,
|
|
22
|
+
entity_type=entity_type,
|
|
23
|
+
entity_id=str(entity_id) if entity_id else None,
|
|
24
|
+
details=details,
|
|
25
|
+
ip_address=ip_address
|
|
26
|
+
)
|
|
27
|
+
db.add(log)
|
|
28
|
+
# Don't commit here โ let the caller's transaction handle it
|
|
29
|
+
# But for standalone audit logs (like login), we flush
|
|
30
|
+
try:
|
|
31
|
+
await db.flush()
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
class Settings(BaseSettings):
|
|
5
|
+
APP_NAME: str = "Pharma ERP"
|
|
6
|
+
APP_ENV: str = "development"
|
|
7
|
+
DEBUG: bool = True
|
|
8
|
+
DATABASE_URL: str = "sqlite+aiosqlite:///./data/pharmacy.db"
|
|
9
|
+
SECRET_KEY: str = "supersecretkey_change_in_production"
|
|
10
|
+
ACCESS_TOKEN_EXPIRE_MINUTES: int = 120
|
|
11
|
+
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
|
|
12
|
+
CORS_ORIGINS: List[str] = ["*"]
|
|
13
|
+
|
|
14
|
+
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
|
|
15
|
+
|
|
16
|
+
settings = Settings()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
|
|
4
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
5
|
+
from app.core.config import settings
|
|
6
|
+
|
|
7
|
+
engine = create_async_engine(
|
|
8
|
+
settings.DATABASE_URL,
|
|
9
|
+
echo=settings.DEBUG,
|
|
10
|
+
connect_args={"check_same_thread": False} if settings.DATABASE_URL.startswith("sqlite") else {}
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
AsyncSessionLocal = async_sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)
|
|
14
|
+
|
|
15
|
+
class Base(DeclarativeBase):
|
|
16
|
+
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
|
|
17
|
+
created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow)
|
|
18
|
+
updated_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
19
|
+
|
|
20
|
+
async def get_db():
|
|
21
|
+
async with AsyncSessionLocal() as session:
|
|
22
|
+
yield session
|
|
23
|
+
|
|
24
|
+
async def init_db():
|
|
25
|
+
async with engine.begin() as conn:
|
|
26
|
+
await conn.run_sync(Base.metadata.create_all)
|