39 Commits

Author SHA1 Message Date
Swapnil9693
d84ba520cf GST Release Add, Edit, Delete Working 2026-03-24 18:46:07 +05:30
0aeaf775dd Merge pull request 'pankaj-dev - all done' (#18) from pankaj-dev into main
Reviewed-on: #18
2026-03-24 11:27:55 +00:00
88e8771b51 add log file function 2026-03-24 16:56:23 +05:30
6c74b5d3bf Merge branch 'main' of https://gitea.lcepl.org/pjpatil12/Payment_Reconciliation into pankaj-dev 2026-03-24 16:29:32 +05:30
47ba78d72c search on table 2026-03-24 16:29:18 +05:30
1946a98d59 Merge pull request 'Invoice Add Edit and Delete Done' (#17) from swapnil-dev into main
Reviewed-on: #17
2026-03-24 10:51:43 +00:00
8f35e08429 Merge pull request 'GST Release Display Chages' (#16) from laxmi-dev into main
Reviewed-on: #16
Reviewed-by: Pankaj J Patil <pankajjpatil2001@gmail.com>
2026-03-24 10:51:05 +00:00
82bedc3117 Update activity.log 2026-03-24 10:49:06 +00:00
cb68e454bc Update .env 2026-03-24 10:47:05 +00:00
Swapnil9693
91b078a0c3 edit_invoice html removed 2026-03-24 16:16:43 +05:30
Swapnil9693
eb46929893 New invoice commit 2026-03-24 16:13:04 +05:30
675301df7f GST Release Display Chages 2026-03-24 16:08:37 +05:30
Swapnil9693
14e799a1d4 Inoive add update delete messages shown properly 2026-03-24 15:51:55 +05:30
8ab1b69033 Merge pull request 'hold type updated , delete done by prajkta' (#13) from pankaj-dev into main
Reviewed-on: #13
2026-03-24 09:51:42 +00:00
dbeec9962d hold type updated , delete done by prajkta 2026-03-24 15:19:38 +05:30
8750f268db Merge pull request 'Village Add, Edit and Delete Messages done' (#12) from swapnil-dev into main
Reviewed-on: #12
2026-03-24 09:47:16 +00:00
Swapnil9693
b78526ad9f Village Add, Edit and Delete Messages done 2026-03-24 13:41:00 +05:30
7146391c18 Merge pull request 'delete payment code updated by prajakta' (#11) from pankaj-dev into main
Reviewed-on: #11
2026-03-24 07:21:43 +00:00
94b5563d15 delete payment code updated by prajakta 2026-03-24 12:50:44 +05:30
937018dc16 Merge pull request 'add item crude' (#10) from pankaj-dev into main
Reviewed-on: #10
2026-03-24 07:07:31 +00:00
eda238c235 add itemcrude 2026-03-24 12:36:15 +05:30
f184d6cecc Merge pull request 'update block redirect added' (#9) from pankaj-dev into main
Reviewed-on: #9
2026-03-24 06:26:39 +00:00
e7646eee76 Merge pull request 'Village module delete issue resolved and also message shown in alert' (#8) from swapnil-dev into main
Reviewed-on: #8
2026-03-24 06:25:19 +00:00
64ca39944b update block redirect added 2026-03-24 11:47:57 +05:30
Swapnil9693
6b4beb5af8 Village module delete issue resolved and also message shown on alert 2026-03-24 11:43:16 +05:30
Swapnil9693
d092eb0527 Sync .env and activity.log with main 2026-03-24 11:39:08 +05:30
Swapnil9693
f9e9612df5 Re-add .env and activity.log 2026-03-24 11:37:08 +05:30
Swapnil9693
46ec2c0276 Remove sensitive files (.env, activity.log) from repo 2026-03-24 11:32:00 +05:30
Swapnil9693
630ee1744f Backup Before Changes village Task 2026-03-24 11:28:14 +05:30
2679554e98 Merge pull request 'edit comments main code' (#7) from pankaj-dev into main
Reviewed-on: #7
2026-03-24 04:55:48 +00:00
0912aef85e edit comments 2026-03-23 18:45:16 +05:30
ce90c6b350 Merge pull request 'pankaj-dev' (#6) from pankaj-dev into main
Reviewed-on: #6
2026-03-23 11:52:17 +00:00
bc20a53f26 updated code of prajakta block,payment, gst 2026-03-23 17:19:21 +05:30
42f69773ec Merge branch 'main' of https://gitea.lcepl.org/pjpatil12/Payment_Reconciliation into pankaj-dev 2026-03-23 14:31:57 +05:30
79ea359b1c Merge pull request 'pankaj-dev' (#4) from pankaj-dev into main
Reviewed-on: #4
2026-03-23 09:01:20 +00:00
acbea7942c Merge branch 'main' of https://gitea.lcepl.org/pjpatil12/Payment_Reconciliation into pankaj-dev 2026-03-23 14:15:59 +05:30
13a4c5836e remove commented code 2026-03-23 14:15:11 +05:30
315e972e1f Merge pull request 'Village, invoice and Contractor Info model and controller added' (#3) from swapnil-dev into main
Reviewed-on: #3
2026-03-23 08:41:38 +00:00
af15b3934e Added gitignore 2026-03-23 11:55:16 +05:30
105 changed files with 2124 additions and 9760 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
venv/
*.pyc
__pycache__/
static/downloads/
static/uploads/

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

114
app.log
View File

@@ -1,114 +0,0 @@
2025-02-15 11:07:16,100 - INFO - Logging is set up.
2025-02-15 11:07:16,100 - INFO - Logging is set up.
2025-02-15 11:07:16,131 - WARNING - * Debugger is active!
2025-02-15 11:07:16,131 - WARNING - * Debugger is active!
2025-02-15 11:07:16,137 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:07:16,137 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:13:26,290 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET / HTTP/1.1" 200 -
2025-02-15 11:13:26,290 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET / HTTP/1.1" 200 -
2025-02-15 11:13:26,315 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/css/index.css HTTP/1.1" 304 -
2025-02-15 11:13:26,315 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/css/index.css HTTP/1.1" 304 -
2025-02-15 11:13:26,504 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:13:26,504 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:13:26,626 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/js/validateFileInput.js HTTP/1.1" 304 -
2025-02-15 11:13:26,626 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/js/validateFileInput.js HTTP/1.1" 304 -
2025-02-15 11:13:26,633 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/js/searchContractor.js HTTP/1.1" 304 -
2025-02-15 11:13:26,633 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /static/js/searchContractor.js HTTP/1.1" 304 -
2025-02-15 11:13:26,950 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /favicon.ico HTTP/1.1" 404 -
2025-02-15 11:13:26,950 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "GET /favicon.ico HTTP/1.1" 404 -
2025-02-15 11:13:28,623 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET / HTTP/1.1" 200 -
2025-02-15 11:13:28,623 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET / HTTP/1.1" 200 -
2025-02-15 11:13:28,933 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/css/index.css HTTP/1.1" 304 -
2025-02-15 11:13:28,933 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/css/index.css HTTP/1.1" 304 -
2025-02-15 11:13:28,952 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/js/validateFileInput.js HTTP/1.1" 304 -
2025-02-15 11:13:28,952 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/js/validateFileInput.js HTTP/1.1" 304 -
2025-02-15 11:13:28,954 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/js/searchContractor.js HTTP/1.1" 304 -
2025-02-15 11:13:28,955 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:13:28,954 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/js/searchContractor.js HTTP/1.1" 304 -
2025-02-15 11:13:28,955 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:13:31,608 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:13:31,608 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:13:31,639 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:13:31,639 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:13:31,649 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:13:31,649 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:13:31,967 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:13:31,967 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:15:01,349 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:01] "POST /check_state HTTP/1.1" 409 -
2025-02-15 11:15:01,349 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:01] "POST /check_state HTTP/1.1" 409 -
2025-02-15 11:15:21,783 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:21] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:21,783 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:21] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,127 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,127 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,151 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,151 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,391 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,391 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,440 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:22,440 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:22] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:15:24,266 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:24] "POST /add_state HTTP/1.1" 200 -
2025-02-15 11:15:24,266 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:24] "POST /add_state HTTP/1.1" 200 -
2025-02-15 11:15:25,418 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:15:25,418 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:15:25,716 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:15:25,716 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:15:25,749 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:15:25,749 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:15:25,752 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:15:25,752 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:46,338 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading
2025-02-15 11:16:46,338 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading
2025-02-15 11:16:48,798 - INFO - Logging is set up.
2025-02-15 11:16:48,798 - INFO - Logging is set up.
2025-02-15 11:16:48,843 - WARNING - * Debugger is active!
2025-02-15 11:16:48,843 - WARNING - * Debugger is active!
2025-02-15 11:16:48,847 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:16:48,847 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:16:52,045 - DEBUG - Fetched state data successfully.
2025-02-15 11:16:52,045 - DEBUG - Fetched state data successfully.
2025-02-15 11:16:52,054 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:16:52,054 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:16:52,076 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:16:52,076 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:16:52,078 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:52,078 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:52,377 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:52,377 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:54,758 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:54] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:16:54,758 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:54] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:16:54,992 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:54] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:16:54,992 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:54] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:16:55,016 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:55] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:16:55,016 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:55] "POST /check_state HTTP/1.1" 200 -
2025-02-15 11:16:55,669 - INFO - State 'sss' added successfully.
2025-02-15 11:16:55,669 - INFO - 'State 'sss added successfully.
2025-02-15 11:16:55,670 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:55] "POST /add_state HTTP/1.1" 200 -
2025-02-15 11:16:55,670 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:55] "POST /add_state HTTP/1.1" 200 -
2025-02-15 11:16:57,235 - DEBUG - Fetched state data successfully.
2025-02-15 11:16:57,235 - DEBUG - Fetched state data successfully.
2025-02-15 11:16:57,239 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:16:57,239 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /add_state HTTP/1.1" 200 -
2025-02-15 11:16:57,263 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:16:57,263 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /static/css/style.css HTTP/1.1" 304 -
2025-02-15 11:16:57,483 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:57,483 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /static/images/icons/pen_blue_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:57,567 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:16:57,567 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "GET /static/images/icons/bin_red_icon.png HTTP/1.1" 304 -
2025-02-15 11:20:55,547 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading
2025-02-15 11:20:55,547 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading
2025-02-15 11:20:56,800 - INFO - Logging is set up.
2025-02-15 11:20:56,800 - INFO - Logging is set up.
2025-02-15 11:20:56,835 - WARNING - * Debugger is active!
2025-02-15 11:20:56,835 - WARNING - * Debugger is active!
2025-02-15 11:20:56,837 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:20:56,837 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:21:04,060 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading
2025-02-15 11:21:04,060 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading
2025-02-15 11:21:05,429 - INFO - Logging is set up.
2025-02-15 11:21:05,429 - INFO - Logging is set up.
2025-02-15 11:21:05,461 - WARNING - * Debugger is active!
2025-02-15 11:21:05,461 - WARNING - * Debugger is active!
2025-02-15 11:21:05,463 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:21:05,463 - INFO - * Debugger PIN: 558-213-972
2025-02-15 11:21:17,911 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading
2025-02-15 11:21:17,911 - INFO - * Detected change in 'C:\\Users\\ADMIN\\PycharmProjects\\ManagementApplicationt\\main.py', reloading

View File

@@ -1,37 +1,28 @@
from flask import Blueprint, render_template, request, redirect, url_for, jsonify import config
from flask import Blueprint, render_template, request, redirect, url_for, jsonify, flash
from flask_login import login_required from flask_login import login_required
import config from model.State import State
import mysql.connector
from model.Block import Block from model.Block import Block
from model.Utilities import HtmlHelper from model.Utilities import HtmlHelper
block_bp = Blueprint('block', __name__) block_bp = Blueprint('block', __name__)
# --- Add Block page -------
@block_bp.route('/add_block', methods=['GET', 'POST']) @block_bp.route('/add_block', methods=['GET', 'POST'])
@login_required @login_required
def add_block(): def add_block():
block = Block() block = Block()
if request.method == 'POST': if request.method == 'POST':
block.AddBlock(request) block.AddBlock(request)
return block.resultMessage return block.resultMessage
connection = config.get_db_connection() state = State()
cursor = connection.cursor() states = state.GetAllStates(request=request)
cursor.callproc("GetAllStates")
for rs in cursor.stored_results():
states = rs.fetchall()
block_data = block.GetAllBlocks(request) block_data = block.GetAllBlocks(request)
cursor.close()
connection.close()
return render_template( return render_template(
'add_block.html', 'add_block.html',
states=states, states=states,
@@ -83,7 +74,14 @@ def edit_block(block_id):
if request.method == 'POST': if request.method == 'POST':
block.EditBlock(request, block_id) block.EditBlock(request, block_id)
return block.resultMessage block.resultMessage
if block.resultMessage:
flash("Block updated successfully!", "success")
return redirect(url_for('block.add_block'))
else:
flash(block.resultMessage, "error")
connection = config.get_db_connection() connection = config.get_db_connection()
cursor = connection.cursor() cursor = connection.cursor()

View File

@@ -6,11 +6,10 @@ from model.State import State
district_bp = Blueprint('district', __name__) district_bp = Blueprint('district', __name__)
# ------- District page --------
@district_bp.route('/add_district', methods=['GET', 'POST']) @district_bp.route('/add_district', methods=['GET', 'POST'])
@login_required @login_required
def add_district(): def add_district():
district = District() district = District()
if request.method == 'POST': if request.method == 'POST':
@@ -28,7 +27,7 @@ def add_district():
states=states states=states
) )
# ------- District check --------
@district_bp.route('/check_district', methods=['POST']) @district_bp.route('/check_district', methods=['POST'])
@login_required @login_required
def check_district(): def check_district():
@@ -37,7 +36,7 @@ def check_district():
return district.CheckDistrict(request=request) return district.CheckDistrict(request=request)
# ------- District delete by district id --------
@district_bp.route('/delete_district/<int:district_id>') @district_bp.route('/delete_district/<int:district_id>')
@login_required @login_required
def delete_district(district_id): def delete_district(district_id):
@@ -52,6 +51,7 @@ def delete_district(district_id):
return redirect(url_for('district.add_district')) return redirect(url_for('district.add_district'))
# ------- District update by district id --------
@district_bp.route('/edit_district/<int:district_id>', methods=['GET', 'POST']) @district_bp.route('/edit_district/<int:district_id>', methods=['GET', 'POST'])
@login_required @login_required
def edit_district(district_id): def edit_district(district_id):

View File

@@ -1,28 +1,16 @@
import os import config
import ast import ast
import re import re
from flask_login import login_required
import openpyxl import openpyxl
from flask import Blueprint, request, render_template, redirect, url_for, jsonify, current_app
from flask_login import current_user from flask_login import current_user
from flask_login import login_required
from flask import Blueprint, request, render_template, redirect, url_for, jsonify
from model.Log import LogHelper from model.Log import LogHelper
import config
from model.FolderAndFile import FolderAndFile from model.FolderAndFile import FolderAndFile
excel_bp = Blueprint('excel', __name__) excel_bp = Blueprint('excel', __name__)
# Default folder in case config not set
# DEFAULT_UPLOAD_FOLDER = 'uploads'
# def get_upload_folder():
# """Returns the upload folder from Flask config or default, ensures it exists."""
# folder = current_app.config.get('UPLOAD_FOLDER', DEFAULT_UPLOAD_FOLDER)
# if not os.path.exists(folder):
# os.makedirs(folder)
# return folder
# ---------------- Upload Excel File ---------------- # ---------------- Upload Excel File ----------------
@excel_bp.route('/upload_excel_file', methods=['GET', 'POST']) @excel_bp.route('/upload_excel_file', methods=['GET', 'POST'])
@login_required @login_required
@@ -30,9 +18,6 @@ def upload():
if request.method == 'POST': if request.method == 'POST':
file = request.files.get('file') file = request.files.get('file')
if file and file.filename.endswith('.xlsx'): if file and file.filename.endswith('.xlsx'):
# upload_folder = get_upload_folder()
# filepath = os.path.join(upload_folder, file.filename)
filepath =FolderAndFile.get_upload_path(file.filename) filepath =FolderAndFile.get_upload_path(file.filename)
file.save(filepath) file.save(filepath)
@@ -42,6 +27,7 @@ def upload():
f"User {current_user.id} Upload Excel File '{file.filename}'" f"User {current_user.id} Upload Excel File '{file.filename}'"
) )
return redirect(url_for('excel.show_table', filename=file.filename)) return redirect(url_for('excel.show_table', filename=file.filename))
return render_template('uploadExcelFile.html') return render_template('uploadExcelFile.html')
@@ -51,7 +37,6 @@ def show_table(filename):
global data global data
data = [] data = []
# filepath = os.path.join(get_upload_folder(), filename)
filepath = FolderAndFile.get_upload_path(filename) filepath = FolderAndFile.get_upload_path(filename)
wb = openpyxl.load_workbook(filepath, data_only=True) wb = openpyxl.load_workbook(filepath, data_only=True)
sheet = wb.active sheet = wb.active

View File

@@ -1,70 +1,46 @@
from flask import Blueprint, render_template, request, redirect, url_for, jsonify # routes/gst_release_routes.py
from flask_login import login_required, current_user from flask import Blueprint, render_template, request, redirect, url_for, flash
from model.gst_release import GSTReleasemodel from flask_login import login_required
from model.gst_release import GSTRelease
from model.Log import LogHelper from model.Log import LogHelper
gst_release_bp = Blueprint('gst_release_bp', __name__) gst_release_bp = Blueprint('gst_release_bp', __name__)
gst_service = GSTRelease()
# ------------------- Add GST Release ------------------- # ---------------- ADD GST RELEASE ----------------
@gst_release_bp.route('/add_gst_release', methods=['GET', 'POST']) @gst_release_bp.route('/add_gst_release', methods=['GET', 'POST'])
@login_required @login_required
def add_gst_release(): def add_gst_release():
gst_releases_dict = GSTReleasemodel.fetch_all_gst_releases()
gst_releases = [
[
g['GST_Release_Id'], g['PMC_No'], g['invoice_no'],
g['Basic_Amount'], g['Final_Amount'], g['Total_Amount'], g['UTR']
] for g in gst_releases_dict
] if gst_releases_dict else []
if request.method == 'POST': if request.method == 'POST':
pmc_no = request.form['PMC_No'] gst_service.AddGSTRelease(request)
invoice_no = request.form['invoice_No'] LogHelper.log_action("Add GST Release", "User added GST release")
basic_amount = request.form['basic_amount'] flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
final_amount = request.form['final_amount']
total_amount = request.form['total_amount']
utr = request.form['utr']
contractor_id = request.form['subcontractor_id']
LogHelper.log_action("Add GST Release", f"User {current_user.id} added GST release '{pmc_no}'")
GSTReleasemodel.insert_gst_release(pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id)
return redirect(url_for('gst_release_bp.add_gst_release')) return redirect(url_for('gst_release_bp.add_gst_release'))
return render_template('add_gst_release.html', gst_releases=gst_releases, invoices=[]) gst_releases = gst_service.GetAllGSTReleases()
return render_template('add_gst_release.html', gst_releases=gst_releases)
# ---------------- EDIT GST RELEASE ----------------
# ------------------- Edit GST Release -------------------
@gst_release_bp.route('/edit_gst_release/<int:gst_release_id>', methods=['GET', 'POST']) @gst_release_bp.route('/edit_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
@login_required @login_required
def edit_gst_release(gst_release_id): def edit_gst_release(gst_release_id):
gst_release_data = GSTReleasemodel.fetch_gst_release_by_id(gst_release_id) gst_data = gst_service.GetGSTReleaseByID(gst_release_id)
if not gst_release_data: if not gst_data:
return "GST Release not found", 404 return "GST Release not found", 404
if request.method == 'POST': if request.method == 'POST':
pmc_no = request.form['PMC_No'] gst_service.EditGSTRelease(request, gst_release_id)
invoice_no = request.form['invoice_No'] LogHelper.log_action("Edit GST Release", "User edited GST release")
basic_amount = request.form['basic_amount'] flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
final_amount = request.form['final_amount']
total_amount = request.form['total_amount']
utr = request.form['utr']
LogHelper.log_action("Edit GST Release", f"User {current_user.id} edited GST release '{pmc_no}'")
GSTReleasemodel.update_gst_release(gst_release_id, pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr)
return redirect(url_for('gst_release_bp.add_gst_release')) return redirect(url_for('gst_release_bp.add_gst_release'))
return render_template('edit_gst_release.html', gst_release_data=gst_release_data, invoices=[]) return render_template('edit_gst_release.html', gst_release_data=gst_data)
# ---------------- DELETE GST RELEASE ----------------
# ------------------- Delete GST Release -------------------
@gst_release_bp.route('/delete_gst_release/<int:gst_release_id>', methods=['GET', 'POST']) @gst_release_bp.route('/delete_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
@login_required @login_required
def delete_gst_release(gst_release_id): def delete_gst_release(gst_release_id):
success, utr = GSTReleasemodel.delete_gst_release(gst_release_id) gst_service.DeleteGSTRelease(gst_release_id)
if not success: LogHelper.log_action("Delete GST Release", "User deleted GST release")
return jsonify({"message": "GST Release not found or failed to delete", "status": "error"}), 404 flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
return redirect(url_for('gst_release_bp.add_gst_release'))
LogHelper.log_action("Delete GST Release", f"User {current_user.id} deleted GST release '{gst_release_id}'")
return jsonify({"message": f"GST Release {gst_release_id} deleted successfully.", "status": "success"}), 200

View File

@@ -10,21 +10,21 @@ hold_bp = Blueprint("hold_types", __name__)
@hold_bp.route('/add_hold_type', methods=['GET','POST']) @hold_bp.route('/add_hold_type', methods=['GET','POST'])
@login_required @login_required
def add_hold_type(): def add_hold_type():
hold = HoldTypes() hold = HoldTypes()
if request.method == 'POST': if request.method == 'POST':
hold.AddHoldType(request) # ✅ hold.AddHoldType(request)
return hold.resultMessage # ✅ Always redirect to same page (NO JSON)
return redirect(url_for("hold_types.add_hold_type"))
hold_types = hold.GetAllHoldTypes() # ✅ # GET request → show data
hold_types = hold.GetAllHoldTypes()
return render_template( return render_template(
"add_hold_type.html", "add_hold_type.html",
Hold_Types_data=hold_types Hold_Types_data=hold_types
) )
# ---------------- CHECK HOLD TYPE (OPTIONAL LIKE BLOCK) ---------------- # ---------------- CHECK HOLD TYPE (OPTIONAL LIKE BLOCK) ----------------
@hold_bp.route('/check_hold_type', methods=['POST']) @hold_bp.route('/check_hold_type', methods=['POST'])
@login_required @login_required

View File

@@ -21,7 +21,6 @@ def activity_log():
end_date, end_date,
user_name user_name
) )
return render_template( return render_template(
"activity_log.html", "activity_log.html",
logs=filtered_logs, logs=filtered_logs,

View File

@@ -1,4 +1,4 @@
from flask import Blueprint, render_template, request, redirect, url_for, jsonify from flask import Blueprint, render_template, request, redirect, url_for, jsonify, flash
from flask_login import login_required, current_user from flask_login import login_required, current_user
from model.payment import Paymentmodel from model.payment import Paymentmodel
from model.Log import LogHelper from model.Log import LogHelper
@@ -86,18 +86,16 @@ def edit_payment(payment_id):
return render_template('edit_payment.html', payment_data=payment_data) return render_template('edit_payment.html', payment_data=payment_data)
# ------------------- Delete Payment ------------------- # ------------------- Delete Payment -------------------
@payment_bp.route('/delete_payment/<int:payment_id>', methods=['GET', 'POST']) @payment_bp.route('/delete_payment/<int:payment_id>', methods=['GET'])
@login_required @login_required
def delete_payment(payment_id): def delete_payment(payment_id):
success, pmc_no, invoice_no = Paymentmodel.delete_payment(payment_id) success, pmc_no, invoice_no = Paymentmodel.delete_payment(payment_id)
if not success: if not success:
return jsonify({"message": "Payment not found or failed to delete", "status": "error"}), 404 flash("Payment not found or failed to delete", "error")
else:
LogHelper.log_action("Delete Payment", f"User {current_user.id} deleted Payment '{payment_id}'") LogHelper.log_action("Delete Payment", f"User {current_user.id} deleted Payment '{payment_id}'")
flash(f"Payment ID {payment_id} deleted successfully.", "success")
return jsonify({ return redirect(url_for('payment_bp.add_payment'))
"message": f"Payment ID {payment_id} deleted successfully.",
"status": "success"
}), 200

View File

@@ -1,9 +1,11 @@
from flask import Blueprint, render_template, send_from_directory from flask import Blueprint, render_template, send_from_directory
from flask_login import login_required, current_user from flask_login import login_required
from model.PmcReport import PmcReport from model.PmcReport import PmcReport
pmc_report_bp = Blueprint("pmc_report", __name__) pmc_report_bp = Blueprint("pmc_report", __name__)
# ---------------- Contractor Report by pmc no ----------------
@pmc_report_bp.route("/pmc_report/<pmc_no>") @pmc_report_bp.route("/pmc_report/<pmc_no>")
@login_required @login_required
def pmc_report(pmc_no): def pmc_report(pmc_no):
@@ -23,6 +25,7 @@ def pmc_report(pmc_no):
total=data["total"] total=data["total"]
) )
# ---------------- Contractor Download Report by pmc no ----------------
@pmc_report_bp.route("/download_pmc_report/<pmc_no>") @pmc_report_bp.route("/download_pmc_report/<pmc_no>")
@login_required @login_required
def download_pmc_report(pmc_no): def download_pmc_report(pmc_no):

View File

@@ -1,13 +1,8 @@
from flask import Blueprint, render_template, request, jsonify, send_file from flask import Blueprint, render_template, request, jsonify
from flask_login import login_required, current_user from flask_login import login_required, current_user
from model.Report import ReportHelper from model.Report import ReportHelper
from model.Log import LogHelper from model.Log import LogHelper
import config
import os
import openpyxl
from openpyxl.styles import Font
from model.ContractorInfo import ContractorInfo
from model.FolderAndFile import FolderAndFile
report_bp = Blueprint("report", __name__) report_bp = Blueprint("report", __name__)
@@ -36,7 +31,7 @@ def search_contractor():
return jsonify(data) return jsonify(data)
# ---------------- Contractor Report ---------------- # ---------------- Contractor Report by contractor id ----------------
@report_bp.route('/contractor_report/<int:contractor_id>') @report_bp.route('/contractor_report/<int:contractor_id>')
@login_required @login_required
def contractor_report(contractor_id): def contractor_report(contractor_id):
@@ -49,145 +44,10 @@ def contractor_report(contractor_id):
**data **data
) )
# ---------------- Contractor Download Report by contractor id ----------------
@report_bp.route('/download_report/<int:contractor_id>') @report_bp.route('/download_report/<int:contractor_id>')
@login_required @login_required
def download_report(contractor_id): def download_report(contractor_id):
return ReportHelper().download_report(contractor_id=contractor_id) return ReportHelper().download_report(contractor_id=contractor_id)
# @report_bp.route('/download_report/<int:contractor_id>')
# @login_required
# def download_report(contractor_id):
# try:
# connection = config.get_db_connection()
# cursor = connection.cursor(dictionary=True)
# # -------- Contractor Info --------
# contractor = ContractorInfo(contractor_id)
# contInfo = contractor.contInfo
# if not contInfo:
# return "No contractor found", 404
# # -------- Invoice Data --------
# cursor.callproc('FetchInvoicesByContractor', [contractor_id])
# invoices = []
# for result in cursor.stored_results():
# invoices.extend(result.fetchall())
# if not invoices:
# return "No invoice data found"
# # -------- Create Workbook --------
# workbook = openpyxl.Workbook()
# sheet = workbook.active
# sheet.title = "Contractor Report"
# # ================= CONTRACTOR DETAILS =================
# sheet.append(["SUB CONTRACTOR DETAILS"])
# sheet.cell(row=sheet.max_row, column=1).font = Font(bold=True)
# sheet.append([])
# sheet.append(["Name", contInfo.get("Contractor_Name") or ""])
# sheet.append(["Mobile No", contInfo.get("Mobile_No") or ""])
# sheet.append(["Email", contInfo.get("Email") or ""])
# sheet.append(["Village", contInfo.get("Village_Name") or ""])
# sheet.append(["Block", contInfo.get("Block_Name") or ""])
# sheet.append(["District", contInfo.get("District_Name") or ""])
# sheet.append(["State", contInfo.get("State_Name") or ""])
# sheet.append(["Address", contInfo.get("Address") or ""])
# sheet.append(["GST No", contInfo.get("GST_No") or ""])
# sheet.append(["PAN No", contInfo.get("PAN_No") or ""])
# sheet.append([])
# sheet.append([])
# # ================= TABLE HEADERS =================
# headers = [
# "PMC No", "Village", "Invoice No", "Invoice Date", "Work Type","Invoice_Details",
# "Basic Amount", "Debit Amount", "After Debit Amount",
# "Amount", "GST Amount", "TDS Amount", "SD Amount",
# "On Commission", "Hydro Testing", "Hold Amount",
# "GST SD Amount", "Final Amount",
# "Payment Amount", "TDS Payment",
# "Total Amount", "UTR"
# ]
# sheet.append(headers)
# for col in range(1, len(headers) + 1):
# sheet.cell(row=sheet.max_row, column=col).font = Font(bold=True)
# # ================= DATA =================
# total_final = 0
# total_payment = 0
# total_amount = 0
# for inv in invoices:
# row = [
# inv.get("PMC_No"),
# inv.get("Village_Name"),
# inv.get("invoice_no"),
# inv.get("Invoice_Date"),
# inv.get("Work_Type"),
# inv.get("Invoice_Details"),
# inv.get("Basic_Amount"),
# inv.get("Debit_Amount"),
# inv.get("After_Debit_Amount"),
# inv.get("Amount"),
# inv.get("GST_Amount"),
# inv.get("TDS_Amount"),
# inv.get("SD_Amount"),
# inv.get("On_Commission"),
# inv.get("Hydro_Testing"),
# inv.get("Hold_Amount"),
# inv.get("GST_SD_Amount"),
# inv.get("Final_Amount"),
# inv.get("Payment_Amount"),
# inv.get("TDS_Payment_Amount"),
# inv.get("Total_Amount"),
# inv.get("UTR")
# ]
# total_final += float(inv.get("Final_Amount") or 0)
# total_payment += float(inv.get("Payment_Amount") or 0)
# total_amount += float(inv.get("Total_Amount") or 0)
# sheet.append(row)
# # ================= TOTAL ROW =================
# sheet.append([])
# sheet.append([
# "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
# "TOTAL",
# total_final,
# total_payment,
# "",
# total_amount,
# ""
# ])
# # ================= AUTO WIDTH =================
# for column in sheet.columns:
# max_length = 0
# column_letter = column[0].column_letter
# for cell in column:
# if cell.value:
# max_length = max(max_length, len(str(cell.value)))
# sheet.column_dimensions[column_letter].width = max_length + 2
# # ================= SAVE FILE =================
# # output_folder = "downloads"
# # os.makedirs(output_folder, exist_ok=True)
# # filename = f"Contractor_Report_{contInfo.get('Contractor_Name')}.xlsx"
# # output_file = os.path.join(output_folder, filename)
# filename = f"Contractor_Report_{contInfo.get('Contractor_Name')}.xlsx"
# output_file = FolderAndFile.get_download_path(filename)
# workbook.save(output_file)
# return send_file(output_file, as_attachment=True)
# except Exception as e:
# return str(e)

View File

@@ -4,7 +4,7 @@ from model.State import State
state_bp = Blueprint('state', __name__) state_bp = Blueprint('state', __name__)
# ----- State page ------
@state_bp.route('/add_state', methods=['GET', 'POST']) @state_bp.route('/add_state', methods=['GET', 'POST'])
@login_required @login_required
def add_state(): def add_state():
@@ -19,7 +19,7 @@ def add_state():
return render_template('add_state.html', statedata=statedata) return render_template('add_state.html', statedata=statedata)
# ----- State check ------
@state_bp.route('/check_state', methods=['POST']) @state_bp.route('/check_state', methods=['POST'])
@login_required @login_required
def check_state(): def check_state():
@@ -28,21 +28,21 @@ def check_state():
return state.CheckState(request=request) return state.CheckState(request=request)
# ----- State delete by state id ------
@state_bp.route('/delete_state/<int:id>') @state_bp.route('/delete_state/<int:id>')
@login_required @login_required
def deleteState(id): def deleteState(id):
state = State() state = State()
msg = state.DeleteState(request=request, id=id) state.DeleteState(request=request, id=id)
if not state.isSuccess: if not state.isSuccess:
return state.resultMessage return state.resultMessage
else: else:
return redirect(url_for('state.add_state')) return redirect(url_for('state.add_state'))
# ----- State update by state id ------
@state_bp.route('/edit_state/<int:id>', methods=['GET', 'POST']) @state_bp.route('/edit_state/<int:id>', methods=['GET', 'POST'])
@login_required @login_required
def editState(id): def editState(id):

View File

@@ -1,34 +1,11 @@
from flask import Blueprint, render_template, request, redirect, url_for, jsonify from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from flask_login import login_required from flask_login import login_required
from model.Subcontractor import Subcontractor from model.Subcontractor import Subcontractor
from model.Utilities import HtmlHelper, ResponseHandler
subcontractor_bp = Blueprint('subcontractor', __name__) subcontractor_bp = Blueprint('subcontractor', __name__)
# ----------------------------------------------------------
# Helpers (unchanged)
# ----------------------------------------------------------
class HtmlHelper:
@staticmethod
def json_response(data, status=200):
return jsonify(data), status
class ResponseHandler:
@staticmethod
def fetch_failure(entity):
return {"status": "error", "message": f"Failed to fetch {entity}"}
@staticmethod
def add_failure(entity):
return {"status": "error", "message": f"Failed to add {entity}"}
@staticmethod
def update_failure(entity):
return {"status": "error", "message": f"Failed to update {entity}"}
@staticmethod
def delete_failure(entity):
return {"status": "error", "message": f"Failed to delete {entity}"}
# ---------------------------------------------------------- # ----------------------------------------------------------
# LIST + ADD # LIST + ADD

View File

@@ -83,17 +83,29 @@ def check_village():
return village.CheckVillage(request=request) return village.CheckVillage(request=request)
# ------------------------- Delete Village -------------------------
@village_bp.route('/delete_village/<int:village_id>') @village_bp.route('/delete_village/<int:village_id>')
@login_required @login_required
def delete_village(village_id): def delete_village(village_id):
village = Village() village = Village()
village.DeleteVillage(request=request, village_id=village_id) village.DeleteVillage(request=request, village_id=village_id)
flash(village.resultMessage, "success" if village.isSuccess else "error") # ✅ Convert resultMessage to string if it's a Response or tuple
return redirect(url_for('village.add_village')) raw_msg = village.resultMessage
if isinstance(raw_msg, tuple):
# e.g., (<Response ...>, 200)
msg = "Village deleted successfully!"
elif hasattr(raw_msg, 'get_data'):
# Flask Response object
msg = raw_msg.get_data(as_text=True) # get raw text
else:
# fallback
msg = str(raw_msg) if raw_msg else "Village deleted successfully!"
return jsonify({
"status": "success" if village.isSuccess else "error",
"message": msg
})
# ------------------------- Edit Village ------------------------- # ------------------------- Edit Village -------------------------
@village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST']) @village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])

View File

@@ -1,8 +1,4 @@
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json from flask import Blueprint, render_template, request, redirect, url_for, jsonify
from flask import current_app
from datetime import datetime
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogData, LogHelper from model.Log import LogData, LogHelper
@@ -151,7 +147,7 @@ class Block:
self.isSuccess = block.isSuccess self.isSuccess = block.isSuccess
self.resultMessage = block.resultMessage self.resultMessage = block.resultMessage
return render_template('add_block.html') return
# ---------------------------------------------------------- # ----------------------------------------------------------
# Delete Block # Delete Block

View File

@@ -1,20 +1,6 @@
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json from model.Utilities import ItemCRUDType
from flask import current_app
from datetime import datetime
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogData, LogHelper
import os
import config
import re
import mysql.connector
from mysql.connector import Error
from model.ItemCRUD import ItemCRUD from model.ItemCRUD import ItemCRUD
class District: class District:
isSuccess = False isSuccess = False
@@ -24,7 +10,7 @@ class District:
self.isSuccess = False self.isSuccess = False
self.resultMessage = "" self.resultMessage = ""
# edit district
def EditDistrict(self, request, district_id): def EditDistrict(self, request, district_id):
district = ItemCRUD(itemType=ItemCRUDType.District) district = ItemCRUD(itemType=ItemCRUDType.District)
@@ -36,7 +22,7 @@ class District:
self.resultMessage = district.resultMessage self.resultMessage = district.resultMessage
return return
# add district
def AddDistrict(self, request): def AddDistrict(self, request):
district = ItemCRUD(ItemCRUDType.District) district = ItemCRUD(ItemCRUDType.District)
@@ -50,7 +36,7 @@ class District:
return return
# get all district data
def GetAllDistricts(self, request): def GetAllDistricts(self, request):
district = ItemCRUD(itemType=ItemCRUDType.District) district = ItemCRUD(itemType=ItemCRUDType.District)
districtsdata = district.GetAllData(request=request, storedproc="GetAllDistricts") districtsdata = district.GetAllData(request=request, storedproc="GetAllDistricts")
@@ -58,7 +44,7 @@ class District:
self.resultMessage = district.resultMessage self.resultMessage = district.resultMessage
return districtsdata return districtsdata
# check district validation
def CheckDistrict(self, request): def CheckDistrict(self, request):
district = ItemCRUD(itemType=ItemCRUDType.District) district = ItemCRUD(itemType=ItemCRUDType.District)
district_name = request.json.get('district_Name', '').strip() district_name = request.json.get('district_Name', '').strip()
@@ -68,13 +54,7 @@ class District:
self.resultMessage = district.resultMessage self.resultMessage = district.resultMessage
return result return result
# get district by district id
# def GetDistrictByID(self, request,district_id):
# district = ItemCRUD(itemType=ItemCRUDType.District)
# districtdata = district.GetAllData(id=district_id,storedproc="GetDistrictDataByID")
# self.isSuccess = district.isSuccess
# self.resultMessage = district.resultMessage
# return districtdata
def GetDistrictByID(self, request, district_id): def GetDistrictByID(self, request, district_id):
district = ItemCRUD(itemType=ItemCRUDType.District) district = ItemCRUD(itemType=ItemCRUDType.District)
@@ -92,7 +72,7 @@ class District:
return districtdata return districtdata
#Delete District # Delete District by district id
def DeleteDistrict(self, request, district_id): def DeleteDistrict(self, request, district_id):
district = ItemCRUD(itemType=ItemCRUDType.District) district = ItemCRUD(itemType=ItemCRUDType.District)
district.DeleteItem(request=request,itemID=district_id,storedprocDelete="DeleteDistrict") district.DeleteItem(request=request,itemID=district_id,storedprocDelete="DeleteDistrict")

View File

@@ -27,13 +27,27 @@ class FolderAndFile:
os.makedirs(folder, exist_ok=True) os.makedirs(folder, exist_ok=True)
return folder return folder
# ----------------------------- @staticmethod
# FILE PATH METHODS def get_logs_folder():
# ----------------------------- folder = os.path.join(current_app.root_path, "logs")
if not os.path.exists(folder):
os.makedirs(folder)
os.makedirs(folder, exist_ok=True)
return folder
# FILE PATH METHODS - download
@staticmethod @staticmethod
def get_download_path(filename): def get_download_path(filename):
return os.path.join(FolderAndFile.get_download_folder(), filename) return os.path.join(FolderAndFile.get_download_folder(), filename)
# FILE PATH METHODS - upload file
@staticmethod @staticmethod
def get_upload_path(filename): def get_upload_path(filename):
return os.path.join(FolderAndFile.get_upload_folder(), filename) return os.path.join(FolderAndFile.get_upload_folder(), filename)
# FILE PATH METHODS - activity log file
@staticmethod
def get_activity_log_path(filename):
return os.path.join(FolderAndFile.get_logs_folder(), filename)

View File

@@ -1,55 +1,51 @@
import config from model.ItemCRUD import ItemCRUD
from model.Utilities import ItemCRUDType
class GST: class GST:
@staticmethod @staticmethod
def get_unreleased_gst(): def get_unreleased_gst():
# Use ItemCRUD for Invoices
invoice_crud = ItemCRUD(itemType=ItemCRUDType.Invoice)
invoices_rows = invoice_crud.GetAllData(storedproc="GetAllInvoicesBasic")
connection = config.get_db_connection() if not invoice_crud.isSuccess:
cursor = connection.cursor(dictionary=True) return [] # Could also log invoice_crud.resultMessage
try: invoices = [
# ----------- Invoices ----------- dict(
cursor.callproc('GetAllInvoicesBasic') Invoice_No=row[1],
invoices = [] GST_SD_Amount=float(row[2]) if row[2] is not None else 0
for result in cursor.stored_results(): )
invoices = result.fetchall() for row in invoices_rows
]
# Use ItemCRUD for GST Releases
gst_crud = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
gst_rows = gst_crud.GetAllData(storedproc="GetAllGSTReleasesBasic")
# ----------- GST Releases ----------- if not gst_crud.isSuccess:
cursor.callproc('GetAllGSTReleasesBasic') return [] # Could also log gst_crud.resultMessage
gst_releases = []
for result in cursor.stored_results():
gst_releases = result.fetchall()
gst_invoice_nos = { gst_invoice_nos = {
g['Invoice_No'] g[2] # Invoice_No is at index 2
for g in gst_releases for g in gst_rows
if g['Invoice_No'] if g[2]
} }
gst_basic_amounts = { gst_basic_amounts = {
float(g['Basic_Amount']) float(g[3]) # Basic_Amount at index 3
for g in gst_releases for g in gst_rows
if g['Basic_Amount'] is not None if g[3] is not None
} }
# Filter unreleased invoices
unreleased = [] unreleased = []
for inv in invoices: for inv in invoices:
match_by_invoice = inv['Invoice_No'] in gst_invoice_nos match_by_invoice = inv['Invoice_No'] in gst_invoice_nos
match_by_gst_amount = inv['GST_SD_Amount'] in gst_basic_amounts
match_by_gst_amount = float(
inv.get('GST_SD_Amount') or 0
) in gst_basic_amounts
if not (match_by_invoice or match_by_gst_amount): if not (match_by_invoice or match_by_gst_amount):
unreleased.append(inv) unreleased.append(inv)
return unreleased return unreleased
finally:
cursor.close()
connection.close()

View File

@@ -6,7 +6,6 @@ import config
import re import re
import mysql.connector import mysql.connector
# ---------------------------------------------------------- # ----------------------------------------------------------
# Mapping Class # Mapping Class
# ---------------------------------------------------------- # ----------------------------------------------------------
@@ -23,10 +22,11 @@ class itemCRUDMapping:
self.name = "Hold Type" self.name = "Hold Type"
elif itemType is ItemCRUDType.Subcontractor: elif itemType is ItemCRUDType.Subcontractor:
self.name = "Subcontractor" self.name = "Subcontractor"
elif itemType.name == "GSTRelease":
self.name = "GSTRelease"
else: else:
self.name = "Item" self.name = "Item"
# ---------------------------------------------------------- # ----------------------------------------------------------
# Generic CRUD Class # Generic CRUD Class
# ---------------------------------------------------------- # ----------------------------------------------------------
@@ -93,13 +93,47 @@ class ItemCRUD:
try: try:
# ====================================================== # ======================================================
# SUBCONTRACTOR (MULTI-FIELD) # GSTRelease MULTI-FIELD
# ====================================================== # ======================================================
if data: if self.itemCRUDType.name == "GSTRelease" and data:
# Duplicate check (PMC_No + Invoice_No)
if storedprocfetch:
cursor.callproc(storedprocfetch, (data['PMC_No'], data['Invoice_No']))
existing_item = None
for rs in cursor.stored_results():
existing_item = rs.fetchone()
if existing_item:
self.isSuccess = False
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.already_exists(self.itemCRUDMapping.name), 409
)
return
# Insert GSTRelease
cursor.callproc(storedprocadd, (
data['PMC_No'],
data['Invoice_No'],
data['Basic_Amount'],
data['Final_Amount'],
data['Total_Amount'],
data['UTR'],
data['Contractor_ID']
))
connection.commit()
self.isSuccess = True
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
)
return
# ======================================================
# SUBCONTRACTOR MULTI-FIELD
# ======================================================
if self.itemCRUDType.name == "Subcontractor" and data:
# Duplicate check
cursor.callproc(storedprocfetch, (data['Contractor_Name'],)) cursor.callproc(storedprocfetch, (data['Contractor_Name'],))
existing_item = None existing_item = None
for rs in cursor.stored_results(): for rs in cursor.stored_results():
existing_item = rs.fetchone() existing_item = rs.fetchone()
@@ -111,7 +145,6 @@ class ItemCRUD:
) )
return return
# Insert
cursor.callproc(storedprocadd, ( cursor.callproc(storedprocadd, (
data['Contractor_Name'], data['Contractor_Name'],
data['Address'], data['Address'],
@@ -123,26 +156,24 @@ class ItemCRUD:
data['GST_No'], data['GST_No'],
data['Contractor_password'] data['Contractor_password']
)) ))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200 ResponseHandler.add_success(self.itemCRUDMapping.name), 200
) )
return return
# ====================================================== # ======================================================
# NORMAL (Village / Block / State) # NORMAL SINGLE-FIELD (Village / Block / State)
# ====================================================== # ======================================================
if not re.match(RegEx.patternAlphabetOnly, childname): if not re.match(RegEx.allPattern, childname):
self.isSuccess = False self.isSuccess = False
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400 ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
) )
return return
# Duplicate check
if parentid is None: if parentid is None:
cursor.callproc(storedprocfetch, (childname,)) cursor.callproc(storedprocfetch, (childname,))
else: else:
@@ -159,17 +190,14 @@ class ItemCRUD:
) )
return return
# Insert
if parentid is None: if parentid is None:
cursor.callproc(storedprocadd, (childname,)) cursor.callproc(storedprocadd, (childname,))
else: else:
cursor.callproc(storedprocadd, (childname, parentid)) cursor.callproc(storedprocadd, (childname, parentid))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.add_success(self.itemCRUDMapping.name), 200 ResponseHandler.add_success(self.itemCRUDMapping.name), 200
) )
@@ -199,9 +227,32 @@ class ItemCRUD:
try: try:
# ====================================================== # ======================================================
# SUBCONTRACTOR (MULTI-FIELD) # GSTRelease MULTI-FIELD
# ====================================================== # ======================================================
if data: if self.itemCRUDType.name == "GSTRelease" and data:
cursor.callproc(storedprocupdate, (
data['p_pmc_no'], # PMC_No
data['p_invoice_no'], # Invoice_No
data['p_basic_amount'], # Basic_Amount
data['p_final_amount'], # Final_Amount
data['p_total_amount'], # Total_Amount
data['p_utr'], # UTR
data['p_gst_release_id']# GST_Release_Id
))
connection.commit()
self.isSuccess = True
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.update_success(self.itemCRUDMapping.name), 200
)
return
# ======================================================
# SUBCONTRACTOR MULTI-FIELD
# ======================================================
if self.itemCRUDType.name == "Subcontractor" and data:
cursor.callproc(storedprocupdate, ( cursor.callproc(storedprocupdate, (
childid, childid,
data['Contractor_Name'], data['Contractor_Name'],
@@ -214,9 +265,7 @@ class ItemCRUD:
data['GST_No'], data['GST_No'],
data['Contractor_password'] data['Contractor_password']
)) ))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = HtmlHelper.json_response( self.resultMessage = HtmlHelper.json_response(
ResponseHandler.update_success(self.itemCRUDMapping.name), 200 ResponseHandler.update_success(self.itemCRUDMapping.name), 200
@@ -224,9 +273,9 @@ class ItemCRUD:
return return
# ====================================================== # ======================================================
# NORMAL # NORMAL SINGLE-FIELD
# ====================================================== # ======================================================
if not re.match(RegEx.patternAlphabetOnly, childname): if not re.match(RegEx.allPattern, childname):
self.isSuccess = False self.isSuccess = False
self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message'] self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message']
return return
@@ -237,7 +286,6 @@ class ItemCRUD:
cursor.callproc(storedprocupdate, (childid, parentid, childname)) cursor.callproc(storedprocupdate, (childid, parentid, childname))
connection.commit() connection.commit()
self.isSuccess = True self.isSuccess = True
self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message'] self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message']
@@ -259,20 +307,15 @@ class ItemCRUD:
data = [] data = []
connection = config.get_db_connection() connection = config.get_db_connection()
if not connection: if not connection:
return [] return []
cursor = connection.cursor() cursor = connection.cursor()
try: try:
cursor.callproc(storedproc) cursor.callproc(storedproc)
for result in cursor.stored_results(): for result in cursor.stored_results():
data = result.fetchall() data = result.fetchall()
self.isSuccess = True self.isSuccess = True
except mysql.connector.Error as e: except mysql.connector.Error as e:
print(f"Error fetching {self.itemCRUDMapping.name}: {e}") print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
self.isSuccess = False self.isSuccess = False
@@ -280,7 +323,6 @@ class ItemCRUD:
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500 ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
) )
return [] return []
finally: finally:
cursor.close() cursor.close()
connection.close() connection.close()
@@ -298,13 +340,10 @@ class ItemCRUD:
try: try:
cursor.callproc(storedproc, (id,)) cursor.callproc(storedproc, (id,))
for rs in cursor.stored_results(): for rs in cursor.stored_results():
data = rs.fetchone() data = rs.fetchone()
except mysql.connector.Error as e: except mysql.connector.Error as e:
print(f"Error fetching {self.itemCRUDMapping.name}: {e}") print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
finally: finally:
cursor.close() cursor.close()
connection.close() connection.close()
@@ -324,7 +363,7 @@ class ItemCRUD:
f"User {current_user.id} checked '{childname}'" f"User {current_user.id} checked '{childname}'"
) )
if not re.match(RegEx.patternAlphabetOnly, childname): if not re.match(RegEx.allPattern, childname):
return HtmlHelper.json_response( return HtmlHelper.json_response(
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400 ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
) )
@@ -353,7 +392,6 @@ class ItemCRUD:
return HtmlHelper.json_response( return HtmlHelper.json_response(
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500 ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
) )
finally: finally:
cursor.close() cursor.close()
connection.close() connection.close()

View File

@@ -1,10 +1,10 @@
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json import os
from flask import current_app from flask import current_app
from datetime import datetime
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
import os from datetime import datetime
from model.FolderAndFile import FolderAndFile
class LogHelper: class LogHelper:
@staticmethod @staticmethod
@@ -14,22 +14,24 @@ class LogHelper:
logData.WriteLog(action, details="") logData.WriteLog(action, details="")
class LogData: class LogData:
filepath = "" filepath = ""
timestamp = None timestamp = None
def __init__(self): def __init__(self):
self.filepath = os.path.join(current_app.root_path, 'activity.log') self.filepath = FolderAndFile.get_activity_log_path('activity.log')
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.user = LogData.get_current_user()
@staticmethod
def get_current_user():
if hasattr(current_user, "cn") and current_user.cn: if hasattr(current_user, "cn") and current_user.cn:
self.user = current_user.cn return current_user.cn
elif hasattr(current_user, "username") and current_user.username: elif hasattr(current_user, "username") and current_user.username:
self.user = current_user.username return current_user.username
elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName: elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName:
self.user = current_user.sAMAccountName return current_user.sAMAccountName
else: return "Unknown"
self.user = "Unknown"
def WriteLog(self, action, details=""): def WriteLog(self, action, details=""):
"""Log user actions with timestamp, user, action, and details.""" """Log user actions with timestamp, user, action, and details."""
@@ -42,7 +44,6 @@ class LogData:
f"Details: {details}\n" f"Details: {details}\n"
) )
def GetActivitiesLog(self): def GetActivitiesLog(self):
logs = [] logs = []
@@ -60,7 +61,6 @@ class LogData:
return logs return logs
def GetFilteredActivitiesLog(self, startDate, endDate, userName): def GetFilteredActivitiesLog(self, startDate, endDate, userName):
filtered_logs = self.GetActivitiesLog() filtered_logs = self.GetActivitiesLog()
# Date filter # Date filter
@@ -69,16 +69,13 @@ class LogData:
start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min
end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max
filtered_logs = [ filtered_logs = [
log for log in filtered_logs log for log in filtered_logs
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
] ]
except Exception as e: except Exception as e:
print("Date filter error:", e) print("Date filter error:", e)
#Why catching all exceptions? Need to handle specific exceptions
# Username filter # Username filter
if userName: if userName:

View File

@@ -1,13 +1,9 @@
from flask import request, redirect, url_for
from flask_login import current_user
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
from model.Log import LogHelper
from model.ItemCRUD import ItemCRUD
import config import config
import re
import mysql.connector import mysql.connector
from flask import request, redirect, url_for
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
from model.ItemCRUD import ItemCRUD
class State: class State:
@@ -20,7 +16,6 @@ class State:
# ADD STATE (USING ITEM CRUD) # ADD STATE (USING ITEM CRUD)
# ---------------------------------------------------------- # ----------------------------------------------------------
def AddState(self, request): def AddState(self, request):
state_name = request.form['state_Name'].strip() state_name = request.form['state_Name'].strip()
crud = ItemCRUD(ItemCRUDType.State) crud = ItemCRUD(ItemCRUDType.State)
@@ -42,16 +37,14 @@ class State:
# GET ALL STATES (NO CHANGE - THIS IS CORRECT) # GET ALL STATES (NO CHANGE - THIS IS CORRECT)
# ---------------------------------------------------------- # ----------------------------------------------------------
def GetAllStates(self, request): def GetAllStates(self, request):
connection = config.get_db_connection() connection = config.get_db_connection()
data = [] data = []
if not connection: if not connection:
return [] return []
cursor = connection.cursor()
try: try:
cursor = connection.cursor()
cursor.callproc("GetAllStates") cursor.callproc("GetAllStates")
for res in cursor.stored_results(): for res in cursor.stored_results():
data = res.fetchall() data = res.fetchall()
@@ -77,7 +70,6 @@ class State:
# CHECK STATE (USING ITEM CRUD) # CHECK STATE (USING ITEM CRUD)
# ---------------------------------------------------------- # ----------------------------------------------------------
def CheckState(self, request): def CheckState(self, request):
state_name = request.json.get('state_Name', '').strip() state_name = request.json.get('state_Name', '').strip()
crud = ItemCRUD(ItemCRUDType.State) crud = ItemCRUD(ItemCRUDType.State)
@@ -94,7 +86,6 @@ class State:
# DELETE STATE (USING ITEM CRUD) # DELETE STATE (USING ITEM CRUD)
# ---------------------------------------------------------- # ----------------------------------------------------------
def DeleteState(self, request, id): def DeleteState(self, request, id):
crud = ItemCRUD(ItemCRUDType.State) crud = ItemCRUD(ItemCRUDType.State)
crud.DeleteItem( crud.DeleteItem(
@@ -113,7 +104,6 @@ class State:
# EDIT STATE (USING ITEM CRUD) # EDIT STATE (USING ITEM CRUD)
# ---------------------------------------------------------- # ----------------------------------------------------------
def EditState(self, request, id): def EditState(self, request, id):
state_name = request.form['state_Name'].strip() state_name = request.form['state_Name'].strip()
crud = ItemCRUD(ItemCRUDType.State) crud = ItemCRUD(ItemCRUDType.State)
@@ -136,16 +126,14 @@ class State:
# GET STATE BY ID (KEEP SAME) # GET STATE BY ID (KEEP SAME)
# ---------------------------------------------------------- # ----------------------------------------------------------
def GetStateByID(self, request, id): def GetStateByID(self, request, id):
connection = config.get_db_connection() connection = config.get_db_connection()
data = None data = None
if not connection: if not connection:
return None return None
cursor = connection.cursor()
try: try:
cursor = connection.cursor()
cursor.callproc("GetStateByID", (id,)) cursor.callproc("GetStateByID", (id,))
for res in cursor.stored_results(): for res in cursor.stored_results():
data = res.fetchone() data = res.fetchone()

View File

@@ -8,10 +8,11 @@ class ItemCRUDType(Enum):
State = 4 State = 4
HoldType = 5 HoldType = 5
Subcontractor = 6 Subcontractor = 6
GSTRelease = 7
class RegEx: class RegEx:
patternAlphabetOnly = "^[A-Za-z ]+$" patternAlphabetOnly = "^[A-Za-z ]+$"
allPattern = "^(?!\s*$).+"
class ResponseHandler: class ResponseHandler:
@@ -62,5 +63,5 @@ class HtmlHelper:
@staticmethod @staticmethod
def json_response(message_obj, status_code): def json_response(message_obj, status_code):
return jsonify(message_obj), status_code return jsonify(message_obj), status_code
#May need to refactor further

View File

@@ -1,4 +1,5 @@
# return blocks
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
import config import config
import mysql.connector import mysql.connector
@@ -12,11 +13,18 @@ class Village:
def __init__(self): def __init__(self):
self.isSuccess = False self.isSuccess = False
self.resultMessage = "" self.resultMessage = ""
self.response = {} # ✅ ADDED
self.village = ItemCRUD(itemType=ItemCRUDType.Village) self.village = ItemCRUD(itemType=ItemCRUDType.Village)
# 🔹 Helper: sync status # 🔹 Helper: sync status
def _set_status(self, village): def _set_status(self, village):
self.isSuccess = village.isSuccess self.isSuccess = village.isSuccess
# ✅ UPDATED (safe handling)
if hasattr(village, "response"):
self.response = village.response
self.resultMessage = village.response.get("message", "")
else:
self.resultMessage = village.resultMessage self.resultMessage = village.resultMessage
# 🔹 Helper: get request data # 🔹 Helper: get request data
@@ -29,8 +37,9 @@ class Village:
block_id, village_name = self._get_form_data(request) block_id, village_name = self._get_form_data(request)
if not village_name: if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.resultMessage = self.response["message"]
self.isSuccess = False self.isSuccess = False
self.resultMessage = "Village name cannot be empty"
return return
try: try:
@@ -66,8 +75,9 @@ class Village:
block_id, village_name = self._get_form_data(request) block_id, village_name = self._get_form_data(request)
if not village_name: if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.resultMessage = self.response["message"]
self.isSuccess = False self.isSuccess = False
self.resultMessage = "Village name cannot be empty"
return None return None
try: try:
@@ -103,8 +113,9 @@ class Village:
block_id, village_name = self._get_form_data(request) block_id, village_name = self._get_form_data(request)
if not village_name: if not village_name:
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
self.resultMessage = self.response["message"]
self.isSuccess = False self.isSuccess = False
self.resultMessage = "Village name cannot be empty"
return return
try: try:
@@ -164,9 +175,11 @@ class Village:
except mysql.connector.Error as e: except mysql.connector.Error as e:
print(f"Error fetching blocks: {e}") print(f"Error fetching blocks: {e}")
self.isSuccess = False self.isSuccess = False
self.resultMessage = HtmlHelper.json_response(
ResponseHandler.fetch_failure("block"), 500 # ✅ FIXED (removed jsonify response)
) self.response = ResponseHandler.fetch_failure("block")
self.resultMessage = self.response["message"]
return [] return []
finally: finally:

View File

@@ -1,150 +1,160 @@
import config # model/gst_release.py
import mysql.connector from flask import request, jsonify
from model.ItemCRUD import ItemCRUD
from model.Utilities import ItemCRUDType
class GSTReleasemodel: class GSTRelease:
@staticmethod def __init__(self):
def get_connection(): self.isSuccess = False
connection = config.get_db_connection() self.resultMessage = ""
if not connection:
return None
return connection
@staticmethod
def fetch_all_gst_releases():
connection = GSTReleasemodel.get_connection()
gst_releases = []
if connection:
cursor = connection.cursor(dictionary=True)
# ------------------- Add GST Release -------------------
def AddGSTRelease(self, request):
try: try:
cursor.callproc('GetAllGSTReleases') gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
gst_releases = [] # Print the full form data
for result in cursor.stored_results(): # change to procedure print("===== DEBUG: FORM DATA =====")
gst_releases = result.fetchall() for key, value in request.form.items():
print(f"{key} : {value}")
print("=============================")
except mysql.connector.Error as e: data = {
print(f"Error fetching GST releases: {e}") "PMC_No": request.form.get("PMC_No", "").strip(),
finally: "Invoice_No": request.form.get("Invoice_No", "").strip(),
cursor.close() "Basic_Amount": float(request.form.get("Basic_Amount", 0) or 0),
connection.close() "Final_Amount": float(request.form.get("Final_Amount", 0) or 0),
return gst_releases "Total_Amount": float(request.form.get("Total_Amount", 0) or 0),
"UTR": request.form.get("UTR", "").strip(),
"Contractor_ID": int(request.form.get("Contractor_ID", 0) or 0)
}
@staticmethod print("===== DEBUG: PARSED DATA =====")
def insert_gst_release(pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id): print(data)
connection = GSTReleasemodel.get_connection() print("==============================")
if not connection:
return False
try:
cursor = connection.cursor()
# Insert into gst_release # Add GST Release
cursor.callproc( gst.AddItem(
'InsertGSTReleaseOnly', request=request,
[pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id] data=data,
storedprocfetch="CheckGSTReleaseExists",
storedprocadd="AddGSTReleaseFromExcel"
) )
# Insert into inpayment print(f"AddItem result: isSuccess={gst.isSuccess}, message={gst.resultMessage}")
cursor.callproc(
'InsertInpaymentOnly', self.isSuccess = gst.isSuccess
[pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id] self.resultMessage = str(gst.resultMessage)
except Exception as e:
print("ERROR in AddGSTRelease:", e)
self.isSuccess = False
self.resultMessage = str(e)
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
def EditGSTRelease(self, request, gst_release_id):
try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# Map form inputs to stored procedure parameters
data = {
"p_pmc_no": request.form.get("PMC_No", "").strip(),
"p_invoice_no": request.form.get("invoice_no", "").strip(),
"p_basic_amount": float(request.form.get("Basic_Amount", 0) or 0),
"p_final_amount": float(request.form.get("Final_Amount", 0) or 0),
"p_total_amount": float(request.form.get("Total_Amount", 0) or 0),
"p_utr": request.form.get("UTR", "").strip(),
"p_gst_release_id": gst_release_id
}
print("===== DEBUG: UPDATE DATA =====")
print(data)
print("==============================")
# Call your stored procedure
gst.EditItem(
request=request,
childid=gst_release_id,
data=data,
storedprocupdate="UpdateGSTRelease"
) )
connection.commit() self.isSuccess = gst.isSuccess
return True self.resultMessage = str(gst.resultMessage)
except mysql.connector.Error as e:
print(f"Error inserting GST release: {e}")
return False
finally:
cursor.close()
connection.close()
@staticmethod except Exception as e:
def fetch_gst_release_by_id(gst_release_id): print("ERROR in EditGSTRelease:", e)
connection = GSTReleasemodel.get_connection() self.isSuccess = False
if not connection: self.resultMessage = str(e)
return None
data = {} # ------------------- Delete GST Release -------------------
def DeleteGSTRelease(self, gst_release_id):
try: try:
cursor = connection.cursor(dictionary=True) gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
cursor.callproc('GetGSTReleaseById', [gst_release_id]) gst.DeleteItem(
request=None,
itemID=gst_release_id,
storedprocDelete="DeleteGSTReleaseById"
)
self.isSuccess = gst.isSuccess
self.resultMessage = str(gst.resultMessage)
except Exception as e:
print("ERROR in DeleteGSTRelease:", e)
self.isSuccess = False
self.resultMessage = str(e)
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
# ------------------- Get All GST Releases -------------------
def GetAllGSTReleases(self):
try:
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
rows = gst.GetAllData(None, "GetAllGSTReleases")
data = []
for row in rows:
data.append({
"gst_release_id": row[0],
"pmc_no": row[1],
"invoice_no": row[2],
"basic_amount": row[3],
"final_amount": row[4],
"total_amount": row[5],
"utr": row[6],
"contractor_id": row[7]
})
for result in cursor.stored_results():
data = result.fetchone()
if data:
# Convert to array for template
data = [
data.get('GST_Release_Id'),
data.get('PMC_No'),
data.get('Invoice_No'),
data.get('Basic_Amount'),
data.get('Final_Amount'),
data.get('Total_Amount'),
data.get('UTR')
]
except mysql.connector.Error as e:
print(f"Error fetching GST release by id: {e}")
finally:
cursor.close()
connection.close()
return data return data
@staticmethod except Exception as e:
def update_gst_release(gst_release_id, pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr): print("ERROR in GetAllGSTReleases:", e)
connection = GSTReleasemodel.get_connection() return []
if not connection:
return False # ------------------- Get GST Release By ID -------------------
def GetGSTReleaseByID(self, gst_release_id):
try: try:
cursor = connection.cursor() gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
# Update gst_release row = gst.GetDataByID(gst_release_id, "GetGSTReleaseById")
cursor.callproc(
'UpdateGSTRelease',
[pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, gst_release_id]
)
# Update inpayment
cursor.callproc(
'UpdateInpaymentByUTR',
[basic_amount, final_amount, total_amount, utr]
)
connection.commit()
return True
except mysql.connector.Error as e:
print(f"Error updating GST release: {e}")
return False
finally:
cursor.close()
connection.close()
@staticmethod if row:
def delete_gst_release(gst_release_id): return {
connection = GSTReleasemodel.get_connection() "gst_release_id": row[0],
if not connection: "pmc_no": row[1],
return False, None "invoice_no": row[2],
try: "basic_amount": row[3],
cursor = connection.cursor(dictionary=True) "final_amount": row[4],
cursor.callproc('GetGSTReleaseUTRById', [gst_release_id]) "total_amount": row[5],
record = None "utr": row[6],
for result in cursor.stored_results(): "contractor_id": row[7]
record = result.fetchone() }
if not record: return None
return False, None
utr = record['UTR'] except Exception as e:
print("ERROR in GetGSTReleaseByID:", e)
# Step 1: Delete gst_release return None
cursor.callproc('DeleteGSTReleaseById', [gst_release_id])
# Step 2: Reset inpayment using UTR
cursor.callproc('ResetInpaymentByUTR', [utr])
connection.commit()
return True, utr
except mysql.connector.Error as e:
print(f"Error deleting GST release: {e}")
return False, None
finally:
cursor.close()
connection.close()

View File

@@ -1,8 +1,13 @@
import config import config
import mysql.connector import mysql.connector
import config
import mysql.connector
from enum import Enum
from model.Utilities import ItemCRUDType
class Paymentmodel: class Paymentmodel:
# ---------------- Database Connection ----------------
@staticmethod @staticmethod
def get_connection(): def get_connection():
connection = config.get_db_connection() connection = config.get_db_connection()
@@ -10,6 +15,7 @@ class Paymentmodel:
return None return None
return connection return connection
# ---------------- Payment Methods ----------------
@staticmethod @staticmethod
def fetch_all_payments(): def fetch_all_payments():
connection = Paymentmodel.get_connection() connection = Paymentmodel.get_connection()
@@ -52,13 +58,7 @@ class Paymentmodel:
try: try:
cursor = connection.cursor() cursor = connection.cursor()
cursor.callproc('UpdateInpaymentRecord', [ cursor.callproc('UpdateInpaymentRecord', [
subcontractor_id, subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr
pmc_no,
invoice_no,
amount,
tds_amount,
total_amount,
utr
]) ])
connection.commit() connection.commit()
return True return True
@@ -80,7 +80,6 @@ class Paymentmodel:
cursor.callproc("GetPaymentById", (payment_id,)) cursor.callproc("GetPaymentById", (payment_id,))
for result in cursor.stored_results(): for result in cursor.stored_results():
payment_data = result.fetchone() payment_data = result.fetchone()
# Convert to array for template
if payment_data: if payment_data:
payment_data = [ payment_data = [
payment_data.get('Payment_Id'), payment_data.get('Payment_Id'),
@@ -117,42 +116,99 @@ class Paymentmodel:
@staticmethod @staticmethod
def delete_payment(payment_id): def delete_payment(payment_id):
"""
Deletes a payment and resets the related inpayment fields in one go.
Returns (success, pmc_no, invoice_no)
"""
connection = Paymentmodel.get_connection() connection = Paymentmodel.get_connection()
if not connection: if not connection:
return False, None, None return False, None, None
try: try:
cursor = connection.cursor(dictionary=True) cursor = connection.cursor(dictionary=True)
# Fetch PMC & Invoice before deleting
cursor.callproc('GetPaymentPMCInvoiceById', [payment_id]) cursor.callproc('GetPaymentPMCInvoiceById', [payment_id])
record = {} record = {}
for result in cursor.stored_results(): for result in cursor.stored_results():
record = result.fetchone() or {} record = result.fetchone() or {}
if not record: if not record:
return False, None, None return False, None, None
pmc_no = record['PMC_No'] pmc_no = record['PMC_No']
invoice_no = record['Invoice_No'] invoice_no = record['Invoice_No']
# Delete payment
# Step 2: Delete the payment using the stored procedure
cursor.callproc("DeletePayment", (payment_id,)) cursor.callproc("DeletePayment", (payment_id,))
connection.commit() connection.commit()
# Reset inpayment fields
# Step 3: Reset inpayment fields using the stored procedure
cursor.callproc("ResetInpayment", [pmc_no, invoice_no]) cursor.callproc("ResetInpayment", [pmc_no, invoice_no])
connection.commit() connection.commit()
return True, pmc_no, invoice_no return True, pmc_no, invoice_no
except mysql.connector.Error as e: except mysql.connector.Error as e:
print(f"Error deleting payment: {e}") print(f"Error deleting payment: {e}")
return False, None, None return False, None, None
finally:
cursor.close()
connection.close()
# ---------------- Item CRUD Methods ----------------
@staticmethod
def fetch_items(item_type: ItemCRUDType):
connection = Paymentmodel.get_connection()
items = []
if connection:
cursor = connection.cursor(dictionary=True)
try:
cursor.callproc('GetItemsByType', [item_type.value])
for result in cursor.stored_results():
items = result.fetchall()
except mysql.connector.Error as e:
print(f"Error fetching {item_type.name}: {e}")
finally:
cursor.close()
connection.close()
return items
@staticmethod
def insert_item(item_type: ItemCRUDType, name: str):
connection = Paymentmodel.get_connection()
if not connection:
return False
try:
cursor = connection.cursor()
cursor.callproc('InsertItem', [item_type.value, name])
connection.commit()
return True
except mysql.connector.Error as e:
print(f"Error inserting {item_type.name}: {e}")
return False
finally:
cursor.close()
connection.close()
@staticmethod
def update_item(item_type: ItemCRUDType, item_id: int, new_name: str):
connection = Paymentmodel.get_connection()
if not connection:
return False
try:
cursor = connection.cursor()
cursor.callproc('UpdateItem', [item_type.value, item_id, new_name])
connection.commit()
return True
except mysql.connector.Error as e:
print(f"Error updating {item_type.name}: {e}")
return False
finally:
cursor.close()
connection.close()
@staticmethod
def delete_item(item_type: ItemCRUDType, item_id: int):
connection = Paymentmodel.get_connection()
if not connection:
return False
try:
cursor = connection.cursor()
cursor.callproc('DeleteItem', [item_type.value, item_id])
connection.commit()
return True
except mysql.connector.Error as e:
print(f"Error deleting {item_type.name}: {e}")
return False
finally: finally:
cursor.close() cursor.close()
connection.close() connection.close()

View File

@@ -3,7 +3,7 @@ $(document).ready(function () {
let holdType = $(this).val().replace(/^\s+/, ""); let holdType = $(this).val().replace(/^\s+/, "");
$(this).val(holdType); $(this).val(holdType);
let reg = /^[A-Za-z]/; let reg = /^.+$/; // all pattern allow
if (!reg.test(holdType)) { if (!reg.test(holdType)) {
$("#holdTypeMessage").text("Hold Type must start with a letter.").css("color", "red"); $("#holdTypeMessage").text("Hold Type must start with a letter.").css("color", "red");

View File

@@ -1,5 +1,160 @@
// Subcontractor autocomplete functionality // $(document).ready(function () {
// // ===============================
// // FORM / TABLE TOGGLE
// // ===============================
// if ($('#addForm').length && $('#addTable').length) {
// $('#addForm').show();
// $('#addTable').hide();
// $('#addButton').click(function () {
// $('#addForm').show();
// $('#addTable').hide();
// });
// $('#displayButton').click(function () {
// $('#addForm').hide();
// $('#addTable').show();
// });
// }
// // ===============================
// // Subcontractor autocomplete
// // ===============================
// $("#subcontractor").keyup(function () {
// let query = $(this).val();
// if (query !== "") {
// $.ajax({
// url: "/search_subcontractor",
// method: "POST",
// data: { query: query },
// success: function (data) {
// $("#subcontractor_list").fadeIn().html(data);
// }
// });
// } else {
// $("#subcontractor_list").fadeOut();
// }
// });
// $(document).on("click", "li", function () {
// $("#subcontractor").val($(this).text());
// $("#subcontractor_id").val($(this).attr("data-id"));
// $("#subcontractor_list").fadeOut();
// });
// // Focus
// if (document.getElementById('subcontractor')) {
// document.getElementById('subcontractor').focus();
// }
// // ===============================
// // ADD INVOICE
// // ===============================
// if ($('#invoiceForm').length && window.location.href.includes('add_invoice')) {
// $("#invoiceForm").on("submit", function (e) {
// e.preventDefault();
// let formData = $(this).serialize();
// $.ajax({
// url: '/add_invoice',
// method: 'POST',
// data: formData,
// dataType: 'json',
// success: function (response) {
// if (response.status === "success") {
// alert(response.message || "Invoice added successfully!");
// $('#invoiceForm')[0].reset(); // clear form
// $('#addForm').hide();
// $('#addTable').show(); // switch to table
// location.reload(); // optional refresh
// } else {
// alert(response.message || "Error adding invoice.");
// }
// },
// error: function (xhr) {
// alert(xhr.responseJSON?.message || "Submission failed. Please try again.");
// }
// });
// });
// }
// // Example AJAX update function
// function updateInvoice(invoiceId, formElement) {
// const formData = $(formElement).serialize();
// $.ajax({
// url: '/update_invoice/' + invoiceId,
// method: 'POST',
// data: formData,
// dataType: 'json',
// success: function(response) {
// if(response.status === "success") {
// alert(response.message || "Invoice updated successfully!");
// // ✅ Hide Add Form, Show Table
// $('#addForm').hide();
// $('#addTable').show();
// // Optional: reload table or refresh page
// location.reload();
// } else {
// alert(response.message || "Update failed. Please try again.");
// }
// },
// error: function(xhr) {
// alert(xhr.responseJSON?.message || "Error updating invoice.");
// }
// });
// }
// // ===============================
// // DELETE INVOICE
// // ===============================
// function deleteInvoice(invoiceId, element) {
// if (!confirm("Are you sure you want to delete this invoice?")) return;
// $.ajax({
// url: '/delete_invoice/' + invoiceId,
// method: 'GET',
// dataType: 'json',
// success: function (response) {
// alert(response.message || "Invoice deleted successfully!");
// if (element) $(element).closest("tr").remove();
// },
// error: function (xhr) {
// alert(xhr.responseJSON?.message || "Error deleting invoice. Please try again.");
// }
// });
// }
$(document).ready(function () { $(document).ready(function () {
// ===============================
// FORM / TABLE TOGGLE
// ===============================
if ($('#addForm').length && $('#addTable').length) {
// Default: show form, hide table
$('#addForm').show();
$('#addTable').hide();
// ✅ Check URL hash to show table instead
if (window.location.hash === "#addTable") {
$('#addForm').hide();
$('#addTable').show();
}
$('#addButton').click(function () {
$('#addForm').show();
$('#addTable').hide();
});
$('#displayButton').click(function () {
$('#addForm').hide();
$('#addTable').show();
});
}
// ===============================
// Subcontractor autocomplete
// ===============================
$("#subcontractor").keyup(function () { $("#subcontractor").keyup(function () {
let query = $(this).val(); let query = $(this).val();
if (query !== "") { if (query !== "") {
@@ -21,42 +176,96 @@
$("#subcontractor_id").val($(this).attr("data-id")); $("#subcontractor_id").val($(this).attr("data-id"));
$("#subcontractor_list").fadeOut(); $("#subcontractor_list").fadeOut();
}); });
});
// Success Alert: show alert and reload after 3 seconds // Focus
function showSuccessAlert() { if (document.getElementById('subcontractor')) {
const alertBox = document.getElementById("invoiceSuccessAlert"); document.getElementById('subcontractor').focus();
alertBox.classList.add("show");
setTimeout(() => {
alertBox.classList.remove("show");
// Reload page or redirect after alert hides
window.location.href = '/add_invoice';
}, 3000);
} }
// Submit form via AJAX // ===============================
// ADD INVOICE
// ===============================
if ($('#invoiceForm').length && window.location.href.includes('add_invoice')) {
$("#invoiceForm").on("submit", function (e) { $("#invoiceForm").on("submit", function (e) {
e.preventDefault(); e.preventDefault();
let formData = $(this).serialize(); let formData = $(this).serialize();
$.ajax({ $.ajax({
url: '/add_invoice', url: '/add_invoice',
method: 'POST', method: 'POST',
data: formData, data: formData,
dataType: 'json',
success: function (response) { success: function (response) {
if (response.status === "success") { if (response.status === "success") {
showSuccessAlert(); alert(response.message || "Invoice added successfully!");
$('#invoiceForm')[0].reset(); // clear form
$('#addForm').hide();
$('#addTable').show(); // switch to table
location.reload(); // optional refresh
} else { } else {
alert(response.message); alert(response.message || "Error adding invoice.");
} }
}, },
error: function (xhr, status, error) { error: function (xhr) {
alert("Submission failed: " + error); let msg = xhr.responseJSON?.message || "Submission failed. Please try again.";
alert(msg);
} }
}); });
}); });
}
// ===============================
// UPDATE INVOICE
// ===============================
function updateInvoice(invoiceId, formElement) {
const formData = $(formElement).serialize();
$.ajax({
url: '/update_invoice/' + invoiceId,
method: 'POST',
data: formData,
dataType: 'json',
success: function(response) {
if(response.status === "success") {
alert(response.message || "Invoice updated successfully!");
window.onload = function () { // Redirect to Add Invoice page's table part
document.getElementById('subcontractor').focus(); window.location.href = "/add_invoice#addTable";
}; } else {
alert(response.message || "Update failed. Please try again.");
}
},
error: function(xhr) {
let msg = xhr.responseJSON?.message || "Error updating invoice.";
alert(msg);
}
});
}
window.updateInvoice = updateInvoice; // make globally accessible
// ===============================
// DELETE INVOICE
// ===============================
function deleteInvoice(invoiceId, element) {
if (!confirm("Are you sure you want to delete this invoice?")) return;
$.ajax({
url: '/delete_invoice/' + invoiceId,
method: 'GET',
dataType: 'json',
success: function (response) {
if (response.status === "success") {
alert(response.message || "Invoice deleted successfully!");
if (element) $(element).closest("tr").remove();
} else {
alert(response.message || "Error deleting invoice.");
}
},
error: function (xhr) {
let msg = xhr.responseJSON?.message || "Error deleting invoice. Please try again.";
alert(msg);
}
});
}
window.deleteInvoice = deleteInvoice; // make globally accessible
});

View File

@@ -1,26 +1,23 @@
// Search on table using search inpute options // Search on table using search inpute options
function searchTable() { function searchTable() {
let input = document.getElementById("searchBar").value.toLowerCase(); let input = document.getElementById("searchBar").value.toLowerCase();
let rows = document.querySelectorAll("table tbody tr"); let tables = document.querySelectorAll("table");
rows.forEach(row => { tables.forEach(table => {
let blockName = row.cells[1].textContent.toLowerCase(); let rows = table.querySelectorAll("tr");
let districtName = row.cells[2].textContent.toLowerCase();
let villageName = row.cells[3].textContent.toLowerCase();
if (blockName.includes(input) || districtName.includes(input)|| villageName.includes(input)) { rows.forEach((row, index) => {
row.style.display = ""; if (index === 0) return; // header skip
} else {
row.style.display = "none"; let text = row.textContent.toLowerCase();
}
row.style.display = text.includes(input) ? "" : "none";
});
}); });
} }
// Common Sorting Script for Tables // Common Sorting Script for Tables
function sortTable(n, dir) { function sortTable(n, dir) {
var table, rows, switching, i, x, y, shouldSwitch; var table, rows, switching, i, x, y, shouldSwitch;
@@ -106,3 +103,30 @@ document.addEventListener("DOMContentLoaded", function () {
addButton.classList.remove("active-button"); addButton.classList.remove("active-button");
}); });
}); });
document.addEventListener("DOMContentLoaded", function () {
let tables = document.querySelectorAll("table");
tables.forEach(table => {
let header = table.querySelector("tr:first-child");
if (header) {
header.style.position = "sticky";
header.style.top = "0";
header.style.background = "#fff";
header.style.zIndex = "2";
}
if (!table.parentElement.classList.contains("table-wrapper")) {
let wrapper = document.createElement("div");
wrapper.classList.add("table-wrapper");
wrapper.style.maxHeight = "65vh"
wrapper.style.overflowY = "auto";
table.parentNode.insertBefore(wrapper, table);
wrapper.appendChild(table);
}
});
});

View File

@@ -1,10 +1,37 @@
window.onload = function () { window.onload = function () {
if (document.getElementById('Village_Name')) {
document.getElementById('Village_Name').focus(); document.getElementById('Village_Name').focus();
}
}; };
$(document).ready(function () { $(document).ready(function () {
// ✅ RUN ONLY IF THIS PAGE HAS FORM/TABLE
if ($('#addForm').length && $('#addTable').length) {
// ✅ DEFAULT VIEW → SHOW FORM
$('#addForm').show();
$('#addTable').hide();
// 🔥 BUTTON TOGGLE
$('#addButton').click(function () {
$('#addForm').show();
$('#addTable').hide();
});
$('#displayButton').click(function () {
$('#addForm').hide();
$('#addTable').show();
});
}
// ===============================
// STATE → DISTRICT // STATE → DISTRICT
// ===============================
if ($('#state_Id').length) {
$('#state_Id').change(function () { $('#state_Id').change(function () {
var stateId = $(this).val(); var stateId = $(this).val();
@@ -23,25 +50,24 @@ $(document).ready(function () {
districtDropdown.append('<option value="" disabled selected>Select District</option>'); districtDropdown.append('<option value="" disabled selected>Select District</option>');
data.forEach(function (district) { data.forEach(function (district) {
districtDropdown.append( districtDropdown.append(
'<option value="' + district.id + '">' + district.name + '</option>' '<option value="' + district.id + '">' + district.name + '</option>'
); );
}); });
districtDropdown.prop('disabled', false); districtDropdown.prop('disabled', false);
}
});
}
});
} }
});
}
});
// ===============================
// DISTRICT → BLOCK // DISTRICT → BLOCK
// ===============================
if ($('#district_Id').length) {
$('#district_Id').change(function () { $('#district_Id').change(function () {
var districtId = $(this).val(); var districtId = $(this).val();
@@ -60,25 +86,24 @@ $(document).ready(function () {
blockDropdown.append('<option value="" disabled selected>Select Block</option>'); blockDropdown.append('<option value="" disabled selected>Select Block</option>');
data.forEach(function (block) { data.forEach(function (block) {
blockDropdown.append( blockDropdown.append(
'<option value="' + block.id + '">' + block.name + '</option>' '<option value="' + block.id + '">' + block.name + '</option>'
); );
}); });
blockDropdown.prop('disabled', false); blockDropdown.prop('disabled', false);
}
});
}
});
} }
});
}
});
// ===============================
// VILLAGE NAME VALIDATION // VILLAGE NAME VALIDATION
// ===============================
if ($('#Village_Name').length) {
$('#Village_Name').on('input', function () { $('#Village_Name').on('input', function () {
var villageName = $(this).val(); var villageName = $(this).val();
@@ -96,13 +121,16 @@ $(document).ready(function () {
$('#villageMessage').text(''); $('#villageMessage').text('');
$('#submitVillage').prop('disabled', false); $('#submitVillage').prop('disabled', false);
}
});
} }
});
// ===============================
// CHECK DUPLICATE VILLAGE // CHECK DUPLICATE VILLAGE
// ===============================
if ($('#Village_Name').length && $('#block_Id').length) {
$('#Village_Name, #block_Id').on('change keyup', function () { $('#Village_Name, #block_Id').on('change keyup', function () {
var blockId = $('#block_Id').val(); var blockId = $('#block_Id').val();
@@ -111,7 +139,6 @@ $(document).ready(function () {
if (blockId && villageName) { if (blockId && villageName) {
$.ajax({ $.ajax({
url: '/check_village', url: '/check_village',
type: 'POST', type: 'POST',
@@ -137,9 +164,7 @@ $(document).ready(function () {
.css('color', 'green'); .css('color', 'green');
$('#submitVillage').prop('disabled', false); $('#submitVillage').prop('disabled', false);
} }
}, },
error: function () { error: function () {
@@ -149,50 +174,77 @@ $(document).ready(function () {
.css('color', 'red'); .css('color', 'red');
$('#submitVillage').prop('disabled', true); $('#submitVillage').prop('disabled', true);
}
});
}
});
} }
});
} // ===============================
// ADD VILLAGE (SAFE SCOPE FIX)
// ===============================
if ($('#villageForm').length) {
});
// ADD VILLAGE
$('#villageForm').submit(function (event) { $('#villageForm').submit(function (event) {
event.preventDefault(); event.preventDefault();
$.ajax({ $.ajax({
url: '/add_village', url: '/add_village',
type: 'POST', type: 'POST',
data: $(this).serialize(), data: $(this).serialize(),
success: function (response) { success: function (response) {
if (response.status === 'success') { if (response && response.status === 'success') {
alert('Village added successfully!'); alert(response.message || 'Village added successfully!');
// ✅ clear form
$('#villageForm')[0].reset();
// ✅ switch to table
$('#addForm').hide();
$('#addTable').show();
// optional refresh
location.reload(); location.reload();
} else { } else {
alert(response.message || 'Error adding village. Please try again.');
alert('Error adding village. Please try again.');
} }
}, },
error: function () { error: function () {
alert('An error occurred. Please try again.'); alert('An error occurred. Please try again.');
}
});
});
} }
}); });
});
// ===============================
// DELETE FUNCTION (SAFE)
// ===============================
function deleteVillage(villageId, element) {
if (!confirm("Are you sure you want to delete this village?")) return;
$.ajax({
url: '/delete_village/' + villageId,
type: 'GET',
dataType: 'json',
success: function (response) {
alert(response.message); // ✅ now shows "Village deleted successfully!"
if (element) $(element).closest("tr").remove();
},
error: function () {
alert("Error deleting village. Please try again.");
}
}); });
}

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Activity Logs</title> <title>Activity Logs</title>
@@ -9,29 +10,37 @@
background-color: #f8f9fa; background-color: #f8f9fa;
margin: 20px; margin: 20px;
} }
h2 { h2 {
text-align: center; text-align: center;
margin-bottom: 20px; margin-bottom: 20px;
} }
form { form {
display: flex; display: flex;
gap: 10px; gap: 10px;
justify-content: center; justify-content: center;
margin-bottom: 20px; margin-bottom: 20px;
flex-wrap: wrap;
} }
input, button {
input,
button {
padding: 8px; padding: 8px;
border-radius: 6px; border-radius: 6px;
border: 1px solid #ccc; border: 1px solid #ccc;
} }
button { button {
background-color: #007bff; background-color: #007bff;
color: white; color: white;
cursor: pointer; cursor: pointer;
} }
button:hover { button:hover {
background-color: #0056b3; background-color: #0056b3;
} }
table { table {
width: 90%; width: 90%;
margin: auto; margin: auto;
@@ -39,24 +48,29 @@
background: white; background: white;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1); box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
} }
th, td {
th,
td {
padding: 12px; padding: 12px;
border: 1px solid #ddd; border: 1px solid #ddd;
text-align: center; text-align: center;
} }
th { th {
background-color: #007bff; background-color: #007bff;
color: white; color: white;
} }
tr:nth-child(even) { tr:nth-child(even) {
background-color: #f2f2f2; background-color: #f2f2f2;
} }
</style> </style>
</head> </head>
<body> <body>
<h2>Activity Logs</h2> <h2>Activity Logs</h2>
<form method="get" action="{{ url_for('activity_log') }}" class="filter-form">
<form method="get" action="{{ url_for('log.activity_log') }}" class="filter-form">
<label for="username">Username:</label> <label for="username">Username:</label>
<input type="text" id="username" name="username" placeholder="Enter username" value="{{ username or '' }}"> <input type="text" id="username" name="username" placeholder="Enter username" value="{{ username or '' }}">
@@ -66,9 +80,8 @@
<label for="end_date">End Date:</label> <label for="end_date">End Date:</label>
<input type="date" id="end_date" name="end_date" value="{{ end_date or '' }}"> <input type="date" id="end_date" name="end_date" value="{{ end_date or '' }}">
<button type="submit" class="btn btn-primary">Filter</button> <button type="submit" class="btn btn-primary">Filter</button>
<!-- <button type="button" style="background-color: #6c757d;" onclick="resetFilter()">Reset</button> --> <button type="button" style="background-color: #6c757d;" onclick="resetFilter()">Reset</button>
</form> </form>
<table> <table>
@@ -95,8 +108,9 @@
<script> <script>
function resetFilter() { function resetFilter() {
window.location.href = "{{ url_for('activity_log') }}"; window.location.href = "{{ url_for('log.activity_log') }}";
} }
</script> </script>
</body> </body>
</html> </html>

View File

@@ -22,8 +22,12 @@
<div class="row1"> <div class="row1">
<div> <div>
<label for="subcontractor">Subcontractor Name:</label> <label for="subcontractor">Subcontractor Name:</label>
<!-- Text input for user-friendly autocomplete -->
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/> <input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
<!-- Hidden input for backend; must match model's Contractor_ID -->
<input type="hidden" id="subcontractor_id" name="Contractor_ID"/>
<div id="subcontractor_list" class="autocomplete-items"></div> <div id="subcontractor_list" class="autocomplete-items"></div>
</div> </div>
</div> </div>
@@ -37,19 +41,19 @@
</select><br><br> </select><br><br>
<label for="invoice_No">Invoice No:</label><br> <label for="invoice_No">Invoice No:</label><br>
<input type="text" id="invoice_No" name="invoice_No" required><br><br> <input type="text" id="invoice_No" name="Invoice_No" required><br><br>
<label for="basic_amount">Basic Amount:</label><br> <label for="basic_amount">Basic Amount:</label><br>
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required><br><br> <input type="number" step="0.01" id="basic_amount" name="Basic_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="final_amount">Final Amount:</label><br> <label for="final_amount">Final Amount:</label><br>
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required><br><br> <input type="number" step="0.01" id="final_amount" name="Final_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="total_amount">Total Amount:</label><br> <label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" placeholder="₹ - 00.00" required><br><br> <input type="number" step="0.01" id="total_amount" name="Total_Amount" placeholder="₹ - 00.00" required><br><br>
<label for="utr">UTR:</label><br> <label for="utr">UTR:</label><br>
<input type="text" id="utr" name="utr" required><br><br> <input type="text" id="utr" name="UTR" required><br><br>
<button type="submit">Submit GST Release</button> <button type="submit">Submit GST Release</button>
</form> </form>
@@ -68,10 +72,11 @@
{% endwith %} {% endwith %}
</div> </div>
<!-- GST Release History Table -->
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none;">
<div class="search-container"> <div class="search-container">
<h2>GST Release History</h2> <h2>GST Release History</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input type="text" id="searchBar" placeholder="Search..." onkeyup="searchTable()">
</div> </div>
<table id="sortableTable" border="1"> <table id="sortableTable" border="1">
<thead> <thead>
@@ -90,24 +95,22 @@
<tbody> <tbody>
{% for gst_rel in gst_releases %} {% for gst_rel in gst_releases %}
<tr> <tr>
<td>{{ gst_rel[0] }}</td> <td>{{ gst_rel.gst_release_id }}</td>
<td>{{ gst_rel[1] }}</td> <td>{{ gst_rel.pmc_no }}</td>
<td>{{ gst_rel[2] }}</td> <td>{{ gst_rel.invoice_no }}</td>
<td>{{ gst_rel[3] }}</td> <td>{{ gst_rel.basic_amount }}</td>
<td>{{ gst_rel[4] }}</td> <td>{{ gst_rel.final_amount }}</td>
<td>{{ gst_rel[5] }}</td> <td>{{ gst_rel.total_amount }}</td>
<td>{{ gst_rel[6] }}</td> <td>{{ gst_rel.utr }}</td>
<td> <td>
<a href="/edit_gst_release/{{ gst_rel[0] }}"> <a href="{{ url_for('gst_release_bp.edit_gst_release', gst_release_id=gst_rel.gst_release_id) }}">
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" <img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon">
class="icon">
</a> </a>
</td> </td>
<td> <td>
<a href="/delete_gst_release/{{ gst_rel[0] }}" <a href="{{ url_for('gst_release_bp.delete_gst_release', gst_release_id=gst_rel.gst_release_id) }}"
onclick="return confirm('Are you sure you want to delete this GST Release?')"> onclick="return confirm('Are you sure you want to delete this GST Release?')">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" <img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon">
class="icon">
</a> </a>
</td> </td>
</tr> </tr>
@@ -118,46 +121,61 @@
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Handle subcontractor autocomplete
document.getElementById("subcontractor").addEventListener("input", function () { const subcontractorInput = document.getElementById("subcontractor");
const subcontractorIdInput = document.getElementById("subcontractor_id");
const subcontractorList = document.getElementById("subcontractor_list");
const pmcDropdown = document.getElementById("PMC_No");
const form = document.querySelector('form');
// --------------------------
// Subcontractor autocomplete
// --------------------------
subcontractorInput.addEventListener("input", function () {
const query = this.value; const query = this.value;
const list = document.getElementById("subcontractor_list");
if (query.length < 2) { if (query.length < 2) {
list.innerHTML = ''; subcontractorList.innerHTML = '';
subcontractorIdInput.value = ''; // reset hidden id
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>'; // reset PMC dropdown
return; return;
} }
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`) fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
list.innerHTML = ''; subcontractorList.innerHTML = '';
data.results.forEach(item => { data.results.forEach(item => {
const div = document.createElement("div"); const div = document.createElement("div");
div.setAttribute("data-id", item.id); div.setAttribute("data-id", item.id);
div.textContent = item.name; div.textContent = item.name;
list.appendChild(div); subcontractorList.appendChild(div);
}); });
}); });
}); });
// Handle subcontractor selection // --------------------------
document.getElementById("subcontractor_list").addEventListener("click", function (e) { // Subcontractor selection
// --------------------------
subcontractorList.addEventListener("click", function (e) {
const selectedId = e.target.getAttribute("data-id"); const selectedId = e.target.getAttribute("data-id");
const selectedName = e.target.textContent; const selectedName = e.target.textContent;
if (selectedId) { if (selectedId) {
document.getElementById("subcontractor_id").value = selectedId; // Set hidden field for backend
document.getElementById("subcontractor").value = selectedName; subcontractorIdInput.value = selectedId;
document.getElementById("subcontractor_list").innerHTML = "";
// Update PMC dropdown for selected subcontractor // Set text input to selected name
subcontractorInput.value = selectedName;
// Clear the autocomplete list
subcontractorList.innerHTML = "";
// Fetch and populate PMC dropdown
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`) fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const pmcDropdown = document.getElementById("PMC_No");
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>'; pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
data.pmc_nos.forEach(pmc => { data.pmc_nos.forEach(pmc => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = pmc; option.value = pmc;
@@ -167,6 +185,22 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
} }
}); });
// --------------------------
// Form submit validation
// --------------------------
form.addEventListener('submit', function(e) {
if (!subcontractorIdInput.value) {
e.preventDefault();
alert("Please select a subcontractor from the list.");
subcontractorInput.focus();
} else if (!pmcDropdown.value) {
e.preventDefault();
alert("Please select a PMC No.");
pmcDropdown.focus();
}
});
}); });
</script> </script>

View File

@@ -1,5 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<head> <head>
<title>Manage Hold Types</title> <title>Manage Hold Types</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
@@ -7,6 +8,7 @@
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
</head> </head>
<body> <body>
<div class="button-container"> <div class="button-container">
@@ -46,8 +48,21 @@
<tr> <tr>
<td>{{ htd['hold_type_id'] }}</td> <td>{{ htd['hold_type_id'] }}</td>
<td>{{ htd['hold_type'] }}</td> <td>{{ htd['hold_type'] }}</td>
<td><a href="{{ url_for('hold_types.edit_hold_type', id=htd['hold_type_id']) }}">Edit</a></td> <td style="text-align:center;">
<td><button class="delete-button" data-id="{{ htd['hold_type_id'] }}">Delete</button></td> <a href="{{ url_for('hold_types.edit_hold_type', id=htd['hold_type_id']) }}">
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
class="icon">
</a>
</td>
<td style="text-align:center;">
<a href="{{ url_for('hold_types.delete_hold_type', id=htd['hold_type_id']) }}"
onclick="return confirm('Are you sure you want to delete this hold type?');">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
class="icon">
</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
@@ -56,4 +71,3 @@
</div> </div>
</body> </body>
{% endblock %} {% endblock %}

View File

@@ -1,47 +1,66 @@
{% extends 'base.html' %} {% extends 'base.html' %} {% block content %}
{% block content %}
<head xmlns="http://www.w3.org/1999/html"> <head xmlns="http://www.w3.org/1999/html">
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Add Invoice</title> <title>Add Invoice</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}"> <link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='css/invoice.css') }}"
/>
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script> <script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
<script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script> <script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script>
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
</head> </head>
<body> <body>
{% if success == 'true' %} {% if success == 'true' %}
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert"> <div
class="alert alert-success alert-dismissible fade show mt-3"
role="alert"
>
✅ Invoice added successfully! ✅ Invoice added successfully!
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button
type="button"
class="btn-close"
data-bs-dismiss="alert"
aria-label="Close"
></button>
</div> </div>
{% endif %} {% endif %}
<!-- Flash Messages --> <!-- Flash Messages -->
{% with messages = get_flashed_messages(with_categories=true) %} {% with messages = get_flashed_messages(with_categories=true) %} {% if
{% if messages %} messages %}
<div class="flash-messages"> <div class="flash-messages">
{% for category, message in messages %} {% for category, message in messages %}
<div class="alert {{ category }}">{{ message }}</div> <div class="alert {{ category }}">{{ message }}</div>
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %} {% endwith %}
{% endwith %}
<div class="button-container"> <div class="button-container">
<button id="addButton" class="action-button">Add</button> <button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button> <button id="displayButton" class="action-button">Display</button>
</div> </div>
<div id="addForm" style="display: none;"> <div id="addForm" style="display: none">
<h2>Add Invoice</h2> <h2>Add Invoice</h2>
<form id="invoiceForm" action="{{ url_for('invoice.add_invoice') }}" method="POST"> <form
id="invoiceForm"
action="{{ url_for('invoice.add_invoice') }}"
method="POST"
>
<div class="row1"> <div class="row1">
<div> <div>
<label for="subcontractor">Subcontractor Name:</label> <label for="subcontractor">Subcontractor Name:</label>
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/> <input
type="text"
id="subcontractor"
name="subcontractor"
required
autocomplete="off"
/>
<input type="hidden" id="subcontractor_id" name="subcontractor_id" /> <input type="hidden" id="subcontractor_id" name="subcontractor_id" />
<div id="subcontractor_list"></div> <div id="subcontractor_list"></div>
</div> </div>
@@ -53,7 +72,9 @@
<select id="village" name="village" required> <select id="village" name="village" required>
<option value="">-- Select Village --</option> <option value="">-- Select Village --</option>
{% for village in villages %} {% for village in villages %}
<option value="{{ village.Village_Name }}">{{ village.Village_Name }}</option> <option value="{{ village.Village_Name }}">
{{ village.Village_Name }}
</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
@@ -71,7 +92,11 @@
</div> </div>
<div> <div>
<label for="invoice_details">Invoice Details:</label> <label for="invoice_details">Invoice Details:</label>
<textarea id="invoice_details" name="invoice_details" required></textarea> <textarea
id="invoice_details"
name="invoice_details"
required
></textarea>
</div> </div>
</div> </div>
@@ -89,59 +114,161 @@
<div class="row3"> <div class="row3">
<div> <div>
<label for="basic_amount">Basic Amount:</label> <label for="basic_amount">Basic Amount:</label>
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="basic_amount"
name="basic_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
<div> <div>
<label for="debit_amount">Debit Amount:</label> <label for="debit_amount">Debit Amount:</label>
<input type="number" step="0.01" id="debit_amount" name="debit_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="debit_amount"
name="debit_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
<div> <div>
<label for="after_debit_amount">After Debit Amount:</label> <label for="after_debit_amount">After Debit Amount:</label>
<input type="number" step="0.01" id="after_debit_amount" name="after_debit_amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="after_debit_amount"
name="after_debit_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
</div> </div>
<div class="row3"> <div class="row3">
<div class="percentage-field"> <div class="percentage-field">
<label for="gst_percentage">GST %:</label> <label for="gst_percentage">GST %:</label>
<input type="number" step="0.01" id="gst_percentage" name="gst_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="gst_percentage"
name="gst_percentage"
placeholder="%"
/>
<label for="gst_amount">GST Amount:</label> <label for="gst_amount">GST Amount:</label>
<input type="number" step="0.01" id="gst_amount" name="gst_amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="gst_amount"
name="gst_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div> <div>
<label for="amount">Amount:</label> <label for="amount">Amount:</label>
<input type="number" step="0.01" id="amount" name="amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="amount"
name="amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div class="percentage-field"> <div class="percentage-field">
<label for="tds_percentage">TDS %:</label> <label for="tds_percentage">TDS %:</label>
<input type="number" step="0.01" id="tds_percentage" name="tds_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="tds_percentage"
name="tds_percentage"
placeholder="%"
/>
<label for="tds_amount">TDS Amount:</label> <label for="tds_amount">TDS Amount:</label>
<input type="number" step="0.01" id="tds_amount" name="tds_amount" placeholder="₹ - 00.00" readonly required/> <input
type="number"
step="0.01"
id="tds_amount"
name="tds_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
</div> </div>
<div class="row3"> <div class="row3">
<div class="percentage-field"> <div class="percentage-field">
<label for="sd_percentage">SD %:</label> <label for="sd_percentage">SD %:</label>
<input type="number" step="0.01" id="sd_percentage" name="sd_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="sd_percentage"
name="sd_percentage"
placeholder="%"
/>
<label for="sd_amount">SD Amount:</label> <label for="sd_amount">SD Amount:</label>
<input type="number" step="0.01" id="sd_amount" name="sd_amount" placeholder="₹ - 00.00" readonly required> <input
type="number"
step="0.01"
id="sd_amount"
name="sd_amount"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div class="percentage-field"> <div class="percentage-field">
<label for="commission_percentage">On Commission %:</label> <label for="commission_percentage">On Commission %:</label>
<input type="number" step="0.01" id="commission_percentage" name="commission_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="commission_percentage"
name="commission_percentage"
placeholder="%"
/>
<label for="on_commission">On Commission:</label> <label for="on_commission">On Commission:</label>
<input type="number" step="0.01" id="on_commission" name="on_commission" placeholder="₹ - 00.00" readonly required> <input
type="number"
step="0.01"
id="on_commission"
name="on_commission"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
<div class="percentage-field"> <div class="percentage-field">
<label for="hydro_percentage">Hydro Testing %:</label> <label for="hydro_percentage">Hydro Testing %:</label>
<input type="number" step="0.01" id="hydro_percentage" name="hydro_percentage" placeholder="%"/> <input
type="number"
step="0.01"
id="hydro_percentage"
name="hydro_percentage"
placeholder="%"
/>
<label for="hydro_testing">Hydro Testing:</label> <label for="hydro_testing">Hydro Testing:</label>
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing" placeholder="₹ - 00.00" readonly required> <input
type="number"
step="0.01"
id="hydro_testing"
name="hydro_testing"
placeholder="₹ - 00.00"
readonly
required
/>
</div> </div>
</div> </div>
<div class="hold-row"> <div class="hold-row">
<button type="button" id="add_hold_amount" class="button">+ Add Hold Amount</button> <button type="button" id="add_hold_amount" class="button">
+ Add Hold Amount
</button>
</div> </div>
<!-- Dynamically added hold amount fields --> <!-- Dynamically added hold amount fields -->
@@ -150,11 +277,25 @@
<div class="row2"> <div class="row2">
<div> <div>
<label for="gst_sd_amount">GST SD Amount:</label> <label for="gst_sd_amount">GST SD Amount:</label>
<input type="number" step="0.01" id="gst_sd_amount" name="gst_sd_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="gst_sd_amount"
name="gst_sd_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
<div> <div>
<label for="final_amount">Final Amount:</label> <label for="final_amount">Final Amount:</label>
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required/> <input
type="number"
step="0.01"
id="final_amount"
name="final_amount"
placeholder="₹ - 00.00"
required
/>
</div> </div>
</div> </div>
@@ -162,11 +303,16 @@
</form> </form>
</div> </div>
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none">
<!-- Invoice Table Section --> <!-- Invoice Table Section -->
<div class="search-container"> <div class="search-container">
<h2>Invoice List</h2> <h2>Invoice List</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input
type="text"
id="searchBar"
placeholder="Searching..."
onkeyup="searchTable()"
/>
{% if invoices %} {% if invoices %}
<table class="invoice-table"> <table class="invoice-table">
<thead> <thead>
@@ -218,14 +364,27 @@
<td>{{ invoice.Final_Amount }}</td> <td>{{ invoice.Final_Amount }}</td>
<td> <td>
<!-- Edit --> <!-- Edit -->
<a href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"> <a
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon"> href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"
>
<img
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
alt="Edit"
class="icon edit-btn"
data-id="{{ invoice.Invoice_Id }}"
/>
</a> </a>
</td> </td>
<td> <td>
<!-- Delete --> <a
<a href="{{ url_for('invoice.delete_invoice_route', invoice_id=invoice.Invoice_Id) }}" onclick="return confirm('Are you sure?')"> href="javascript:void(0);"
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon"> onclick="deleteInvoice({{ invoice.Invoice_Id }}, this)"
>
<img
src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
alt="Delete"
class="icon"
/>
</a> </a>
</td> </td>
</tr> </tr>
@@ -238,26 +397,28 @@
</div> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener("DOMContentLoaded", function () {
// Get all the input fields // Get all the input fields
const basicAmount = document.getElementById('basic_amount'); const basicAmount = document.getElementById("basic_amount");
const debitAmount = document.getElementById('debit_amount'); const debitAmount = document.getElementById("debit_amount");
const afterDebitAmount = document.getElementById('after_debit_amount'); const afterDebitAmount = document.getElementById("after_debit_amount");
const amount = document.getElementById('amount'); const amount = document.getElementById("amount");
// Percentage fields // Percentage fields
const gstPercentage = document.getElementById('gst_percentage'); const gstPercentage = document.getElementById("gst_percentage");
const gstAmount = document.getElementById('gst_amount'); const gstAmount = document.getElementById("gst_amount");
const tdsPercentage = document.getElementById('tds_percentage'); const tdsPercentage = document.getElementById("tds_percentage");
const tdsAmount = document.getElementById('tds_amount'); const tdsAmount = document.getElementById("tds_amount");
const sdPercentage = document.getElementById('sd_percentage'); const sdPercentage = document.getElementById("sd_percentage");
const sdAmountInput = document.getElementById('sd_amount'); const sdAmountInput = document.getElementById("sd_amount");
const commissionPercentage = document.getElementById('commission_percentage'); const commissionPercentage = document.getElementById(
const onCommission = document.getElementById('on_commission'); "commission_percentage",
const hydroPercentage = document.getElementById('hydro_percentage'); );
const hydroTesting = document.getElementById('hydro_testing'); const onCommission = document.getElementById("on_commission");
const gstSdAmount = document.getElementById('gst_sd_amount'); const hydroPercentage = document.getElementById("hydro_percentage");
const finalAmount = document.getElementById('final_amount'); const hydroTesting = document.getElementById("hydro_testing");
const gstSdAmount = document.getElementById("gst_sd_amount");
const finalAmount = document.getElementById("final_amount");
// Calculate after debit amount when basic or debit amount changes // Calculate after debit amount when basic or debit amount changes
function calculateAfterDebitAmount() { function calculateAfterDebitAmount() {
@@ -333,7 +494,9 @@ document.addEventListener('DOMContentLoaded', function() {
// Get hold amounts // Get hold amounts
let totalHold = 0; let totalHold = 0;
document.querySelectorAll('input[name="hold_amount[]"]').forEach(input => { document
.querySelectorAll('input[name="hold_amount[]"]')
.forEach((input) => {
totalHold += parseFloat(input.value) || 0; totalHold += parseFloat(input.value) || 0;
}); });
@@ -343,27 +506,30 @@ document.addEventListener('DOMContentLoaded', function() {
} }
// Add event listeners // Add event listeners
basicAmount.addEventListener('input', calculateAfterDebitAmount); basicAmount.addEventListener("input", calculateAfterDebitAmount);
debitAmount.addEventListener('input', calculateAfterDebitAmount); debitAmount.addEventListener("input", calculateAfterDebitAmount);
// Percentage fields // Percentage fields
gstPercentage.addEventListener('input', calculateGST); gstPercentage.addEventListener("input", calculateGST);
tdsPercentage.addEventListener('input', calculateOtherDeductions); tdsPercentage.addEventListener("input", calculateOtherDeductions);
sdPercentage.addEventListener('input', calculateOtherDeductions); sdPercentage.addEventListener("input", calculateOtherDeductions);
commissionPercentage.addEventListener('input', calculateOtherDeductions); commissionPercentage.addEventListener(
hydroPercentage.addEventListener('input', calculateOtherDeductions); "input",
calculateOtherDeductions,
);
hydroPercentage.addEventListener("input", calculateOtherDeductions);
// Listen for changes in hold amounts // Listen for changes in hold amounts
document.addEventListener('holdAmountChanged', calculateFinalAmount); document.addEventListener("holdAmountChanged", calculateFinalAmount);
}); });
// Optional JS for auto-hiding flash // Optional JS for auto-hiding flash
setTimeout(() => { setTimeout(() => {
document.querySelectorAll('.alert').forEach(el => el.style.display = 'none'); document
.querySelectorAll(".alert")
.forEach((el) => (el.style.display = "none"));
}, 5000); }, 5000);
</script> </script>
</div> </div>
</body> </body>

View File

@@ -1,5 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -8,6 +9,7 @@
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script> <script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
</head> </head>
<body> <body>
<div class="button-container"> <div class="button-container">
@@ -37,13 +39,16 @@
<input type="number" step="0.01" id="invoice_No" name="invoice_No"><br><br> <input type="number" step="0.01" id="invoice_No" name="invoice_No"><br><br>
<label for="Payment_Amount">Amount:</label><br> <label for="Payment_Amount">Amount:</label><br>
<input type="number" step="0.01" id="Payment_Amount" name="Payment_Amount" required oninput="calculateTDSAndTotal()"><br><br> <input type="number" step="0.01" id="Payment_Amount" name="Payment_Amount" required
oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Percentage">TDS Percentage (%):</label><br> <label for="TDS_Percentage">TDS Percentage (%):</label><br>
<input type="number" step="0.01" id="TDS_Percentage" name="TDS_Percentage" oninput="calculateTDSAndTotal()"><br><br> <input type="number" step="0.01" id="TDS_Percentage" name="TDS_Percentage"
oninput="calculateTDSAndTotal()"><br><br>
<label for="TDS_Payment_Amount">TDS Amount:</label><br> <label for="TDS_Payment_Amount">TDS Amount:</label><br>
<input type="number" step="0.01" id="TDS_Payment_Amount" name="TDS_Payment_Amount" required readonly><br><br> <input type="number" step="0.01" id="TDS_Payment_Amount" name="TDS_Payment_Amount" required
readonly><br><br>
<label for="total_amount">Total Amount:</label><br> <label for="total_amount">Total Amount:</label><br>
<input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br> <input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br>
@@ -89,8 +94,17 @@
<td>{{ payment[4] }}</td> <td>{{ payment[4] }}</td>
<td>{{ payment[5] }}</td> <td>{{ payment[5] }}</td>
<td>{{ payment[6] }}</td> <td>{{ payment[6] }}</td>
<td><a href="/edit_payment/{{ payment[0] }}"><img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon"></a></td> <td><a href="/edit_payment/{{ payment[0] }}"><img
<td><a href="/delete_payment/{{ payment[0] }}" onclick="return confirm('Are you sure you want to delete this payment?')"><img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon"></a></td> src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
class="icon"></a></td>
<td>
<a href="{{ url_for('payment_bp.delete_payment', payment_id=payment[0]) }}"
onclick="return confirm('Are you sure you want to delete this Payment?')">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
class="icon">
</a>
</td>
</tr> </tr>
<!-- <tr> <!-- <tr>
<td>{{ payment['Payment_Id'] }}</td> <td>{{ payment['Payment_Id'] }}</td>

View File

@@ -1,22 +1,24 @@
{% extends 'base.html' %} {% extends 'base.html' %} {% block content %}
{% block content %}
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Village Management</title> <title>Village Management</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}"> <link
rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='css/style.css') }}"
/>
<script src="{{ url_for('static', filename='js/village.js') }}"></script> <script src="{{ url_for('static', filename='js/village.js') }}"></script>
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script> <script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
</head> </head>
<body> <body>
<!-- Button Container to Center Buttons --> <!-- Button Container to Center Buttons -->
<div class="button-container"> <div class="button-container">
<button id="addButton" class="action-button">Add</button> <button id="addButton" class="action-button">Add</button>
<button id="displayButton" class="action-button">Display</button> <button id="displayButton" class="action-button">Display</button>
</div> </div>
<div id="addForm" style="display: none;"> <div id="addForm" style="display: none">
<div class="container"> <div class="container">
<div class="form-block"> <div class="form-block">
<h2>Add a New Village</h2> <h2>Add a New Village</h2>
@@ -40,7 +42,13 @@
</select> </select>
<label for="Village_Name">Village Name:</label> <label for="Village_Name">Village Name:</label>
<input type="text" id="Village_Name" name="Village_Name" placeholder="Enter Village Name" required> <input
type="text"
id="Village_Name"
name="Village_Name"
placeholder="Enter Village Name"
required
/>
<span id="villageMessage"></span> <span id="villageMessage"></span>
<button type="submit" id="submitVillage" disabled>Add Village</button> <button type="submit" id="submitVillage" disabled>Add Village</button>
@@ -49,10 +57,15 @@
</div> </div>
</div> </div>
<div id="addTable" style="display: none;"> <div id="addTable" style="display: none">
<div class="search-container"> <div class="search-container">
<h2>Display Villages</h2> <h2>Display Villages</h2>
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()"> <input
type="text"
id="searchBar"
placeholder="Searching..."
onkeyup="searchTable()"
/>
</div> </div>
<table id="sortableTable" border="1"> <table id="sortableTable" border="1">
@@ -63,12 +76,15 @@
<span class="sort-buttons"> <span class="sort-buttons">
<span class="sort-asc">⬆️</span> <span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span> <span class="sort-desc">⬇️</span>
</span></th> </span>
<th class="sortable-header">Block Name </th>
<th class="sortable-header">
Block Name
<span class="sort-buttons"> <span class="sort-buttons">
<span class="sort-asc">⬆️</span> <span class="sort-asc">⬆️</span>
<span class="sort-desc">⬇️</span> <span class="sort-desc">⬇️</span>
</span></th> </span>
</th>
<th>Update</th> <th>Update</th>
<th>Delete</th> <th>Delete</th>
</tr> </tr>
@@ -78,15 +94,20 @@
<td>{{ village[1] }}</td> <td>{{ village[1] }}</td>
<td>{{ village[2] }}</td> <td>{{ village[2] }}</td>
<td> <td>
<a href="{{ url_for('village.edit_village', village_id=village[0]) }}"> <a
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" href="{{ url_for('village.edit_village', village_id=village[0]) }}"
class="icon"> >
<img
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
alt="Edit"
class="icon"
/>
</a> </a>
</td> </td>
<td> <td>
<a href="{{ url_for('village.delete_village', village_id=village[0]) }}" <a href="javascript:void(0);"
onclick="return confirm('Are you sure you want to delete this village?');"> onclick="deleteVillage({{ village[0] }}, this)">
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" <img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
class="icon"> class="icon">
</a> </a>
</td> </td>
@@ -94,6 +115,15 @@
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
<!-- Flash Alerts -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<script>
{% for category, message in messages %}
alert("{{ message }}");
{% endfor %}
</script>
{% endif %}
{% endwith %}
</body> </body>
{% endblock %} {% endblock %}

Some files were not shown because too many files have changed in this diff Show More