Compare commits
24 Commits
315e972e1f
...
laxmi-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 82bedc3117 | |||
| cb68e454bc | |||
| 675301df7f | |||
| 7146391c18 | |||
| 94b5563d15 | |||
| 937018dc16 | |||
| eda238c235 | |||
| f184d6cecc | |||
| e7646eee76 | |||
| 64ca39944b | |||
|
|
6b4beb5af8 | ||
|
|
d092eb0527 | ||
|
|
f9e9612df5 | ||
|
|
46ec2c0276 | ||
|
|
630ee1744f | ||
| 2679554e98 | |||
| 0912aef85e | |||
| ce90c6b350 | |||
| bc20a53f26 | |||
| 42f69773ec | |||
| 79ea359b1c | |||
| acbea7942c | |||
| 13a4c5836e | |||
| af15b3934e |
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
venv/
|
||||
*.pyc
|
||||
__pycache__/
|
||||
|
||||
static/downloads/
|
||||
static/uploads/
|
||||
|
||||
Binary file not shown.
Binary file not shown.
7989
activity.log
7989
activity.log
File diff suppressed because it is too large
Load Diff
114
app.log
114
app.log
@@ -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] "[36mGET /static/css/index.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,315 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[36mGET /static/css/index.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,504 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,504 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,626 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[36mGET /static/js/validateFileInput.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,626 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[36mGET /static/js/validateFileInput.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,633 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[36mGET /static/js/searchContractor.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,633 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[36mGET /static/js/searchContractor.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:26,950 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[33mGET /favicon.ico HTTP/1.1[0m" 404 -
|
||||
2025-02-15 11:13:26,950 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:26] "[33mGET /favicon.ico HTTP/1.1[0m" 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] "[36mGET /static/css/index.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:28,933 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "[36mGET /static/css/index.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:28,952 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "[36mGET /static/js/validateFileInput.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:28,952 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "[36mGET /static/js/validateFileInput.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:28,954 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "[36mGET /static/js/searchContractor.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:28,955 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:28,954 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "[36mGET /static/js/searchContractor.js HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:28,955 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:28] "[36mGET /static/css/style.css HTTP/1.1[0m" 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] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:31,639 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:31,649 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:31,649 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:31,967 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:13:31,967 - INFO - 127.0.0.1 - - [15/Feb/2025 11:13:31] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:15:01,349 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:01] "[31m[1mPOST /check_state HTTP/1.1[0m" 409 -
|
||||
2025-02-15 11:15:01,349 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:01] "[31m[1mPOST /check_state HTTP/1.1[0m" 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] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:15:25,716 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:15:25,749 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:15:25,749 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:15:25,752 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:15:25,752 - INFO - 127.0.0.1 - - [15/Feb/2025 11:15:25] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 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] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:52,076 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:52,078 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:52,078 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:52,377 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:52,377 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:52] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 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] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:57,263 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "[36mGET /static/css/style.css HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:57,483 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:57,483 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "[36mGET /static/images/icons/pen_blue_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:57,567 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 304 -
|
||||
2025-02-15 11:16:57,567 - INFO - 127.0.0.1 - - [15/Feb/2025 11:16:57] "[36mGET /static/images/icons/bin_red_icon.png HTTP/1.1[0m" 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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
import config
|
||||
import mysql.connector
|
||||
|
||||
from model.State import State
|
||||
from model.Block import Block
|
||||
from model.Utilities import HtmlHelper
|
||||
|
||||
block_bp = Blueprint('block', __name__)
|
||||
|
||||
|
||||
# --- Add Block page -------
|
||||
@block_bp.route('/add_block', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def add_block():
|
||||
|
||||
block = Block()
|
||||
|
||||
if request.method == 'POST':
|
||||
block.AddBlock(request)
|
||||
return block.resultMessage
|
||||
|
||||
connection = config.get_db_connection()
|
||||
cursor = connection.cursor()
|
||||
|
||||
cursor.callproc("GetAllStates")
|
||||
for rs in cursor.stored_results():
|
||||
states = rs.fetchall()
|
||||
|
||||
state = State()
|
||||
states = state.GetAllStates(request=request)
|
||||
|
||||
block_data = block.GetAllBlocks(request)
|
||||
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
return render_template(
|
||||
'add_block.html',
|
||||
states=states,
|
||||
@@ -83,8 +74,15 @@ def edit_block(block_id):
|
||||
|
||||
if request.method == 'POST':
|
||||
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()
|
||||
cursor = connection.cursor()
|
||||
|
||||
|
||||
@@ -6,11 +6,10 @@ from model.State import State
|
||||
|
||||
district_bp = Blueprint('district', __name__)
|
||||
|
||||
|
||||
# ------- District page --------
|
||||
@district_bp.route('/add_district', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def add_district():
|
||||
|
||||
district = District()
|
||||
|
||||
if request.method == 'POST':
|
||||
@@ -28,7 +27,7 @@ def add_district():
|
||||
states=states
|
||||
)
|
||||
|
||||
|
||||
# ------- District check --------
|
||||
@district_bp.route('/check_district', methods=['POST'])
|
||||
@login_required
|
||||
def check_district():
|
||||
@@ -37,7 +36,7 @@ def check_district():
|
||||
|
||||
return district.CheckDistrict(request=request)
|
||||
|
||||
|
||||
# ------- District delete by district id --------
|
||||
@district_bp.route('/delete_district/<int:district_id>')
|
||||
@login_required
|
||||
def delete_district(district_id):
|
||||
@@ -52,6 +51,7 @@ def delete_district(district_id):
|
||||
return redirect(url_for('district.add_district'))
|
||||
|
||||
|
||||
# ------- District update by district id --------
|
||||
@district_bp.route('/edit_district/<int:district_id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def edit_district(district_id):
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
import os
|
||||
import config
|
||||
import ast
|
||||
import re
|
||||
from flask_login import login_required
|
||||
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 login_required
|
||||
from flask import Blueprint, request, render_template, redirect, url_for, jsonify
|
||||
|
||||
from model.Log import LogHelper
|
||||
import config
|
||||
from model.FolderAndFile import FolderAndFile
|
||||
|
||||
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 ----------------
|
||||
@excel_bp.route('/upload_excel_file', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@@ -30,9 +18,6 @@ def upload():
|
||||
if request.method == 'POST':
|
||||
file = request.files.get('file')
|
||||
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)
|
||||
|
||||
file.save(filepath)
|
||||
@@ -42,6 +27,7 @@ def upload():
|
||||
f"User {current_user.id} Upload Excel File '{file.filename}'"
|
||||
)
|
||||
return redirect(url_for('excel.show_table', filename=file.filename))
|
||||
|
||||
return render_template('uploadExcelFile.html')
|
||||
|
||||
|
||||
@@ -51,7 +37,6 @@ def show_table(filename):
|
||||
global data
|
||||
data = []
|
||||
|
||||
# filepath = os.path.join(get_upload_folder(), filename)
|
||||
filepath = FolderAndFile.get_upload_path(filename)
|
||||
wb = openpyxl.load_workbook(filepath, data_only=True)
|
||||
sheet = wb.active
|
||||
|
||||
@@ -1,70 +1,46 @@
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from model.gst_release import GSTReleasemodel
|
||||
# routes/gst_release_routes.py
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash
|
||||
from flask_login import login_required
|
||||
from model.gst_release import GSTRelease
|
||||
from model.Log import LogHelper
|
||||
|
||||
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'])
|
||||
@login_required
|
||||
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':
|
||||
pmc_no = request.form['PMC_No']
|
||||
invoice_no = request.form['invoice_No']
|
||||
basic_amount = request.form['basic_amount']
|
||||
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)
|
||||
|
||||
gst_service.AddGSTRelease(request)
|
||||
LogHelper.log_action("Add GST Release", "User added GST release")
|
||||
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
|
||||
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'])
|
||||
@login_required
|
||||
def edit_gst_release(gst_release_id):
|
||||
gst_release_data = GSTReleasemodel.fetch_gst_release_by_id(gst_release_id)
|
||||
if not gst_release_data:
|
||||
gst_data = gst_service.GetGSTReleaseByID(gst_release_id)
|
||||
if not gst_data:
|
||||
return "GST Release not found", 404
|
||||
|
||||
if request.method == 'POST':
|
||||
pmc_no = request.form['PMC_No']
|
||||
invoice_no = request.form['invoice_No']
|
||||
basic_amount = request.form['basic_amount']
|
||||
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)
|
||||
|
||||
gst_service.EditGSTRelease(request, gst_release_id)
|
||||
LogHelper.log_action("Edit GST Release", "User edited GST release")
|
||||
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
|
||||
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'])
|
||||
@login_required
|
||||
def delete_gst_release(gst_release_id):
|
||||
success, utr = GSTReleasemodel.delete_gst_release(gst_release_id)
|
||||
if not success:
|
||||
return jsonify({"message": "GST Release not found or failed to delete", "status": "error"}), 404
|
||||
|
||||
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
|
||||
gst_service.DeleteGSTRelease(gst_release_id)
|
||||
LogHelper.log_action("Delete GST Release", "User deleted GST release")
|
||||
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
|
||||
return redirect(url_for('gst_release_bp.add_gst_release'))
|
||||
@@ -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 model.payment import Paymentmodel
|
||||
from model.Log import LogHelper
|
||||
@@ -86,18 +86,16 @@ def edit_payment(payment_id):
|
||||
|
||||
return render_template('edit_payment.html', payment_data=payment_data)
|
||||
|
||||
|
||||
# ------------------- 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
|
||||
def delete_payment(payment_id):
|
||||
success, pmc_no, invoice_no = Paymentmodel.delete_payment(payment_id)
|
||||
|
||||
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}'")
|
||||
flash(f"Payment ID {payment_id} deleted successfully.", "success")
|
||||
|
||||
LogHelper.log_action("Delete Payment", f"User {current_user.id} deleted Payment '{payment_id}'")
|
||||
|
||||
return jsonify({
|
||||
"message": f"Payment ID {payment_id} deleted successfully.",
|
||||
"status": "success"
|
||||
}), 200
|
||||
return redirect(url_for('payment_bp.add_payment'))
|
||||
@@ -1,9 +1,11 @@
|
||||
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
|
||||
|
||||
pmc_report_bp = Blueprint("pmc_report", __name__)
|
||||
|
||||
# ---------------- Contractor Report by pmc no ----------------
|
||||
@pmc_report_bp.route("/pmc_report/<pmc_no>")
|
||||
@login_required
|
||||
def pmc_report(pmc_no):
|
||||
@@ -23,6 +25,7 @@ def pmc_report(pmc_no):
|
||||
total=data["total"]
|
||||
)
|
||||
|
||||
# ---------------- Contractor Download Report by pmc no ----------------
|
||||
@pmc_report_bp.route("/download_pmc_report/<pmc_no>")
|
||||
@login_required
|
||||
def download_pmc_report(pmc_no):
|
||||
|
||||
@@ -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 model.Report import ReportHelper
|
||||
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__)
|
||||
@@ -36,7 +31,7 @@ def search_contractor():
|
||||
|
||||
return jsonify(data)
|
||||
|
||||
# ---------------- Contractor Report ----------------
|
||||
# ---------------- Contractor Report by contractor id ----------------
|
||||
@report_bp.route('/contractor_report/<int:contractor_id>')
|
||||
@login_required
|
||||
def contractor_report(contractor_id):
|
||||
@@ -49,145 +44,10 @@ def contractor_report(contractor_id):
|
||||
**data
|
||||
)
|
||||
|
||||
# ---------------- Contractor Download Report by contractor id ----------------
|
||||
@report_bp.route('/download_report/<int:contractor_id>')
|
||||
@login_required
|
||||
def download_report(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)
|
||||
|
||||
@@ -4,7 +4,7 @@ from model.State import State
|
||||
|
||||
state_bp = Blueprint('state', __name__)
|
||||
|
||||
|
||||
# ----- State page ------
|
||||
@state_bp.route('/add_state', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def add_state():
|
||||
@@ -19,7 +19,7 @@ def add_state():
|
||||
|
||||
return render_template('add_state.html', statedata=statedata)
|
||||
|
||||
|
||||
# ----- State check ------
|
||||
@state_bp.route('/check_state', methods=['POST'])
|
||||
@login_required
|
||||
def check_state():
|
||||
@@ -28,21 +28,21 @@ def check_state():
|
||||
|
||||
return state.CheckState(request=request)
|
||||
|
||||
|
||||
# ----- State delete by state id ------
|
||||
@state_bp.route('/delete_state/<int:id>')
|
||||
@login_required
|
||||
def deleteState(id):
|
||||
|
||||
state = State()
|
||||
|
||||
msg = state.DeleteState(request=request, id=id)
|
||||
state.DeleteState(request=request, id=id)
|
||||
|
||||
if not state.isSuccess:
|
||||
return state.resultMessage
|
||||
else:
|
||||
return redirect(url_for('state.add_state'))
|
||||
|
||||
|
||||
# ----- State update by state id ------
|
||||
@state_bp.route('/edit_state/<int:id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def editState(id):
|
||||
|
||||
@@ -1,34 +1,11 @@
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
||||
from flask_login import login_required
|
||||
from model.Subcontractor import Subcontractor
|
||||
from model.Utilities import HtmlHelper, ResponseHandler
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -19,7 +19,7 @@ village_bp = Blueprint('village', __name__)
|
||||
def add_village():
|
||||
|
||||
village = Village()
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
village.AddVillage(request=request)
|
||||
return village.resultMessage
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json
|
||||
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 import Blueprint, render_template, request, redirect, url_for, jsonify
|
||||
|
||||
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
|
||||
from model.Log import LogData, LogHelper
|
||||
@@ -151,7 +147,7 @@ class Block:
|
||||
|
||||
self.isSuccess = block.isSuccess
|
||||
self.resultMessage = block.resultMessage
|
||||
return render_template('add_block.html')
|
||||
return
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Delete Block
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json
|
||||
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.Utilities import ItemCRUDType
|
||||
from model.ItemCRUD import ItemCRUD
|
||||
|
||||
class District:
|
||||
|
||||
isSuccess = False
|
||||
@@ -24,7 +10,7 @@ class District:
|
||||
self.isSuccess = False
|
||||
self.resultMessage = ""
|
||||
|
||||
|
||||
# edit district
|
||||
def EditDistrict(self, request, district_id):
|
||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||
|
||||
@@ -36,7 +22,7 @@ class District:
|
||||
self.resultMessage = district.resultMessage
|
||||
return
|
||||
|
||||
|
||||
# add district
|
||||
def AddDistrict(self, request):
|
||||
|
||||
district = ItemCRUD(ItemCRUDType.District)
|
||||
@@ -50,7 +36,7 @@ class District:
|
||||
return
|
||||
|
||||
|
||||
|
||||
# get all district data
|
||||
def GetAllDistricts(self, request):
|
||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||
districtsdata = district.GetAllData(request=request, storedproc="GetAllDistricts")
|
||||
@@ -58,7 +44,7 @@ class District:
|
||||
self.resultMessage = district.resultMessage
|
||||
return districtsdata
|
||||
|
||||
|
||||
# check district validation
|
||||
def CheckDistrict(self, request):
|
||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||
district_name = request.json.get('district_Name', '').strip()
|
||||
@@ -68,13 +54,7 @@ class District:
|
||||
self.resultMessage = district.resultMessage
|
||||
return result
|
||||
|
||||
|
||||
# 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
|
||||
# get district by district id
|
||||
def GetDistrictByID(self, request, district_id):
|
||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||
|
||||
@@ -92,7 +72,7 @@ class District:
|
||||
return districtdata
|
||||
|
||||
|
||||
#Delete District
|
||||
# Delete District by district id
|
||||
def DeleteDistrict(self, request, district_id):
|
||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||
district.DeleteItem(request=request,itemID=district_id,storedprocDelete="DeleteDistrict")
|
||||
|
||||
78
model/GST.py
78
model/GST.py
@@ -1,55 +1,51 @@
|
||||
import config
|
||||
from model.ItemCRUD import ItemCRUD
|
||||
from model.Utilities import ItemCRUDType
|
||||
|
||||
class GST:
|
||||
|
||||
@staticmethod
|
||||
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()
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
if not invoice_crud.isSuccess:
|
||||
return [] # Could also log invoice_crud.resultMessage
|
||||
|
||||
try:
|
||||
# ----------- Invoices -----------
|
||||
cursor.callproc('GetAllInvoicesBasic')
|
||||
invoices = []
|
||||
for result in cursor.stored_results():
|
||||
invoices = result.fetchall()
|
||||
invoices = [
|
||||
dict(
|
||||
Invoice_No=row[1],
|
||||
GST_SD_Amount=float(row[2]) if row[2] is not None else 0
|
||||
)
|
||||
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 -----------
|
||||
cursor.callproc('GetAllGSTReleasesBasic')
|
||||
gst_releases = []
|
||||
for result in cursor.stored_results():
|
||||
gst_releases = result.fetchall()
|
||||
if not gst_crud.isSuccess:
|
||||
return [] # Could also log gst_crud.resultMessage
|
||||
|
||||
gst_invoice_nos = {
|
||||
g['Invoice_No']
|
||||
for g in gst_releases
|
||||
if g['Invoice_No']
|
||||
}
|
||||
gst_invoice_nos = {
|
||||
g[2] # Invoice_No is at index 2
|
||||
for g in gst_rows
|
||||
if g[2]
|
||||
}
|
||||
|
||||
gst_basic_amounts = {
|
||||
float(g['Basic_Amount'])
|
||||
for g in gst_releases
|
||||
if g['Basic_Amount'] is not None
|
||||
}
|
||||
gst_basic_amounts = {
|
||||
float(g[3]) # Basic_Amount at index 3
|
||||
for g in gst_rows
|
||||
if g[3] is not None
|
||||
}
|
||||
|
||||
unreleased = []
|
||||
# Filter unreleased invoices
|
||||
unreleased = []
|
||||
for inv in invoices:
|
||||
match_by_invoice = inv['Invoice_No'] in gst_invoice_nos
|
||||
match_by_gst_amount = inv['GST_SD_Amount'] in gst_basic_amounts
|
||||
|
||||
for inv in invoices:
|
||||
if not (match_by_invoice or match_by_gst_amount):
|
||||
unreleased.append(inv)
|
||||
|
||||
match_by_invoice = inv['Invoice_No'] in gst_invoice_nos
|
||||
|
||||
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):
|
||||
unreleased.append(inv)
|
||||
|
||||
return unreleased
|
||||
|
||||
finally:
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
return unreleased
|
||||
@@ -6,7 +6,6 @@ import config
|
||||
import re
|
||||
import mysql.connector
|
||||
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Mapping Class
|
||||
# ----------------------------------------------------------
|
||||
@@ -23,10 +22,11 @@ class itemCRUDMapping:
|
||||
self.name = "Hold Type"
|
||||
elif itemType is ItemCRUDType.Subcontractor:
|
||||
self.name = "Subcontractor"
|
||||
elif itemType.name == "GSTRelease":
|
||||
self.name = "GSTRelease"
|
||||
else:
|
||||
self.name = "Item"
|
||||
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Generic CRUD Class
|
||||
# ----------------------------------------------------------
|
||||
@@ -93,13 +93,47 @@ class ItemCRUD:
|
||||
|
||||
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'],))
|
||||
|
||||
existing_item = None
|
||||
for rs in cursor.stored_results():
|
||||
existing_item = rs.fetchone()
|
||||
@@ -111,7 +145,6 @@ class ItemCRUD:
|
||||
)
|
||||
return
|
||||
|
||||
# Insert
|
||||
cursor.callproc(storedprocadd, (
|
||||
data['Contractor_Name'],
|
||||
data['Address'],
|
||||
@@ -123,17 +156,16 @@ class ItemCRUD:
|
||||
data['GST_No'],
|
||||
data['Contractor_password']
|
||||
))
|
||||
|
||||
connection.commit()
|
||||
|
||||
self.isSuccess = True
|
||||
self.resultMessage = HtmlHelper.json_response(
|
||||
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
# ======================================================
|
||||
# NORMAL (Village / Block / State)
|
||||
# NORMAL SINGLE-FIELD (Village / Block / State)
|
||||
# ======================================================
|
||||
if not re.match(RegEx.patternAlphabetOnly, childname):
|
||||
self.isSuccess = False
|
||||
@@ -142,7 +174,6 @@ class ItemCRUD:
|
||||
)
|
||||
return
|
||||
|
||||
# Duplicate check
|
||||
if parentid is None:
|
||||
cursor.callproc(storedprocfetch, (childname,))
|
||||
else:
|
||||
@@ -159,17 +190,14 @@ class ItemCRUD:
|
||||
)
|
||||
return
|
||||
|
||||
# Insert
|
||||
if parentid is None:
|
||||
cursor.callproc(storedprocadd, (childname,))
|
||||
else:
|
||||
cursor.callproc(storedprocadd, (childname, parentid))
|
||||
|
||||
connection.commit()
|
||||
|
||||
self.isSuccess = True
|
||||
self.resultMessage = HtmlHelper.json_response(
|
||||
|
||||
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
|
||||
)
|
||||
|
||||
@@ -199,9 +227,33 @@ class ItemCRUD:
|
||||
|
||||
try:
|
||||
# ======================================================
|
||||
# SUBCONTRACTOR (MULTI-FIELD)
|
||||
# GSTRelease MULTI-FIELD
|
||||
# ======================================================
|
||||
if data:
|
||||
if self.itemCRUDType.name == "GSTRelease" and data:
|
||||
|
||||
cursor.callproc(storedprocupdate, (
|
||||
childid,
|
||||
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.update_success(self.itemCRUDMapping.name), 200
|
||||
)
|
||||
return
|
||||
|
||||
# ======================================================
|
||||
# SUBCONTRACTOR MULTI-FIELD
|
||||
# ======================================================
|
||||
if self.itemCRUDType.name == "Subcontractor" and data:
|
||||
|
||||
cursor.callproc(storedprocupdate, (
|
||||
childid,
|
||||
data['Contractor_Name'],
|
||||
@@ -214,9 +266,7 @@ class ItemCRUD:
|
||||
data['GST_No'],
|
||||
data['Contractor_password']
|
||||
))
|
||||
|
||||
connection.commit()
|
||||
|
||||
self.isSuccess = True
|
||||
self.resultMessage = HtmlHelper.json_response(
|
||||
ResponseHandler.update_success(self.itemCRUDMapping.name), 200
|
||||
@@ -224,7 +274,7 @@ class ItemCRUD:
|
||||
return
|
||||
|
||||
# ======================================================
|
||||
# NORMAL
|
||||
# NORMAL SINGLE-FIELD
|
||||
# ======================================================
|
||||
if not re.match(RegEx.patternAlphabetOnly, childname):
|
||||
self.isSuccess = False
|
||||
@@ -237,7 +287,6 @@ class ItemCRUD:
|
||||
cursor.callproc(storedprocupdate, (childid, parentid, childname))
|
||||
|
||||
connection.commit()
|
||||
|
||||
self.isSuccess = True
|
||||
self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message']
|
||||
|
||||
@@ -259,20 +308,15 @@ class ItemCRUD:
|
||||
|
||||
data = []
|
||||
connection = config.get_db_connection()
|
||||
|
||||
if not connection:
|
||||
return []
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
try:
|
||||
cursor.callproc(storedproc)
|
||||
|
||||
for result in cursor.stored_results():
|
||||
data = result.fetchall()
|
||||
|
||||
self.isSuccess = True
|
||||
|
||||
except mysql.connector.Error as e:
|
||||
print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
|
||||
self.isSuccess = False
|
||||
@@ -280,7 +324,6 @@ class ItemCRUD:
|
||||
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
|
||||
)
|
||||
return []
|
||||
|
||||
finally:
|
||||
cursor.close()
|
||||
connection.close()
|
||||
@@ -298,13 +341,10 @@ class ItemCRUD:
|
||||
|
||||
try:
|
||||
cursor.callproc(storedproc, (id,))
|
||||
|
||||
for rs in cursor.stored_results():
|
||||
data = rs.fetchone()
|
||||
|
||||
except mysql.connector.Error as e:
|
||||
print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
|
||||
|
||||
finally:
|
||||
cursor.close()
|
||||
connection.close()
|
||||
@@ -353,7 +393,6 @@ class ItemCRUD:
|
||||
return HtmlHelper.json_response(
|
||||
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
|
||||
)
|
||||
|
||||
finally:
|
||||
cursor.close()
|
||||
connection.close()
|
||||
123
model/Log.py
123
model/Log.py
@@ -1,39 +1,29 @@
|
||||
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json
|
||||
from flask import current_app
|
||||
|
||||
from datetime import datetime
|
||||
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
|
||||
|
||||
class LogHelper:
|
||||
@staticmethod
|
||||
def log_action(action, details=""):
|
||||
"""Log user actions with timestamp, user, action, and details."""
|
||||
logData = LogData()
|
||||
logData.WriteLog(action, details="")
|
||||
"""Add a log entry."""
|
||||
log_data = LogData()
|
||||
log_data.add_log(action, details)
|
||||
|
||||
|
||||
class LogData:
|
||||
|
||||
filepath = ""
|
||||
timestamp = None
|
||||
|
||||
def __init__(self):
|
||||
self.filepath = os.path.join(current_app.root_path, 'activity.log')
|
||||
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.user = getattr(current_user, "cn", None) \
|
||||
or getattr(current_user, "username", None) \
|
||||
or getattr(current_user, "sAMAccountName", "Unknown")
|
||||
|
||||
if hasattr(current_user, "cn") and current_user.cn:
|
||||
self.user = current_user.cn
|
||||
elif hasattr(current_user, "username") and current_user.username:
|
||||
self.user = current_user.username
|
||||
elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName:
|
||||
self.user = current_user.sAMAccountName
|
||||
else:
|
||||
self.user = "Unknown"
|
||||
|
||||
def WriteLog(self, action, details=""):
|
||||
"""Log user actions with timestamp, user, action, and details."""
|
||||
|
||||
def add_log(self, action, details=""):
|
||||
"""Create/Add a log entry."""
|
||||
with open(self.filepath, "a", encoding="utf-8") as f:
|
||||
f.write(
|
||||
f"Timestamp: {self.timestamp} | "
|
||||
@@ -42,46 +32,73 @@ class LogData:
|
||||
f"Details: {details}\n"
|
||||
)
|
||||
|
||||
|
||||
def GetActivitiesLog(self):
|
||||
def get_all_logs(self):
|
||||
"""Read all logs."""
|
||||
logs = []
|
||||
|
||||
if os.path.exists(self.filepath):
|
||||
with open(self.filepath, 'r') as f:
|
||||
with open(self.filepath, 'r', encoding="utf-8") as f:
|
||||
for line in f:
|
||||
parts = line.strip().split(" | ")
|
||||
if len(parts) == 4:
|
||||
logs.append({
|
||||
"timestamp": parts[0].replace("Timestamp:", "").strip(),
|
||||
"user": parts[1].replace("User:", "").strip(),
|
||||
"action": parts[2].replace("Action:", "").strip(),
|
||||
"details": parts[3].replace("Details:", "").strip()
|
||||
"timestamp": parts[0].split(":", 1)[1].strip(),
|
||||
"user": parts[1].split(":", 1)[1].strip(),
|
||||
"action": parts[2].split(":", 1)[1].strip(),
|
||||
"details": parts[3].split(":", 1)[1].strip()
|
||||
})
|
||||
return logs
|
||||
|
||||
def GetFilteredActivitiesLog(self, startDate, endDate, userName):
|
||||
def get_filtered_logs(self, start_date=None, end_date=None, user_name=None):
|
||||
"""Filter logs by date and/or user."""
|
||||
logs = self.get_all_logs()
|
||||
|
||||
filtered_logs = self.GetActivitiesLog()
|
||||
# Filter by date
|
||||
if start_date or end_date:
|
||||
start_dt = datetime.strptime(start_date, "%Y-%m-%d") if start_date else datetime.min
|
||||
end_dt = datetime.strptime(end_date, "%Y-%m-%d") if end_date else datetime.max
|
||||
logs = [
|
||||
log for log in logs
|
||||
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
||||
]
|
||||
|
||||
# Date filter
|
||||
if startDate or endDate:
|
||||
try:
|
||||
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
|
||||
# Filter by username
|
||||
if user_name:
|
||||
logs = [log for log in logs if user_name.lower() in log.get("user", "").lower()]
|
||||
|
||||
return logs
|
||||
|
||||
def update_log(self, index, action=None, details=None):
|
||||
"""Update a specific log entry by index (0-based)."""
|
||||
logs = self.get_all_logs()
|
||||
if 0 <= index < len(logs):
|
||||
if action:
|
||||
logs[index]["action"] = action
|
||||
if details:
|
||||
logs[index]["details"] = details
|
||||
self._rewrite_logs_file(logs)
|
||||
return True
|
||||
return False
|
||||
|
||||
def delete_log(self, index):
|
||||
"""Delete a specific log entry by index (0-based)."""
|
||||
logs = self.get_all_logs()
|
||||
if 0 <= index < len(logs):
|
||||
logs.pop(index)
|
||||
self._rewrite_logs_file(logs)
|
||||
return True
|
||||
return False
|
||||
|
||||
# ------------------- INTERNAL HELPER -------------------
|
||||
|
||||
def _rewrite_logs_file(self, logs):
|
||||
"""Overwrite the log file with current logs."""
|
||||
with open(self.filepath, "w", encoding="utf-8") as f:
|
||||
for log in logs:
|
||||
f.write(
|
||||
f"Timestamp: {log['timestamp']} | "
|
||||
f"User: {log['user']} | "
|
||||
f"Action: {log['action']} | "
|
||||
f"Details: {log['details']}\n"
|
||||
)
|
||||
|
||||
|
||||
filtered_logs = [
|
||||
log for log in filtered_logs
|
||||
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
||||
]
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print("Date filter error:", e)
|
||||
#Why catching all exceptions? Need to handle specific exceptions
|
||||
|
||||
# Username filter
|
||||
if userName:
|
||||
filtered_logs = [log for log in filtered_logs if userName.lower() in log["user"].lower()]
|
||||
|
||||
return filtered_logs
|
||||
@@ -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 re
|
||||
import mysql.connector
|
||||
from flask import request, redirect, url_for
|
||||
|
||||
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
|
||||
from model.ItemCRUD import ItemCRUD
|
||||
|
||||
|
||||
class State:
|
||||
@@ -20,7 +16,6 @@ class State:
|
||||
# ADD STATE (USING ITEM CRUD)
|
||||
# ----------------------------------------------------------
|
||||
def AddState(self, request):
|
||||
|
||||
state_name = request.form['state_Name'].strip()
|
||||
|
||||
crud = ItemCRUD(ItemCRUDType.State)
|
||||
@@ -42,16 +37,14 @@ class State:
|
||||
# GET ALL STATES (NO CHANGE - THIS IS CORRECT)
|
||||
# ----------------------------------------------------------
|
||||
def GetAllStates(self, request):
|
||||
|
||||
connection = config.get_db_connection()
|
||||
data = []
|
||||
|
||||
if not connection:
|
||||
return []
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
cursor.callproc("GetAllStates")
|
||||
for res in cursor.stored_results():
|
||||
data = res.fetchall()
|
||||
@@ -77,7 +70,6 @@ class State:
|
||||
# CHECK STATE (USING ITEM CRUD)
|
||||
# ----------------------------------------------------------
|
||||
def CheckState(self, request):
|
||||
|
||||
state_name = request.json.get('state_Name', '').strip()
|
||||
|
||||
crud = ItemCRUD(ItemCRUDType.State)
|
||||
@@ -94,7 +86,6 @@ class State:
|
||||
# DELETE STATE (USING ITEM CRUD)
|
||||
# ----------------------------------------------------------
|
||||
def DeleteState(self, request, id):
|
||||
|
||||
crud = ItemCRUD(ItemCRUDType.State)
|
||||
|
||||
crud.DeleteItem(
|
||||
@@ -113,7 +104,6 @@ class State:
|
||||
# EDIT STATE (USING ITEM CRUD)
|
||||
# ----------------------------------------------------------
|
||||
def EditState(self, request, id):
|
||||
|
||||
state_name = request.form['state_Name'].strip()
|
||||
|
||||
crud = ItemCRUD(ItemCRUDType.State)
|
||||
@@ -136,16 +126,14 @@ class State:
|
||||
# GET STATE BY ID (KEEP SAME)
|
||||
# ----------------------------------------------------------
|
||||
def GetStateByID(self, request, id):
|
||||
|
||||
connection = config.get_db_connection()
|
||||
data = None
|
||||
|
||||
if not connection:
|
||||
return None
|
||||
|
||||
cursor = connection.cursor()
|
||||
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
cursor.callproc("GetStateByID", (id,))
|
||||
for res in cursor.stored_results():
|
||||
data = res.fetchone()
|
||||
|
||||
@@ -8,7 +8,7 @@ class ItemCRUDType(Enum):
|
||||
State = 4
|
||||
HoldType = 5
|
||||
Subcontractor = 6
|
||||
|
||||
GSTRelease = 7
|
||||
|
||||
class RegEx:
|
||||
patternAlphabetOnly = "^[A-Za-z ]+$"
|
||||
@@ -62,5 +62,5 @@ class HtmlHelper:
|
||||
@staticmethod
|
||||
def json_response(message_obj, status_code):
|
||||
return jsonify(message_obj), status_code
|
||||
#May need to refactor further
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# return blocks
|
||||
|
||||
|
||||
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
|
||||
import config
|
||||
import mysql.connector
|
||||
from model.ItemCRUD import ItemCRUD
|
||||
|
||||
|
||||
|
||||
class Village:
|
||||
isSuccess = False
|
||||
resultMessage = ""
|
||||
@@ -12,12 +13,19 @@ class Village:
|
||||
def __init__(self):
|
||||
self.isSuccess = False
|
||||
self.resultMessage = ""
|
||||
self.response = {} # ✅ ADDED
|
||||
self.village = ItemCRUD(itemType=ItemCRUDType.Village)
|
||||
|
||||
# 🔹 Helper: sync status
|
||||
def _set_status(self, village):
|
||||
self.isSuccess = village.isSuccess
|
||||
self.resultMessage = village.resultMessage
|
||||
|
||||
# ✅ UPDATED (safe handling)
|
||||
if hasattr(village, "response"):
|
||||
self.response = village.response
|
||||
self.resultMessage = village.response.get("message", "")
|
||||
else:
|
||||
self.resultMessage = village.resultMessage
|
||||
|
||||
# 🔹 Helper: get request data
|
||||
def _get_form_data(self, request):
|
||||
@@ -29,8 +37,9 @@ class Village:
|
||||
block_id, village_name = self._get_form_data(request)
|
||||
|
||||
if not village_name:
|
||||
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
|
||||
self.resultMessage = self.response["message"]
|
||||
self.isSuccess = False
|
||||
self.resultMessage = "Village name cannot be empty"
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -66,8 +75,9 @@ class Village:
|
||||
block_id, village_name = self._get_form_data(request)
|
||||
|
||||
if not village_name:
|
||||
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
|
||||
self.resultMessage = self.response["message"]
|
||||
self.isSuccess = False
|
||||
self.resultMessage = "Village name cannot be empty"
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -103,8 +113,9 @@ class Village:
|
||||
block_id, village_name = self._get_form_data(request)
|
||||
|
||||
if not village_name:
|
||||
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
|
||||
self.resultMessage = self.response["message"]
|
||||
self.isSuccess = False
|
||||
self.resultMessage = "Village name cannot be empty"
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -164,9 +175,11 @@ class Village:
|
||||
except mysql.connector.Error as e:
|
||||
print(f"Error fetching blocks: {e}")
|
||||
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 []
|
||||
|
||||
finally:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,150 +1,150 @@
|
||||
import config
|
||||
import mysql.connector
|
||||
# model/gst_release.py
|
||||
from flask import request, jsonify
|
||||
from model.ItemCRUD import ItemCRUD
|
||||
from model.Utilities import ItemCRUDType
|
||||
|
||||
class GSTReleasemodel:
|
||||
class GSTRelease:
|
||||
|
||||
@staticmethod
|
||||
def get_connection():
|
||||
connection = config.get_db_connection()
|
||||
if not connection:
|
||||
return None
|
||||
return connection
|
||||
def __init__(self):
|
||||
self.isSuccess = False
|
||||
self.resultMessage = ""
|
||||
|
||||
@staticmethod
|
||||
def fetch_all_gst_releases():
|
||||
connection = GSTReleasemodel.get_connection()
|
||||
gst_releases = []
|
||||
if connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
try:
|
||||
cursor.callproc('GetAllGSTReleases')
|
||||
|
||||
gst_releases = []
|
||||
for result in cursor.stored_results(): # change to procedure
|
||||
gst_releases = result.fetchall()
|
||||
|
||||
except mysql.connector.Error as e:
|
||||
print(f"Error fetching GST releases: {e}")
|
||||
finally:
|
||||
cursor.close()
|
||||
connection.close()
|
||||
return gst_releases
|
||||
|
||||
@staticmethod
|
||||
def insert_gst_release(pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id):
|
||||
connection = GSTReleasemodel.get_connection()
|
||||
if not connection:
|
||||
return False
|
||||
# ------------------- Add GST Release -------------------
|
||||
def AddGSTRelease(self, request):
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||
|
||||
# Insert into gst_release
|
||||
cursor.callproc(
|
||||
'InsertGSTReleaseOnly',
|
||||
[pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id]
|
||||
data = {
|
||||
"PMC_No": request.form.get("PMC_No", "").strip(),
|
||||
"Invoice_No": request.form.get("Invoice_No", "").strip(),
|
||||
"Basic_Amount": float(request.form.get("Basic_Amount", 0) or 0),
|
||||
"Final_Amount": float(request.form.get("Final_Amount", 0) or 0),
|
||||
"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)
|
||||
}
|
||||
|
||||
# Add GST Release
|
||||
gst.AddItem(
|
||||
request=request,
|
||||
data=data,
|
||||
storedprocfetch="CheckGSTReleaseExists",
|
||||
storedprocadd="AddGSTReleaseFromExcel"
|
||||
)
|
||||
|
||||
# Insert into inpayment
|
||||
cursor.callproc(
|
||||
'InsertInpaymentOnly',
|
||||
[pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id]
|
||||
)
|
||||
|
||||
connection.commit()
|
||||
return True
|
||||
except mysql.connector.Error as e:
|
||||
print(f"Error inserting GST release: {e}")
|
||||
return False
|
||||
finally:
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
@staticmethod
|
||||
def fetch_gst_release_by_id(gst_release_id):
|
||||
connection = GSTReleasemodel.get_connection()
|
||||
if not connection:
|
||||
return None
|
||||
data = {}
|
||||
try:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
cursor.callproc('GetGSTReleaseById', [gst_release_id])
|
||||
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
def update_gst_release(gst_release_id, pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr):
|
||||
connection = GSTReleasemodel.get_connection()
|
||||
if not connection:
|
||||
return False
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
# Update gst_release
|
||||
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
|
||||
def delete_gst_release(gst_release_id):
|
||||
connection = GSTReleasemodel.get_connection()
|
||||
if not connection:
|
||||
return False, None
|
||||
try:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
cursor.callproc('GetGSTReleaseUTRById', [gst_release_id])
|
||||
record = None
|
||||
for result in cursor.stored_results():
|
||||
record = result.fetchone()
|
||||
|
||||
if not record:
|
||||
return False, None
|
||||
|
||||
utr = record['UTR']
|
||||
|
||||
# Step 1: Delete gst_release
|
||||
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()
|
||||
# Check if addition was successful
|
||||
if gst.isSuccess:
|
||||
print(f"GST Release Added: {data}")
|
||||
else:
|
||||
print(f"Failed to add GST Release: {gst.resultMessage}")
|
||||
|
||||
self.isSuccess = gst.isSuccess
|
||||
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})
|
||||
|
||||
# ------------------- Edit GST Release -------------------
|
||||
def EditGSTRelease(self, request, gst_release_id):
|
||||
try:
|
||||
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||
|
||||
data = {
|
||||
"PMC_No": request.form.get("PMC_No", "").strip(),
|
||||
"Invoice_No": request.form.get("Invoice_No", "").strip(),
|
||||
"Basic_Amount": float(request.form.get("Basic_Amount", 0) or 0),
|
||||
"Final_Amount": float(request.form.get("Final_Amount", 0) or 0),
|
||||
"Total_Amount": float(request.form.get("Total_Amount", 0) or 0),
|
||||
"UTR": request.form.get("UTR", "").strip()
|
||||
}
|
||||
|
||||
gst.EditItem(
|
||||
request=request,
|
||||
childid=gst_release_id,
|
||||
data=data,
|
||||
storedprocupdate="UpdateGSTRelease"
|
||||
)
|
||||
|
||||
self.isSuccess = gst.isSuccess
|
||||
self.resultMessage = str(gst.resultMessage)
|
||||
|
||||
except Exception as e:
|
||||
print("ERROR in EditGSTRelease:", e)
|
||||
self.isSuccess = False
|
||||
self.resultMessage = str(e)
|
||||
|
||||
return jsonify({"success": self.isSuccess, "message": self.resultMessage})
|
||||
|
||||
# ------------------- Delete GST Release -------------------
|
||||
def DeleteGSTRelease(self, gst_release_id):
|
||||
try:
|
||||
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||
|
||||
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]
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
print("ERROR in GetAllGSTReleases:", e)
|
||||
return []
|
||||
|
||||
# ------------------- Get GST Release By ID -------------------
|
||||
def GetGSTReleaseByID(self, gst_release_id):
|
||||
try:
|
||||
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||
row = gst.GetDataByID(gst_release_id, "GetGSTReleaseById")
|
||||
|
||||
if row:
|
||||
return {
|
||||
"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]
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print("ERROR in GetGSTReleaseByID:", e)
|
||||
return None
|
||||
102
model/payment.py
102
model/payment.py
@@ -1,8 +1,13 @@
|
||||
import config
|
||||
import mysql.connector
|
||||
import config
|
||||
import mysql.connector
|
||||
from enum import Enum
|
||||
from model.Utilities import ItemCRUDType
|
||||
|
||||
class Paymentmodel:
|
||||
|
||||
# ---------------- Database Connection ----------------
|
||||
@staticmethod
|
||||
def get_connection():
|
||||
connection = config.get_db_connection()
|
||||
@@ -10,6 +15,7 @@ class Paymentmodel:
|
||||
return None
|
||||
return connection
|
||||
|
||||
# ---------------- Payment Methods ----------------
|
||||
@staticmethod
|
||||
def fetch_all_payments():
|
||||
connection = Paymentmodel.get_connection()
|
||||
@@ -52,14 +58,8 @@ class Paymentmodel:
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
cursor.callproc('UpdateInpaymentRecord', [
|
||||
subcontractor_id,
|
||||
pmc_no,
|
||||
invoice_no,
|
||||
amount,
|
||||
tds_amount,
|
||||
total_amount,
|
||||
utr
|
||||
])
|
||||
subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr
|
||||
])
|
||||
connection.commit()
|
||||
return True
|
||||
except mysql.connector.Error as e:
|
||||
@@ -80,7 +80,6 @@ class Paymentmodel:
|
||||
cursor.callproc("GetPaymentById", (payment_id,))
|
||||
for result in cursor.stored_results():
|
||||
payment_data = result.fetchone()
|
||||
# Convert to array for template
|
||||
if payment_data:
|
||||
payment_data = [
|
||||
payment_data.get('Payment_Id'),
|
||||
@@ -117,42 +116,99 @@ class Paymentmodel:
|
||||
|
||||
@staticmethod
|
||||
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()
|
||||
if not connection:
|
||||
return False, None, None
|
||||
|
||||
try:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# Fetch PMC & Invoice before deleting
|
||||
cursor.callproc('GetPaymentPMCInvoiceById', [payment_id])
|
||||
|
||||
record = {}
|
||||
for result in cursor.stored_results():
|
||||
record = result.fetchone() or {}
|
||||
if not record:
|
||||
return False, None, None
|
||||
|
||||
pmc_no = record['PMC_No']
|
||||
invoice_no = record['Invoice_No']
|
||||
|
||||
# Step 2: Delete the payment using the stored procedure
|
||||
# Delete payment
|
||||
cursor.callproc("DeletePayment", (payment_id,))
|
||||
connection.commit()
|
||||
|
||||
# Step 3: Reset inpayment fields using the stored procedure
|
||||
# Reset inpayment fields
|
||||
cursor.callproc("ResetInpayment", [pmc_no, invoice_no])
|
||||
connection.commit()
|
||||
|
||||
return True, pmc_no, invoice_no
|
||||
|
||||
except mysql.connector.Error as e:
|
||||
print(f"Error deleting payment: {e}")
|
||||
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:
|
||||
cursor.close()
|
||||
connection.close()
|
||||
@@ -1,9 +1,40 @@
|
||||
|
||||
|
||||
window.onload = function () {
|
||||
document.getElementById('Village_Name').focus();
|
||||
};
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// 🔥 RESTORE VIEW MODE AFTER RELOAD
|
||||
var viewMode = localStorage.getItem("viewMode");
|
||||
|
||||
if (viewMode === "table") {
|
||||
$('#addForm').hide();
|
||||
$('#addTable').show();
|
||||
} else {
|
||||
$('#addForm').show();
|
||||
$('#addTable').hide();
|
||||
}
|
||||
|
||||
|
||||
// 🔥 BUTTON TOGGLE LOGIC
|
||||
|
||||
$('#addButton').click(function () {
|
||||
$('#addForm').show();
|
||||
$('#addTable').hide();
|
||||
|
||||
localStorage.setItem("viewMode", "form");
|
||||
});
|
||||
|
||||
$('#displayButton').click(function () {
|
||||
$('#addForm').hide();
|
||||
$('#addTable').show();
|
||||
|
||||
localStorage.setItem("viewMode", "table");
|
||||
});
|
||||
|
||||
|
||||
// STATE → DISTRICT
|
||||
$('#state_Id').change(function () {
|
||||
|
||||
@@ -179,7 +210,7 @@ $(document).ready(function () {
|
||||
|
||||
} else {
|
||||
|
||||
alert('Error adding village. Please try again.');
|
||||
alert(response.message || 'Error adding village. Please try again.');
|
||||
|
||||
}
|
||||
|
||||
@@ -195,4 +226,39 @@ $(document).ready(function () {
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// 🔥 DELETE FUNCTION (UPDATED)
|
||||
function deleteVillage(villageId) {
|
||||
|
||||
if (!confirm("Are you sure you want to delete this village?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ save that user is on table
|
||||
localStorage.setItem("viewMode", "table");
|
||||
|
||||
$.ajax({
|
||||
url: '/delete_village/' + villageId,
|
||||
type: 'GET',
|
||||
|
||||
success: function () {
|
||||
|
||||
setTimeout(function () {
|
||||
|
||||
alert("Village deleted successfully!");
|
||||
|
||||
// reload but stay on table
|
||||
location.reload();
|
||||
|
||||
}, 1000);
|
||||
|
||||
},
|
||||
|
||||
error: function () {
|
||||
alert("Error deleting village. Please try again.");
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Activity Logs</title>
|
||||
@@ -9,67 +10,79 @@
|
||||
background-color: #f8f9fa;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
input, button {
|
||||
|
||||
input,
|
||||
button {
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
border-collapse: collapse;
|
||||
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;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>Activity Logs</h2>
|
||||
<form method="get" action="{{ url_for('activity_log') }}" class="filter-form">
|
||||
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" placeholder="Enter username" value="{{ username or '' }}">
|
||||
<form method="get" action="{{ url_for('log.activity_log') }}" class="filter-form">
|
||||
<label for="username">Username:</label>
|
||||
<input type="text" id="username" name="username" placeholder="Enter username" value="{{ username or '' }}">
|
||||
|
||||
<label for="start_date">Start Date:</label>
|
||||
<input type="date" id="start_date" name="start_date" value="{{ start_date or '' }}">
|
||||
<label for="start_date">Start Date:</label>
|
||||
<input type="date" id="start_date" name="start_date" value="{{ start_date or '' }}">
|
||||
|
||||
<label for="end_date">End Date:</label>
|
||||
<input type="date" id="end_date" name="end_date" value="{{ end_date or '' }}">
|
||||
<label for="end_date">End Date:</label>
|
||||
<input type="date" id="end_date" name="end_date" value="{{ end_date or '' }}">
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-primary">Filter</button>
|
||||
<!-- <button type="button" style="background-color: #6c757d;" onclick="resetFilter()">Reset</button> -->
|
||||
</form>
|
||||
<button type="submit" class="btn btn-primary">Filter</button>
|
||||
<button type="button" style="background-color: #6c757d;" onclick="resetFilter()">Reset</button>
|
||||
</form>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@@ -94,9 +107,10 @@
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function resetFilter() {
|
||||
window.location.href = "{{ url_for('activity_log') }}";
|
||||
}
|
||||
</script>
|
||||
function resetFilter() {
|
||||
window.location.href = "{{ url_for('log.activity_log') }}";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -68,50 +68,49 @@
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<!-- GST Release History Table -->
|
||||
<div id="addTable" style="display: none;">
|
||||
<div class="search-container">
|
||||
<h2>GST Release History</h2>
|
||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||
<input type="text" id="searchBar" placeholder="Search..." onkeyup="searchTable()">
|
||||
</div>
|
||||
<table id="sortableTable" border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sortable-header">GST_Release_Id</th>
|
||||
<th class="sortable-header">PMC_No</th>
|
||||
<th>Invoice_No</th>
|
||||
<th>Basic_Amount</th>
|
||||
<th>Final_Amount</th>
|
||||
<th>Total_Amount</th>
|
||||
<th>UTR</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="sortable-header">GST_Release_Id</th>
|
||||
<th class="sortable-header">PMC_No</th>
|
||||
<th>Invoice_No</th>
|
||||
<th>Basic_Amount</th>
|
||||
<th>Final_Amount</th>
|
||||
<th>Total_Amount</th>
|
||||
<th>UTR</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for gst_rel in gst_releases %}
|
||||
<tr>
|
||||
<td>{{ gst_rel[0] }}</td>
|
||||
<td>{{ gst_rel[1] }}</td>
|
||||
<td>{{ gst_rel[2] }}</td>
|
||||
<td>{{ gst_rel[3] }}</td>
|
||||
<td>{{ gst_rel[4] }}</td>
|
||||
<td>{{ gst_rel[5] }}</td>
|
||||
<td>{{ gst_rel[6] }}</td>
|
||||
<td>
|
||||
<a href="/edit_gst_release/{{ gst_rel[0] }}">
|
||||
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
|
||||
class="icon">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/delete_gst_release/{{ gst_rel[0] }}"
|
||||
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"
|
||||
class="icon">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for gst_rel in gst_releases %}
|
||||
<tr>
|
||||
<td>{{ gst_rel.gst_release_id }}</td>
|
||||
<td>{{ gst_rel.pmc_no }}</td>
|
||||
<td>{{ gst_rel.invoice_no }}</td>
|
||||
<td>{{ gst_rel.basic_amount }}</td>
|
||||
<td>{{ gst_rel.final_amount }}</td>
|
||||
<td>{{ gst_rel.total_amount }}</td>
|
||||
<td>{{ gst_rel.utr }}</td>
|
||||
<td>
|
||||
<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" class="icon">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<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?')">
|
||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@@ -8,91 +9,104 @@
|
||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="button-container">
|
||||
<button id="addButton" class="action-button">Add</button>
|
||||
<button id="displayButton" class="action-button">Display</button>
|
||||
</div>
|
||||
|
||||
<div id="addForm" style="display: none;">
|
||||
<h2>Add Payment</h2>
|
||||
|
||||
<form action="/add_payment" method="POST" onsubmit="showSuccessAlert(event)">
|
||||
<div class="row1">
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
|
||||
<div id="subcontractor_list" class="autocomplete-items"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="PMC_No">PMC No:</label><br>
|
||||
<select id="PMC_No" name="PMC_No" required>
|
||||
<option value="">Select PMC No</option>
|
||||
</select><br><br>
|
||||
|
||||
<label for="invoice_No">Invoice No:</label><br>
|
||||
<input type="number" step="0.01" id="invoice_No" name="invoice_No" ><br><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>
|
||||
|
||||
<label for="TDS_Percentage">TDS Percentage (%):</label><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>
|
||||
<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>
|
||||
<input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br>
|
||||
|
||||
|
||||
<label for="utr">UTR:</label><br>
|
||||
<input type="text" id="utr" name="utr"><br><br>
|
||||
|
||||
<button type="submit">Submit Payment</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="successPopup" class="success-popup">
|
||||
<i>✔</i> Payment added successfully!
|
||||
</div>
|
||||
|
||||
<div id="addTable" style="display: none;">
|
||||
<div class="search-container">
|
||||
<h2>Payment History</h2>
|
||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||
<div class="button-container">
|
||||
<button id="addButton" class="action-button">Add</button>
|
||||
<button id="displayButton" class="action-button">Display</button>
|
||||
</div>
|
||||
<table id="sortableTable" border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sortable-header">Payment ID</th>
|
||||
<th class="sortable-header">PMC No</th>
|
||||
<th>Invoice No</th>
|
||||
<th>Payment Amount</th>
|
||||
<th>TDS Amount</th>
|
||||
<th>Total Amount</th>
|
||||
<th>UTR</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for payment in payments %}
|
||||
<tr>
|
||||
<td>{{ payment[0] }}</td>
|
||||
<td>{{ payment[1] }}</td>
|
||||
<td>{{ payment[2] }}</td>
|
||||
<td>{{ payment[3] }}</td>
|
||||
<td>{{ payment[4] }}</td>
|
||||
<td>{{ payment[5] }}</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="/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>
|
||||
</tr>
|
||||
<!-- <tr>
|
||||
|
||||
<div id="addForm" style="display: none;">
|
||||
<h2>Add Payment</h2>
|
||||
|
||||
<form action="/add_payment" method="POST" onsubmit="showSuccessAlert(event)">
|
||||
<div class="row1">
|
||||
<div>
|
||||
<label for="subcontractor">Subcontractor Name:</label>
|
||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off" />
|
||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id" />
|
||||
<div id="subcontractor_list" class="autocomplete-items"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="PMC_No">PMC No:</label><br>
|
||||
<select id="PMC_No" name="PMC_No" required>
|
||||
<option value="">Select PMC No</option>
|
||||
</select><br><br>
|
||||
|
||||
<label for="invoice_No">Invoice No:</label><br>
|
||||
<input type="number" step="0.01" id="invoice_No" name="invoice_No"><br><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>
|
||||
|
||||
<label for="TDS_Percentage">TDS Percentage (%):</label><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>
|
||||
<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>
|
||||
<input type="number" step="0.01" id="total_amount" name="total_amount" required readonly><br><br>
|
||||
|
||||
|
||||
<label for="utr">UTR:</label><br>
|
||||
<input type="text" id="utr" name="utr"><br><br>
|
||||
|
||||
<button type="submit">Submit Payment</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="successPopup" class="success-popup">
|
||||
<i>✔</i> Payment added successfully!
|
||||
</div>
|
||||
|
||||
<div id="addTable" style="display: none;">
|
||||
<div class="search-container">
|
||||
<h2>Payment History</h2>
|
||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||
</div>
|
||||
<table id="sortableTable" border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sortable-header">Payment ID</th>
|
||||
<th class="sortable-header">PMC No</th>
|
||||
<th>Invoice No</th>
|
||||
<th>Payment Amount</th>
|
||||
<th>TDS Amount</th>
|
||||
<th>Total Amount</th>
|
||||
<th>UTR</th>
|
||||
<th>Update</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for payment in payments %}
|
||||
<tr>
|
||||
<td>{{ payment[0] }}</td>
|
||||
<td>{{ payment[1] }}</td>
|
||||
<td>{{ payment[2] }}</td>
|
||||
<td>{{ payment[3] }}</td>
|
||||
<td>{{ payment[4] }}</td>
|
||||
<td>{{ payment[5] }}</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="{{ 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>
|
||||
<td>{{ payment['Payment_Id'] }}</td>
|
||||
<td>{{ payment['PMC_No'] }}</td>
|
||||
<td>{{ payment['invoice_no'] }}</td>
|
||||
@@ -103,91 +117,91 @@
|
||||
<td><a href="/edit_payment/{{ payment['Payment_Id'] }}"><img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon"></a></td>
|
||||
<td><a href="/delete_payment/{{ payment['Payment_Id'] }}" 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> -->
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("subcontractor").addEventListener("input", function () {
|
||||
const query = this.value;
|
||||
const list = document.getElementById("subcontractor_list");
|
||||
<script>
|
||||
document.getElementById("subcontractor").addEventListener("input", function () {
|
||||
const query = this.value;
|
||||
const list = document.getElementById("subcontractor_list");
|
||||
|
||||
if (query.length < 2) {
|
||||
list.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
if (query.length < 2) {
|
||||
list.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
list.innerHTML = '';
|
||||
data.results.forEach(item => {
|
||||
const div = document.createElement("div");
|
||||
div.setAttribute("data-id", item.id);
|
||||
div.textContent = item.name;
|
||||
list.appendChild(div);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
|
||||
const selectedId = e.target.getAttribute("data-id");
|
||||
const selectedName = e.target.textContent;
|
||||
|
||||
if (selectedId) {
|
||||
document.getElementById("subcontractor_id").value = selectedId;
|
||||
document.getElementById("subcontractor").value = selectedName;
|
||||
document.getElementById("subcontractor_list").innerHTML = ""; // hide the list
|
||||
|
||||
console.log("Contractor id is", selectedId);
|
||||
|
||||
// Fetch PMC numbers
|
||||
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log("Fetched PMC Nos:", data.pmc_nos);
|
||||
const pmcDropdown = document.getElementById("PMC_No");
|
||||
pmcDropdown.innerHTML = "";
|
||||
|
||||
const defaultOption = document.createElement("option");
|
||||
defaultOption.value = "";
|
||||
defaultOption.textContent = "Select PMC No";
|
||||
pmcDropdown.appendChild(defaultOption);
|
||||
|
||||
data.pmc_nos.forEach(pmc => {
|
||||
const option = document.createElement("option");
|
||||
option.value = pmc;
|
||||
option.textContent = pmc;
|
||||
pmcDropdown.appendChild(option);
|
||||
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
list.innerHTML = '';
|
||||
data.results.forEach(item => {
|
||||
const div = document.createElement("div");
|
||||
div.setAttribute("data-id", item.id);
|
||||
div.textContent = item.name;
|
||||
list.appendChild(div);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (data.pmc_nos.length === 0) {
|
||||
alert("No PMC Nos found for this subcontractor.");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching PMC Nos:", error);
|
||||
alert("Failed to fetch PMC numbers.");
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
|
||||
const selectedId = e.target.getAttribute("data-id");
|
||||
const selectedName = e.target.textContent;
|
||||
|
||||
<script>
|
||||
function calculateTDSAndTotal() {
|
||||
const amount = parseFloat(document.getElementById("Payment_Amount").value) || 0;
|
||||
const tdsPercent = parseFloat(document.getElementById("TDS_Percentage").value) || 0;
|
||||
if (selectedId) {
|
||||
document.getElementById("subcontractor_id").value = selectedId;
|
||||
document.getElementById("subcontractor").value = selectedName;
|
||||
document.getElementById("subcontractor_list").innerHTML = ""; // hide the list
|
||||
|
||||
const tdsAmount = (amount * tdsPercent / 100).toFixed(2);
|
||||
const totalAmount = (amount - tdsAmount).toFixed(2);
|
||||
console.log("Contractor id is", selectedId);
|
||||
|
||||
document.getElementById("TDS_Payment_Amount").value = tdsAmount;
|
||||
document.getElementById("total_amount").value = totalAmount;
|
||||
}
|
||||
</script>
|
||||
// Fetch PMC numbers
|
||||
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log("Fetched PMC Nos:", data.pmc_nos);
|
||||
const pmcDropdown = document.getElementById("PMC_No");
|
||||
pmcDropdown.innerHTML = "";
|
||||
|
||||
const defaultOption = document.createElement("option");
|
||||
defaultOption.value = "";
|
||||
defaultOption.textContent = "Select PMC No";
|
||||
pmcDropdown.appendChild(defaultOption);
|
||||
|
||||
data.pmc_nos.forEach(pmc => {
|
||||
const option = document.createElement("option");
|
||||
option.value = pmc;
|
||||
option.textContent = pmc;
|
||||
pmcDropdown.appendChild(option);
|
||||
});
|
||||
|
||||
if (data.pmc_nos.length === 0) {
|
||||
alert("No PMC Nos found for this subcontractor.");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching PMC Nos:", error);
|
||||
alert("Failed to fetch PMC numbers.");
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function calculateTDSAndTotal() {
|
||||
const amount = parseFloat(document.getElementById("Payment_Amount").value) || 0;
|
||||
const tdsPercent = parseFloat(document.getElementById("TDS_Percentage").value) || 0;
|
||||
|
||||
const tdsAmount = (amount * tdsPercent / 100).toFixed(2);
|
||||
const totalAmount = (amount - tdsAmount).toFixed(2);
|
||||
|
||||
document.getElementById("TDS_Payment_Amount").value = tdsAmount;
|
||||
document.getElementById("total_amount").value = totalAmount;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<script src="{{ url_for('static', filename='js/showSuccessAlert.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/showSuccessAlert.js') }}"></script>
|
||||
</body>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -28,7 +28,7 @@
|
||||
<option value="{{ state[0] }}">{{ state[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
|
||||
<label for="district_Id">District:</label>
|
||||
<select id="district_Id" name="district_Id" required disabled>
|
||||
<option value="" disabled selected>Select District</option>
|
||||
@@ -84,11 +84,11 @@
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('village.delete_village', village_id=village[0]) }}"
|
||||
onclick="return confirm('Are you sure you want to delete this village?');">
|
||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
|
||||
class="icon">
|
||||
</a>
|
||||
<a href="#"
|
||||
onclick="deleteVillage({{ village[0] }}); return false;">
|
||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
|
||||
alt="Delete" class="icon">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user