Compare commits
39 Commits
dffa9769bc
...
swapnil-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d84ba520cf | ||
| 0aeaf775dd | |||
| 88e8771b51 | |||
| 6c74b5d3bf | |||
| 47ba78d72c | |||
| 1946a98d59 | |||
| 8f35e08429 | |||
| 82bedc3117 | |||
| cb68e454bc | |||
|
|
91b078a0c3 | ||
|
|
eb46929893 | ||
| 675301df7f | |||
|
|
14e799a1d4 | ||
| 8ab1b69033 | |||
| dbeec9962d | |||
| 8750f268db | |||
|
|
b78526ad9f | ||
| 7146391c18 | |||
| 94b5563d15 | |||
| 937018dc16 | |||
| eda238c235 | |||
| f184d6cecc | |||
| e7646eee76 | |||
| 64ca39944b | |||
|
|
6b4beb5af8 | ||
|
|
d092eb0527 | ||
|
|
f9e9612df5 | ||
|
|
46ec2c0276 | ||
|
|
630ee1744f | ||
| 2679554e98 | |||
| 0912aef85e | |||
| ce90c6b350 | |||
| bc20a53f26 | |||
| 42f69773ec | |||
| 79ea359b1c | |||
| acbea7942c | |||
| 13a4c5836e | |||
| 315e972e1f | |||
| 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
|
from flask_login import login_required
|
||||||
|
|
||||||
import config
|
from model.State import State
|
||||||
import mysql.connector
|
|
||||||
|
|
||||||
from model.Block import Block
|
from model.Block import Block
|
||||||
from model.Utilities import HtmlHelper
|
from model.Utilities import HtmlHelper
|
||||||
|
|
||||||
block_bp = Blueprint('block', __name__)
|
block_bp = Blueprint('block', __name__)
|
||||||
|
|
||||||
|
# --- Add Block page -------
|
||||||
@block_bp.route('/add_block', methods=['GET', 'POST'])
|
@block_bp.route('/add_block', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def add_block():
|
def add_block():
|
||||||
|
|
||||||
block = Block()
|
block = Block()
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
block.AddBlock(request)
|
block.AddBlock(request)
|
||||||
return block.resultMessage
|
return block.resultMessage
|
||||||
|
|
||||||
connection = config.get_db_connection()
|
state = State()
|
||||||
cursor = connection.cursor()
|
states = state.GetAllStates(request=request)
|
||||||
|
|
||||||
cursor.callproc("GetAllStates")
|
|
||||||
for rs in cursor.stored_results():
|
|
||||||
states = rs.fetchall()
|
|
||||||
|
|
||||||
block_data = block.GetAllBlocks(request)
|
block_data = block.GetAllBlocks(request)
|
||||||
|
|
||||||
cursor.close()
|
|
||||||
connection.close()
|
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'add_block.html',
|
'add_block.html',
|
||||||
states=states,
|
states=states,
|
||||||
@@ -83,7 +74,14 @@ def edit_block(block_id):
|
|||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
block.EditBlock(request, block_id)
|
block.EditBlock(request, block_id)
|
||||||
return block.resultMessage
|
block.resultMessage
|
||||||
|
|
||||||
|
if block.resultMessage:
|
||||||
|
flash("Block updated successfully!", "success")
|
||||||
|
return redirect(url_for('block.add_block'))
|
||||||
|
else:
|
||||||
|
flash(block.resultMessage, "error")
|
||||||
|
|
||||||
|
|
||||||
connection = config.get_db_connection()
|
connection = config.get_db_connection()
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ from model.State import State
|
|||||||
|
|
||||||
district_bp = Blueprint('district', __name__)
|
district_bp = Blueprint('district', __name__)
|
||||||
|
|
||||||
|
# ------- District page --------
|
||||||
@district_bp.route('/add_district', methods=['GET', 'POST'])
|
@district_bp.route('/add_district', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def add_district():
|
def add_district():
|
||||||
|
|
||||||
district = District()
|
district = District()
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -28,7 +27,7 @@ def add_district():
|
|||||||
states=states
|
states=states
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ------- District check --------
|
||||||
@district_bp.route('/check_district', methods=['POST'])
|
@district_bp.route('/check_district', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def check_district():
|
def check_district():
|
||||||
@@ -37,7 +36,7 @@ def check_district():
|
|||||||
|
|
||||||
return district.CheckDistrict(request=request)
|
return district.CheckDistrict(request=request)
|
||||||
|
|
||||||
|
# ------- District delete by district id --------
|
||||||
@district_bp.route('/delete_district/<int:district_id>')
|
@district_bp.route('/delete_district/<int:district_id>')
|
||||||
@login_required
|
@login_required
|
||||||
def delete_district(district_id):
|
def delete_district(district_id):
|
||||||
@@ -52,6 +51,7 @@ def delete_district(district_id):
|
|||||||
return redirect(url_for('district.add_district'))
|
return redirect(url_for('district.add_district'))
|
||||||
|
|
||||||
|
|
||||||
|
# ------- District update by district id --------
|
||||||
@district_bp.route('/edit_district/<int:district_id>', methods=['GET', 'POST'])
|
@district_bp.route('/edit_district/<int:district_id>', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def edit_district(district_id):
|
def edit_district(district_id):
|
||||||
|
|||||||
@@ -1,28 +1,16 @@
|
|||||||
import os
|
import config
|
||||||
import ast
|
import ast
|
||||||
import re
|
import re
|
||||||
from flask_login import login_required
|
|
||||||
import openpyxl
|
import openpyxl
|
||||||
from flask import Blueprint, request, render_template, redirect, url_for, jsonify, current_app
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
from flask_login import login_required
|
||||||
|
from flask import Blueprint, request, render_template, redirect, url_for, jsonify
|
||||||
|
|
||||||
from model.Log import LogHelper
|
from model.Log import LogHelper
|
||||||
import config
|
|
||||||
from model.FolderAndFile import FolderAndFile
|
from model.FolderAndFile import FolderAndFile
|
||||||
|
|
||||||
excel_bp = Blueprint('excel', __name__)
|
excel_bp = Blueprint('excel', __name__)
|
||||||
|
|
||||||
# Default folder in case config not set
|
|
||||||
# DEFAULT_UPLOAD_FOLDER = 'uploads'
|
|
||||||
|
|
||||||
|
|
||||||
# def get_upload_folder():
|
|
||||||
# """Returns the upload folder from Flask config or default, ensures it exists."""
|
|
||||||
# folder = current_app.config.get('UPLOAD_FOLDER', DEFAULT_UPLOAD_FOLDER)
|
|
||||||
# if not os.path.exists(folder):
|
|
||||||
# os.makedirs(folder)
|
|
||||||
# return folder
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------- Upload Excel File ----------------
|
# ---------------- Upload Excel File ----------------
|
||||||
@excel_bp.route('/upload_excel_file', methods=['GET', 'POST'])
|
@excel_bp.route('/upload_excel_file', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@@ -30,9 +18,6 @@ def upload():
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
file = request.files.get('file')
|
file = request.files.get('file')
|
||||||
if file and file.filename.endswith('.xlsx'):
|
if file and file.filename.endswith('.xlsx'):
|
||||||
# upload_folder = get_upload_folder()
|
|
||||||
# filepath = os.path.join(upload_folder, file.filename)
|
|
||||||
|
|
||||||
filepath =FolderAndFile.get_upload_path(file.filename)
|
filepath =FolderAndFile.get_upload_path(file.filename)
|
||||||
|
|
||||||
file.save(filepath)
|
file.save(filepath)
|
||||||
@@ -42,6 +27,7 @@ def upload():
|
|||||||
f"User {current_user.id} Upload Excel File '{file.filename}'"
|
f"User {current_user.id} Upload Excel File '{file.filename}'"
|
||||||
)
|
)
|
||||||
return redirect(url_for('excel.show_table', filename=file.filename))
|
return redirect(url_for('excel.show_table', filename=file.filename))
|
||||||
|
|
||||||
return render_template('uploadExcelFile.html')
|
return render_template('uploadExcelFile.html')
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +37,6 @@ def show_table(filename):
|
|||||||
global data
|
global data
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
# filepath = os.path.join(get_upload_folder(), filename)
|
|
||||||
filepath = FolderAndFile.get_upload_path(filename)
|
filepath = FolderAndFile.get_upload_path(filename)
|
||||||
wb = openpyxl.load_workbook(filepath, data_only=True)
|
wb = openpyxl.load_workbook(filepath, data_only=True)
|
||||||
sheet = wb.active
|
sheet = wb.active
|
||||||
|
|||||||
@@ -1,70 +1,46 @@
|
|||||||
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
# routes/gst_release_routes.py
|
||||||
from flask_login import login_required, current_user
|
from flask import Blueprint, render_template, request, redirect, url_for, flash
|
||||||
from model.gst_release import GSTReleasemodel
|
from flask_login import login_required
|
||||||
|
from model.gst_release import GSTRelease
|
||||||
from model.Log import LogHelper
|
from model.Log import LogHelper
|
||||||
|
|
||||||
gst_release_bp = Blueprint('gst_release_bp', __name__)
|
gst_release_bp = Blueprint('gst_release_bp', __name__)
|
||||||
|
gst_service = GSTRelease()
|
||||||
|
|
||||||
# ------------------- Add GST Release -------------------
|
# ---------------- ADD GST RELEASE ----------------
|
||||||
@gst_release_bp.route('/add_gst_release', methods=['GET', 'POST'])
|
@gst_release_bp.route('/add_gst_release', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def add_gst_release():
|
def add_gst_release():
|
||||||
gst_releases_dict = GSTReleasemodel.fetch_all_gst_releases()
|
|
||||||
gst_releases = [
|
|
||||||
[
|
|
||||||
g['GST_Release_Id'], g['PMC_No'], g['invoice_no'],
|
|
||||||
g['Basic_Amount'], g['Final_Amount'], g['Total_Amount'], g['UTR']
|
|
||||||
] for g in gst_releases_dict
|
|
||||||
] if gst_releases_dict else []
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
pmc_no = request.form['PMC_No']
|
gst_service.AddGSTRelease(request)
|
||||||
invoice_no = request.form['invoice_No']
|
LogHelper.log_action("Add GST Release", "User added GST release")
|
||||||
basic_amount = request.form['basic_amount']
|
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
|
||||||
final_amount = request.form['final_amount']
|
|
||||||
total_amount = request.form['total_amount']
|
|
||||||
utr = request.form['utr']
|
|
||||||
contractor_id = request.form['subcontractor_id']
|
|
||||||
|
|
||||||
LogHelper.log_action("Add GST Release", f"User {current_user.id} added GST release '{pmc_no}'")
|
|
||||||
GSTReleasemodel.insert_gst_release(pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id)
|
|
||||||
|
|
||||||
return redirect(url_for('gst_release_bp.add_gst_release'))
|
return redirect(url_for('gst_release_bp.add_gst_release'))
|
||||||
|
|
||||||
return render_template('add_gst_release.html', gst_releases=gst_releases, invoices=[])
|
gst_releases = gst_service.GetAllGSTReleases()
|
||||||
|
return render_template('add_gst_release.html', gst_releases=gst_releases)
|
||||||
|
|
||||||
|
# ---------------- EDIT GST RELEASE ----------------
|
||||||
# ------------------- Edit GST Release -------------------
|
|
||||||
@gst_release_bp.route('/edit_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
|
@gst_release_bp.route('/edit_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def edit_gst_release(gst_release_id):
|
def edit_gst_release(gst_release_id):
|
||||||
gst_release_data = GSTReleasemodel.fetch_gst_release_by_id(gst_release_id)
|
gst_data = gst_service.GetGSTReleaseByID(gst_release_id)
|
||||||
if not gst_release_data:
|
if not gst_data:
|
||||||
return "GST Release not found", 404
|
return "GST Release not found", 404
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
pmc_no = request.form['PMC_No']
|
gst_service.EditGSTRelease(request, gst_release_id)
|
||||||
invoice_no = request.form['invoice_No']
|
LogHelper.log_action("Edit GST Release", "User edited GST release")
|
||||||
basic_amount = request.form['basic_amount']
|
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
|
||||||
final_amount = request.form['final_amount']
|
|
||||||
total_amount = request.form['total_amount']
|
|
||||||
utr = request.form['utr']
|
|
||||||
|
|
||||||
LogHelper.log_action("Edit GST Release", f"User {current_user.id} edited GST release '{pmc_no}'")
|
|
||||||
GSTReleasemodel.update_gst_release(gst_release_id, pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr)
|
|
||||||
|
|
||||||
return redirect(url_for('gst_release_bp.add_gst_release'))
|
return redirect(url_for('gst_release_bp.add_gst_release'))
|
||||||
|
|
||||||
return render_template('edit_gst_release.html', gst_release_data=gst_release_data, invoices=[])
|
return render_template('edit_gst_release.html', gst_release_data=gst_data)
|
||||||
|
|
||||||
|
# ---------------- DELETE GST RELEASE ----------------
|
||||||
# ------------------- Delete GST Release -------------------
|
|
||||||
@gst_release_bp.route('/delete_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
|
@gst_release_bp.route('/delete_gst_release/<int:gst_release_id>', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def delete_gst_release(gst_release_id):
|
def delete_gst_release(gst_release_id):
|
||||||
success, utr = GSTReleasemodel.delete_gst_release(gst_release_id)
|
gst_service.DeleteGSTRelease(gst_release_id)
|
||||||
if not success:
|
LogHelper.log_action("Delete GST Release", "User deleted GST release")
|
||||||
return jsonify({"message": "GST Release not found or failed to delete", "status": "error"}), 404
|
flash(gst_service.resultMessage, 'success' if gst_service.isSuccess else 'error')
|
||||||
|
return redirect(url_for('gst_release_bp.add_gst_release'))
|
||||||
LogHelper.log_action("Delete GST Release", f"User {current_user.id} deleted GST release '{gst_release_id}'")
|
|
||||||
return jsonify({"message": f"GST Release {gst_release_id} deleted successfully.", "status": "success"}), 200
|
|
||||||
@@ -10,21 +10,21 @@ hold_bp = Blueprint("hold_types", __name__)
|
|||||||
@hold_bp.route('/add_hold_type', methods=['GET','POST'])
|
@hold_bp.route('/add_hold_type', methods=['GET','POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def add_hold_type():
|
def add_hold_type():
|
||||||
|
|
||||||
hold = HoldTypes()
|
hold = HoldTypes()
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
hold.AddHoldType(request) # ✅
|
hold.AddHoldType(request)
|
||||||
return hold.resultMessage
|
# ✅ Always redirect to same page (NO JSON)
|
||||||
|
return redirect(url_for("hold_types.add_hold_type"))
|
||||||
|
|
||||||
hold_types = hold.GetAllHoldTypes() # ✅
|
# GET request → show data
|
||||||
|
hold_types = hold.GetAllHoldTypes()
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"add_hold_type.html",
|
"add_hold_type.html",
|
||||||
Hold_Types_data=hold_types
|
Hold_Types_data=hold_types
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# ---------------- CHECK HOLD TYPE (OPTIONAL LIKE BLOCK) ----------------
|
# ---------------- CHECK HOLD TYPE (OPTIONAL LIKE BLOCK) ----------------
|
||||||
@hold_bp.route('/check_hold_type', methods=['POST'])
|
@hold_bp.route('/check_hold_type', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ def activity_log():
|
|||||||
end_date,
|
end_date,
|
||||||
user_name
|
user_name
|
||||||
)
|
)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"activity_log.html",
|
"activity_log.html",
|
||||||
logs=filtered_logs,
|
logs=filtered_logs,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
from flask import Blueprint, render_template, request, redirect, url_for, jsonify, flash
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from model.payment import Paymentmodel
|
from model.payment import Paymentmodel
|
||||||
from model.Log import LogHelper
|
from model.Log import LogHelper
|
||||||
@@ -86,18 +86,16 @@ def edit_payment(payment_id):
|
|||||||
|
|
||||||
return render_template('edit_payment.html', payment_data=payment_data)
|
return render_template('edit_payment.html', payment_data=payment_data)
|
||||||
|
|
||||||
|
|
||||||
# ------------------- Delete Payment -------------------
|
# ------------------- Delete Payment -------------------
|
||||||
@payment_bp.route('/delete_payment/<int:payment_id>', methods=['GET', 'POST'])
|
@payment_bp.route('/delete_payment/<int:payment_id>', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def delete_payment(payment_id):
|
def delete_payment(payment_id):
|
||||||
success, pmc_no, invoice_no = Paymentmodel.delete_payment(payment_id)
|
success, pmc_no, invoice_no = Paymentmodel.delete_payment(payment_id)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
return jsonify({"message": "Payment not found or failed to delete", "status": "error"}), 404
|
flash("Payment not found or failed to delete", "error")
|
||||||
|
else:
|
||||||
|
LogHelper.log_action("Delete Payment", f"User {current_user.id} deleted Payment '{payment_id}'")
|
||||||
|
flash(f"Payment ID {payment_id} deleted successfully.", "success")
|
||||||
|
|
||||||
LogHelper.log_action("Delete Payment", f"User {current_user.id} deleted Payment '{payment_id}'")
|
return redirect(url_for('payment_bp.add_payment'))
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
"message": f"Payment ID {payment_id} deleted successfully.",
|
|
||||||
"status": "success"
|
|
||||||
}), 200
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
from flask import Blueprint, render_template, send_from_directory
|
from flask import Blueprint, render_template, send_from_directory
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required
|
||||||
|
|
||||||
from model.PmcReport import PmcReport
|
from model.PmcReport import PmcReport
|
||||||
|
|
||||||
pmc_report_bp = Blueprint("pmc_report", __name__)
|
pmc_report_bp = Blueprint("pmc_report", __name__)
|
||||||
|
|
||||||
|
# ---------------- Contractor Report by pmc no ----------------
|
||||||
@pmc_report_bp.route("/pmc_report/<pmc_no>")
|
@pmc_report_bp.route("/pmc_report/<pmc_no>")
|
||||||
@login_required
|
@login_required
|
||||||
def pmc_report(pmc_no):
|
def pmc_report(pmc_no):
|
||||||
@@ -23,6 +25,7 @@ def pmc_report(pmc_no):
|
|||||||
total=data["total"]
|
total=data["total"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ---------------- Contractor Download Report by pmc no ----------------
|
||||||
@pmc_report_bp.route("/download_pmc_report/<pmc_no>")
|
@pmc_report_bp.route("/download_pmc_report/<pmc_no>")
|
||||||
@login_required
|
@login_required
|
||||||
def download_pmc_report(pmc_no):
|
def download_pmc_report(pmc_no):
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
from flask import Blueprint, render_template, request, jsonify, send_file
|
from flask import Blueprint, render_template, request, jsonify
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
from model.Report import ReportHelper
|
from model.Report import ReportHelper
|
||||||
from model.Log import LogHelper
|
from model.Log import LogHelper
|
||||||
import config
|
|
||||||
import os
|
|
||||||
import openpyxl
|
|
||||||
from openpyxl.styles import Font
|
|
||||||
from model.ContractorInfo import ContractorInfo
|
|
||||||
from model.FolderAndFile import FolderAndFile
|
|
||||||
|
|
||||||
|
|
||||||
report_bp = Blueprint("report", __name__)
|
report_bp = Blueprint("report", __name__)
|
||||||
@@ -36,7 +31,7 @@ def search_contractor():
|
|||||||
|
|
||||||
return jsonify(data)
|
return jsonify(data)
|
||||||
|
|
||||||
# ---------------- Contractor Report ----------------
|
# ---------------- Contractor Report by contractor id ----------------
|
||||||
@report_bp.route('/contractor_report/<int:contractor_id>')
|
@report_bp.route('/contractor_report/<int:contractor_id>')
|
||||||
@login_required
|
@login_required
|
||||||
def contractor_report(contractor_id):
|
def contractor_report(contractor_id):
|
||||||
@@ -49,145 +44,10 @@ def contractor_report(contractor_id):
|
|||||||
**data
|
**data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ---------------- Contractor Download Report by contractor id ----------------
|
||||||
@report_bp.route('/download_report/<int:contractor_id>')
|
@report_bp.route('/download_report/<int:contractor_id>')
|
||||||
@login_required
|
@login_required
|
||||||
def download_report(contractor_id):
|
def download_report(contractor_id):
|
||||||
|
|
||||||
return ReportHelper().download_report(contractor_id=contractor_id)
|
return ReportHelper().download_report(contractor_id=contractor_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# @report_bp.route('/download_report/<int:contractor_id>')
|
|
||||||
# @login_required
|
|
||||||
# def download_report(contractor_id):
|
|
||||||
# try:
|
|
||||||
# connection = config.get_db_connection()
|
|
||||||
# cursor = connection.cursor(dictionary=True)
|
|
||||||
|
|
||||||
# # -------- Contractor Info --------
|
|
||||||
# contractor = ContractorInfo(contractor_id)
|
|
||||||
# contInfo = contractor.contInfo
|
|
||||||
|
|
||||||
# if not contInfo:
|
|
||||||
# return "No contractor found", 404
|
|
||||||
|
|
||||||
# # -------- Invoice Data --------
|
|
||||||
# cursor.callproc('FetchInvoicesByContractor', [contractor_id])
|
|
||||||
|
|
||||||
# invoices = []
|
|
||||||
# for result in cursor.stored_results():
|
|
||||||
# invoices.extend(result.fetchall())
|
|
||||||
|
|
||||||
# if not invoices:
|
|
||||||
# return "No invoice data found"
|
|
||||||
|
|
||||||
# # -------- Create Workbook --------
|
|
||||||
# workbook = openpyxl.Workbook()
|
|
||||||
# sheet = workbook.active
|
|
||||||
# sheet.title = "Contractor Report"
|
|
||||||
|
|
||||||
# # ================= CONTRACTOR DETAILS =================
|
|
||||||
# sheet.append(["SUB CONTRACTOR DETAILS"])
|
|
||||||
# sheet.cell(row=sheet.max_row, column=1).font = Font(bold=True)
|
|
||||||
# sheet.append([])
|
|
||||||
|
|
||||||
# sheet.append(["Name", contInfo.get("Contractor_Name") or ""])
|
|
||||||
# sheet.append(["Mobile No", contInfo.get("Mobile_No") or ""])
|
|
||||||
# sheet.append(["Email", contInfo.get("Email") or ""])
|
|
||||||
# sheet.append(["Village", contInfo.get("Village_Name") or ""])
|
|
||||||
# sheet.append(["Block", contInfo.get("Block_Name") or ""])
|
|
||||||
# sheet.append(["District", contInfo.get("District_Name") or ""])
|
|
||||||
# sheet.append(["State", contInfo.get("State_Name") or ""])
|
|
||||||
# sheet.append(["Address", contInfo.get("Address") or ""])
|
|
||||||
# sheet.append(["GST No", contInfo.get("GST_No") or ""])
|
|
||||||
# sheet.append(["PAN No", contInfo.get("PAN_No") or ""])
|
|
||||||
# sheet.append([])
|
|
||||||
# sheet.append([])
|
|
||||||
|
|
||||||
# # ================= TABLE HEADERS =================
|
|
||||||
# headers = [
|
|
||||||
# "PMC No", "Village", "Invoice No", "Invoice Date", "Work Type","Invoice_Details",
|
|
||||||
# "Basic Amount", "Debit Amount", "After Debit Amount",
|
|
||||||
# "Amount", "GST Amount", "TDS Amount", "SD Amount",
|
|
||||||
# "On Commission", "Hydro Testing", "Hold Amount",
|
|
||||||
# "GST SD Amount", "Final Amount",
|
|
||||||
# "Payment Amount", "TDS Payment",
|
|
||||||
# "Total Amount", "UTR"
|
|
||||||
# ]
|
|
||||||
# sheet.append(headers)
|
|
||||||
# for col in range(1, len(headers) + 1):
|
|
||||||
# sheet.cell(row=sheet.max_row, column=col).font = Font(bold=True)
|
|
||||||
|
|
||||||
# # ================= DATA =================
|
|
||||||
# total_final = 0
|
|
||||||
# total_payment = 0
|
|
||||||
# total_amount = 0
|
|
||||||
|
|
||||||
# for inv in invoices:
|
|
||||||
# row = [
|
|
||||||
# inv.get("PMC_No"),
|
|
||||||
# inv.get("Village_Name"),
|
|
||||||
# inv.get("invoice_no"),
|
|
||||||
# inv.get("Invoice_Date"),
|
|
||||||
# inv.get("Work_Type"),
|
|
||||||
# inv.get("Invoice_Details"),
|
|
||||||
# inv.get("Basic_Amount"),
|
|
||||||
# inv.get("Debit_Amount"),
|
|
||||||
# inv.get("After_Debit_Amount"),
|
|
||||||
# inv.get("Amount"),
|
|
||||||
# inv.get("GST_Amount"),
|
|
||||||
# inv.get("TDS_Amount"),
|
|
||||||
# inv.get("SD_Amount"),
|
|
||||||
# inv.get("On_Commission"),
|
|
||||||
# inv.get("Hydro_Testing"),
|
|
||||||
# inv.get("Hold_Amount"),
|
|
||||||
# inv.get("GST_SD_Amount"),
|
|
||||||
# inv.get("Final_Amount"),
|
|
||||||
# inv.get("Payment_Amount"),
|
|
||||||
# inv.get("TDS_Payment_Amount"),
|
|
||||||
# inv.get("Total_Amount"),
|
|
||||||
# inv.get("UTR")
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# total_final += float(inv.get("Final_Amount") or 0)
|
|
||||||
# total_payment += float(inv.get("Payment_Amount") or 0)
|
|
||||||
# total_amount += float(inv.get("Total_Amount") or 0)
|
|
||||||
|
|
||||||
# sheet.append(row)
|
|
||||||
|
|
||||||
# # ================= TOTAL ROW =================
|
|
||||||
# sheet.append([])
|
|
||||||
# sheet.append([
|
|
||||||
# "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
|
||||||
# "TOTAL",
|
|
||||||
# total_final,
|
|
||||||
# total_payment,
|
|
||||||
# "",
|
|
||||||
# total_amount,
|
|
||||||
# ""
|
|
||||||
# ])
|
|
||||||
|
|
||||||
# # ================= AUTO WIDTH =================
|
|
||||||
# for column in sheet.columns:
|
|
||||||
# max_length = 0
|
|
||||||
# column_letter = column[0].column_letter
|
|
||||||
# for cell in column:
|
|
||||||
# if cell.value:
|
|
||||||
# max_length = max(max_length, len(str(cell.value)))
|
|
||||||
# sheet.column_dimensions[column_letter].width = max_length + 2
|
|
||||||
|
|
||||||
# # ================= SAVE FILE =================
|
|
||||||
# # output_folder = "downloads"
|
|
||||||
# # os.makedirs(output_folder, exist_ok=True)
|
|
||||||
# # filename = f"Contractor_Report_{contInfo.get('Contractor_Name')}.xlsx"
|
|
||||||
# # output_file = os.path.join(output_folder, filename)
|
|
||||||
|
|
||||||
# filename = f"Contractor_Report_{contInfo.get('Contractor_Name')}.xlsx"
|
|
||||||
# output_file = FolderAndFile.get_download_path(filename)
|
|
||||||
# workbook.save(output_file)
|
|
||||||
|
|
||||||
# return send_file(output_file, as_attachment=True)
|
|
||||||
|
|
||||||
# except Exception as e:
|
|
||||||
# return str(e)
|
|
||||||
@@ -4,7 +4,7 @@ from model.State import State
|
|||||||
|
|
||||||
state_bp = Blueprint('state', __name__)
|
state_bp = Blueprint('state', __name__)
|
||||||
|
|
||||||
|
# ----- State page ------
|
||||||
@state_bp.route('/add_state', methods=['GET', 'POST'])
|
@state_bp.route('/add_state', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def add_state():
|
def add_state():
|
||||||
@@ -19,7 +19,7 @@ def add_state():
|
|||||||
|
|
||||||
return render_template('add_state.html', statedata=statedata)
|
return render_template('add_state.html', statedata=statedata)
|
||||||
|
|
||||||
|
# ----- State check ------
|
||||||
@state_bp.route('/check_state', methods=['POST'])
|
@state_bp.route('/check_state', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def check_state():
|
def check_state():
|
||||||
@@ -28,21 +28,21 @@ def check_state():
|
|||||||
|
|
||||||
return state.CheckState(request=request)
|
return state.CheckState(request=request)
|
||||||
|
|
||||||
|
# ----- State delete by state id ------
|
||||||
@state_bp.route('/delete_state/<int:id>')
|
@state_bp.route('/delete_state/<int:id>')
|
||||||
@login_required
|
@login_required
|
||||||
def deleteState(id):
|
def deleteState(id):
|
||||||
|
|
||||||
state = State()
|
state = State()
|
||||||
|
|
||||||
msg = state.DeleteState(request=request, id=id)
|
state.DeleteState(request=request, id=id)
|
||||||
|
|
||||||
if not state.isSuccess:
|
if not state.isSuccess:
|
||||||
return state.resultMessage
|
return state.resultMessage
|
||||||
else:
|
else:
|
||||||
return redirect(url_for('state.add_state'))
|
return redirect(url_for('state.add_state'))
|
||||||
|
|
||||||
|
# ----- State update by state id ------
|
||||||
@state_bp.route('/edit_state/<int:id>', methods=['GET', 'POST'])
|
@state_bp.route('/edit_state/<int:id>', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def editState(id):
|
def editState(id):
|
||||||
|
|||||||
@@ -1,34 +1,11 @@
|
|||||||
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
from model.Subcontractor import Subcontractor
|
from model.Subcontractor import Subcontractor
|
||||||
|
from model.Utilities import HtmlHelper, ResponseHandler
|
||||||
|
|
||||||
|
|
||||||
subcontractor_bp = Blueprint('subcontractor', __name__)
|
subcontractor_bp = Blueprint('subcontractor', __name__)
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
|
||||||
# Helpers (unchanged)
|
|
||||||
# ----------------------------------------------------------
|
|
||||||
class HtmlHelper:
|
|
||||||
@staticmethod
|
|
||||||
def json_response(data, status=200):
|
|
||||||
return jsonify(data), status
|
|
||||||
|
|
||||||
class ResponseHandler:
|
|
||||||
@staticmethod
|
|
||||||
def fetch_failure(entity):
|
|
||||||
return {"status": "error", "message": f"Failed to fetch {entity}"}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_failure(entity):
|
|
||||||
return {"status": "error", "message": f"Failed to add {entity}"}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_failure(entity):
|
|
||||||
return {"status": "error", "message": f"Failed to update {entity}"}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def delete_failure(entity):
|
|
||||||
return {"status": "error", "message": f"Failed to delete {entity}"}
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# LIST + ADD
|
# LIST + ADD
|
||||||
|
|||||||
@@ -83,17 +83,29 @@ def check_village():
|
|||||||
return village.CheckVillage(request=request)
|
return village.CheckVillage(request=request)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------- Delete Village -------------------------
|
|
||||||
@village_bp.route('/delete_village/<int:village_id>')
|
@village_bp.route('/delete_village/<int:village_id>')
|
||||||
@login_required
|
@login_required
|
||||||
def delete_village(village_id):
|
def delete_village(village_id):
|
||||||
|
|
||||||
village = Village()
|
village = Village()
|
||||||
village.DeleteVillage(request=request, village_id=village_id)
|
village.DeleteVillage(request=request, village_id=village_id)
|
||||||
|
|
||||||
flash(village.resultMessage, "success" if village.isSuccess else "error")
|
# ✅ Convert resultMessage to string if it's a Response or tuple
|
||||||
return redirect(url_for('village.add_village'))
|
raw_msg = village.resultMessage
|
||||||
|
|
||||||
|
if isinstance(raw_msg, tuple):
|
||||||
|
# e.g., (<Response ...>, 200)
|
||||||
|
msg = "Village deleted successfully!"
|
||||||
|
elif hasattr(raw_msg, 'get_data'):
|
||||||
|
# Flask Response object
|
||||||
|
msg = raw_msg.get_data(as_text=True) # get raw text
|
||||||
|
else:
|
||||||
|
# fallback
|
||||||
|
msg = str(raw_msg) if raw_msg else "Village deleted successfully!"
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "success" if village.isSuccess else "error",
|
||||||
|
"message": msg
|
||||||
|
})
|
||||||
|
|
||||||
# ------------------------- Edit Village -------------------------
|
# ------------------------- Edit Village -------------------------
|
||||||
@village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])
|
@village_bp.route('/edit_village/<int:village_id>', methods=['GET', 'POST'])
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json
|
from flask import Blueprint, render_template, request, redirect, url_for, jsonify
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
|
|
||||||
|
|
||||||
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
|
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
|
||||||
from model.Log import LogData, LogHelper
|
from model.Log import LogData, LogHelper
|
||||||
@@ -151,7 +147,7 @@ class Block:
|
|||||||
|
|
||||||
self.isSuccess = block.isSuccess
|
self.isSuccess = block.isSuccess
|
||||||
self.resultMessage = block.resultMessage
|
self.resultMessage = block.resultMessage
|
||||||
return render_template('add_block.html')
|
return
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# Delete Block
|
# Delete Block
|
||||||
|
|||||||
@@ -1,20 +1,6 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json
|
from model.Utilities import ItemCRUDType
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
|
|
||||||
|
|
||||||
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
|
|
||||||
from model.Log import LogData, LogHelper
|
|
||||||
|
|
||||||
import os
|
|
||||||
import config
|
|
||||||
import re
|
|
||||||
|
|
||||||
import mysql.connector
|
|
||||||
from mysql.connector import Error
|
|
||||||
|
|
||||||
from model.ItemCRUD import ItemCRUD
|
from model.ItemCRUD import ItemCRUD
|
||||||
|
|
||||||
class District:
|
class District:
|
||||||
|
|
||||||
isSuccess = False
|
isSuccess = False
|
||||||
@@ -24,7 +10,7 @@ class District:
|
|||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = ""
|
self.resultMessage = ""
|
||||||
|
|
||||||
|
# edit district
|
||||||
def EditDistrict(self, request, district_id):
|
def EditDistrict(self, request, district_id):
|
||||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||||
|
|
||||||
@@ -36,7 +22,7 @@ class District:
|
|||||||
self.resultMessage = district.resultMessage
|
self.resultMessage = district.resultMessage
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# add district
|
||||||
def AddDistrict(self, request):
|
def AddDistrict(self, request):
|
||||||
|
|
||||||
district = ItemCRUD(ItemCRUDType.District)
|
district = ItemCRUD(ItemCRUDType.District)
|
||||||
@@ -50,7 +36,7 @@ class District:
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# get all district data
|
||||||
def GetAllDistricts(self, request):
|
def GetAllDistricts(self, request):
|
||||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||||
districtsdata = district.GetAllData(request=request, storedproc="GetAllDistricts")
|
districtsdata = district.GetAllData(request=request, storedproc="GetAllDistricts")
|
||||||
@@ -58,7 +44,7 @@ class District:
|
|||||||
self.resultMessage = district.resultMessage
|
self.resultMessage = district.resultMessage
|
||||||
return districtsdata
|
return districtsdata
|
||||||
|
|
||||||
|
# check district validation
|
||||||
def CheckDistrict(self, request):
|
def CheckDistrict(self, request):
|
||||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||||
district_name = request.json.get('district_Name', '').strip()
|
district_name = request.json.get('district_Name', '').strip()
|
||||||
@@ -68,13 +54,7 @@ class District:
|
|||||||
self.resultMessage = district.resultMessage
|
self.resultMessage = district.resultMessage
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
# get district by district id
|
||||||
# def GetDistrictByID(self, request,district_id):
|
|
||||||
# district = ItemCRUD(itemType=ItemCRUDType.District)
|
|
||||||
# districtdata = district.GetAllData(id=district_id,storedproc="GetDistrictDataByID")
|
|
||||||
# self.isSuccess = district.isSuccess
|
|
||||||
# self.resultMessage = district.resultMessage
|
|
||||||
# return districtdata
|
|
||||||
def GetDistrictByID(self, request, district_id):
|
def GetDistrictByID(self, request, district_id):
|
||||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||||
|
|
||||||
@@ -92,7 +72,7 @@ class District:
|
|||||||
return districtdata
|
return districtdata
|
||||||
|
|
||||||
|
|
||||||
#Delete District
|
# Delete District by district id
|
||||||
def DeleteDistrict(self, request, district_id):
|
def DeleteDistrict(self, request, district_id):
|
||||||
district = ItemCRUD(itemType=ItemCRUDType.District)
|
district = ItemCRUD(itemType=ItemCRUDType.District)
|
||||||
district.DeleteItem(request=request,itemID=district_id,storedprocDelete="DeleteDistrict")
|
district.DeleteItem(request=request,itemID=district_id,storedprocDelete="DeleteDistrict")
|
||||||
|
|||||||
@@ -27,13 +27,27 @@ class FolderAndFile:
|
|||||||
os.makedirs(folder, exist_ok=True)
|
os.makedirs(folder, exist_ok=True)
|
||||||
return folder
|
return folder
|
||||||
|
|
||||||
# -----------------------------
|
@staticmethod
|
||||||
# FILE PATH METHODS
|
def get_logs_folder():
|
||||||
# -----------------------------
|
folder = os.path.join(current_app.root_path, "logs")
|
||||||
|
|
||||||
|
if not os.path.exists(folder):
|
||||||
|
os.makedirs(folder)
|
||||||
|
|
||||||
|
os.makedirs(folder, exist_ok=True)
|
||||||
|
return folder
|
||||||
|
|
||||||
|
# FILE PATH METHODS - download
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_download_path(filename):
|
def get_download_path(filename):
|
||||||
return os.path.join(FolderAndFile.get_download_folder(), filename)
|
return os.path.join(FolderAndFile.get_download_folder(), filename)
|
||||||
|
|
||||||
|
# FILE PATH METHODS - upload file
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_upload_path(filename):
|
def get_upload_path(filename):
|
||||||
return os.path.join(FolderAndFile.get_upload_folder(), filename)
|
return os.path.join(FolderAndFile.get_upload_folder(), filename)
|
||||||
|
|
||||||
|
# FILE PATH METHODS - activity log file
|
||||||
|
@staticmethod
|
||||||
|
def get_activity_log_path(filename):
|
||||||
|
return os.path.join(FolderAndFile.get_logs_folder(), filename)
|
||||||
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:
|
class GST:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_unreleased_gst():
|
def get_unreleased_gst():
|
||||||
|
# Use ItemCRUD for Invoices
|
||||||
|
invoice_crud = ItemCRUD(itemType=ItemCRUDType.Invoice)
|
||||||
|
invoices_rows = invoice_crud.GetAllData(storedproc="GetAllInvoicesBasic")
|
||||||
|
|
||||||
connection = config.get_db_connection()
|
if not invoice_crud.isSuccess:
|
||||||
cursor = connection.cursor(dictionary=True)
|
return [] # Could also log invoice_crud.resultMessage
|
||||||
|
|
||||||
try:
|
invoices = [
|
||||||
# ----------- Invoices -----------
|
dict(
|
||||||
cursor.callproc('GetAllInvoicesBasic')
|
Invoice_No=row[1],
|
||||||
invoices = []
|
GST_SD_Amount=float(row[2]) if row[2] is not None else 0
|
||||||
for result in cursor.stored_results():
|
)
|
||||||
invoices = result.fetchall()
|
for row in invoices_rows
|
||||||
|
]
|
||||||
|
|
||||||
|
# Use ItemCRUD for GST Releases
|
||||||
|
gst_crud = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||||
|
gst_rows = gst_crud.GetAllData(storedproc="GetAllGSTReleasesBasic")
|
||||||
|
|
||||||
# ----------- GST Releases -----------
|
if not gst_crud.isSuccess:
|
||||||
cursor.callproc('GetAllGSTReleasesBasic')
|
return [] # Could also log gst_crud.resultMessage
|
||||||
gst_releases = []
|
|
||||||
for result in cursor.stored_results():
|
|
||||||
gst_releases = result.fetchall()
|
|
||||||
|
|
||||||
gst_invoice_nos = {
|
gst_invoice_nos = {
|
||||||
g['Invoice_No']
|
g[2] # Invoice_No is at index 2
|
||||||
for g in gst_releases
|
for g in gst_rows
|
||||||
if g['Invoice_No']
|
if g[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_basic_amounts = {
|
gst_basic_amounts = {
|
||||||
float(g['Basic_Amount'])
|
float(g[3]) # Basic_Amount at index 3
|
||||||
for g in gst_releases
|
for g in gst_rows
|
||||||
if g['Basic_Amount'] is not None
|
if g[3] is not None
|
||||||
}
|
}
|
||||||
|
|
||||||
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 re
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# Mapping Class
|
# Mapping Class
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
@@ -23,10 +22,11 @@ class itemCRUDMapping:
|
|||||||
self.name = "Hold Type"
|
self.name = "Hold Type"
|
||||||
elif itemType is ItemCRUDType.Subcontractor:
|
elif itemType is ItemCRUDType.Subcontractor:
|
||||||
self.name = "Subcontractor"
|
self.name = "Subcontractor"
|
||||||
|
elif itemType.name == "GSTRelease":
|
||||||
|
self.name = "GSTRelease"
|
||||||
else:
|
else:
|
||||||
self.name = "Item"
|
self.name = "Item"
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# Generic CRUD Class
|
# Generic CRUD Class
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
@@ -93,13 +93,47 @@ class ItemCRUD:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# ======================================================
|
# ======================================================
|
||||||
# SUBCONTRACTOR (MULTI-FIELD)
|
# GSTRelease MULTI-FIELD
|
||||||
# ======================================================
|
# ======================================================
|
||||||
if data:
|
if self.itemCRUDType.name == "GSTRelease" and data:
|
||||||
|
|
||||||
|
# Duplicate check (PMC_No + Invoice_No)
|
||||||
|
if storedprocfetch:
|
||||||
|
cursor.callproc(storedprocfetch, (data['PMC_No'], data['Invoice_No']))
|
||||||
|
existing_item = None
|
||||||
|
for rs in cursor.stored_results():
|
||||||
|
existing_item = rs.fetchone()
|
||||||
|
if existing_item:
|
||||||
|
self.isSuccess = False
|
||||||
|
self.resultMessage = HtmlHelper.json_response(
|
||||||
|
ResponseHandler.already_exists(self.itemCRUDMapping.name), 409
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Insert GSTRelease
|
||||||
|
cursor.callproc(storedprocadd, (
|
||||||
|
data['PMC_No'],
|
||||||
|
data['Invoice_No'],
|
||||||
|
data['Basic_Amount'],
|
||||||
|
data['Final_Amount'],
|
||||||
|
data['Total_Amount'],
|
||||||
|
data['UTR'],
|
||||||
|
data['Contractor_ID']
|
||||||
|
))
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
|
self.isSuccess = True
|
||||||
|
self.resultMessage = HtmlHelper.json_response(
|
||||||
|
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# ======================================================
|
||||||
|
# SUBCONTRACTOR MULTI-FIELD
|
||||||
|
# ======================================================
|
||||||
|
if self.itemCRUDType.name == "Subcontractor" and data:
|
||||||
|
|
||||||
# Duplicate check
|
|
||||||
cursor.callproc(storedprocfetch, (data['Contractor_Name'],))
|
cursor.callproc(storedprocfetch, (data['Contractor_Name'],))
|
||||||
|
|
||||||
existing_item = None
|
existing_item = None
|
||||||
for rs in cursor.stored_results():
|
for rs in cursor.stored_results():
|
||||||
existing_item = rs.fetchone()
|
existing_item = rs.fetchone()
|
||||||
@@ -111,7 +145,6 @@ class ItemCRUD:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Insert
|
|
||||||
cursor.callproc(storedprocadd, (
|
cursor.callproc(storedprocadd, (
|
||||||
data['Contractor_Name'],
|
data['Contractor_Name'],
|
||||||
data['Address'],
|
data['Address'],
|
||||||
@@ -123,26 +156,24 @@ class ItemCRUD:
|
|||||||
data['GST_No'],
|
data['GST_No'],
|
||||||
data['Contractor_password']
|
data['Contractor_password']
|
||||||
))
|
))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
self.isSuccess = True
|
self.isSuccess = True
|
||||||
self.resultMessage = HtmlHelper.json_response(
|
self.resultMessage = HtmlHelper.json_response(
|
||||||
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
|
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# ======================================================
|
# ======================================================
|
||||||
# NORMAL (Village / Block / State)
|
# NORMAL SINGLE-FIELD (Village / Block / State)
|
||||||
# ======================================================
|
# ======================================================
|
||||||
if not re.match(RegEx.patternAlphabetOnly, childname):
|
if not re.match(RegEx.allPattern, childname):
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = HtmlHelper.json_response(
|
self.resultMessage = HtmlHelper.json_response(
|
||||||
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
|
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Duplicate check
|
|
||||||
if parentid is None:
|
if parentid is None:
|
||||||
cursor.callproc(storedprocfetch, (childname,))
|
cursor.callproc(storedprocfetch, (childname,))
|
||||||
else:
|
else:
|
||||||
@@ -159,17 +190,14 @@ class ItemCRUD:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Insert
|
|
||||||
if parentid is None:
|
if parentid is None:
|
||||||
cursor.callproc(storedprocadd, (childname,))
|
cursor.callproc(storedprocadd, (childname,))
|
||||||
else:
|
else:
|
||||||
cursor.callproc(storedprocadd, (childname, parentid))
|
cursor.callproc(storedprocadd, (childname, parentid))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
self.isSuccess = True
|
self.isSuccess = True
|
||||||
self.resultMessage = HtmlHelper.json_response(
|
self.resultMessage = HtmlHelper.json_response(
|
||||||
|
|
||||||
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
|
ResponseHandler.add_success(self.itemCRUDMapping.name), 200
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -199,9 +227,32 @@ class ItemCRUD:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# ======================================================
|
# ======================================================
|
||||||
# SUBCONTRACTOR (MULTI-FIELD)
|
# GSTRelease MULTI-FIELD
|
||||||
# ======================================================
|
# ======================================================
|
||||||
if data:
|
if self.itemCRUDType.name == "GSTRelease" and data:
|
||||||
|
|
||||||
|
cursor.callproc(storedprocupdate, (
|
||||||
|
data['p_pmc_no'], # PMC_No
|
||||||
|
data['p_invoice_no'], # Invoice_No
|
||||||
|
data['p_basic_amount'], # Basic_Amount
|
||||||
|
data['p_final_amount'], # Final_Amount
|
||||||
|
data['p_total_amount'], # Total_Amount
|
||||||
|
data['p_utr'], # UTR
|
||||||
|
data['p_gst_release_id']# GST_Release_Id
|
||||||
|
))
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
|
self.isSuccess = True
|
||||||
|
self.resultMessage = HtmlHelper.json_response(
|
||||||
|
ResponseHandler.update_success(self.itemCRUDMapping.name), 200
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# ======================================================
|
||||||
|
# SUBCONTRACTOR MULTI-FIELD
|
||||||
|
# ======================================================
|
||||||
|
if self.itemCRUDType.name == "Subcontractor" and data:
|
||||||
|
|
||||||
cursor.callproc(storedprocupdate, (
|
cursor.callproc(storedprocupdate, (
|
||||||
childid,
|
childid,
|
||||||
data['Contractor_Name'],
|
data['Contractor_Name'],
|
||||||
@@ -214,9 +265,7 @@ class ItemCRUD:
|
|||||||
data['GST_No'],
|
data['GST_No'],
|
||||||
data['Contractor_password']
|
data['Contractor_password']
|
||||||
))
|
))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
self.isSuccess = True
|
self.isSuccess = True
|
||||||
self.resultMessage = HtmlHelper.json_response(
|
self.resultMessage = HtmlHelper.json_response(
|
||||||
ResponseHandler.update_success(self.itemCRUDMapping.name), 200
|
ResponseHandler.update_success(self.itemCRUDMapping.name), 200
|
||||||
@@ -224,9 +273,9 @@ class ItemCRUD:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# ======================================================
|
# ======================================================
|
||||||
# NORMAL
|
# NORMAL SINGLE-FIELD
|
||||||
# ======================================================
|
# ======================================================
|
||||||
if not re.match(RegEx.patternAlphabetOnly, childname):
|
if not re.match(RegEx.allPattern, childname):
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message']
|
self.resultMessage = ResponseHandler.update_failure(self.itemCRUDMapping.name)['message']
|
||||||
return
|
return
|
||||||
@@ -237,7 +286,6 @@ class ItemCRUD:
|
|||||||
cursor.callproc(storedprocupdate, (childid, parentid, childname))
|
cursor.callproc(storedprocupdate, (childid, parentid, childname))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
self.isSuccess = True
|
self.isSuccess = True
|
||||||
self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message']
|
self.resultMessage = ResponseHandler.update_success(self.itemCRUDMapping.name)['message']
|
||||||
|
|
||||||
@@ -259,20 +307,15 @@ class ItemCRUD:
|
|||||||
|
|
||||||
data = []
|
data = []
|
||||||
connection = config.get_db_connection()
|
connection = config.get_db_connection()
|
||||||
|
|
||||||
if not connection:
|
if not connection:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor.callproc(storedproc)
|
cursor.callproc(storedproc)
|
||||||
|
|
||||||
for result in cursor.stored_results():
|
for result in cursor.stored_results():
|
||||||
data = result.fetchall()
|
data = result.fetchall()
|
||||||
|
|
||||||
self.isSuccess = True
|
self.isSuccess = True
|
||||||
|
|
||||||
except mysql.connector.Error as e:
|
except mysql.connector.Error as e:
|
||||||
print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
|
print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
@@ -280,7 +323,6 @@ class ItemCRUD:
|
|||||||
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
|
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
|
||||||
)
|
)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
@@ -298,13 +340,10 @@ class ItemCRUD:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
cursor.callproc(storedproc, (id,))
|
cursor.callproc(storedproc, (id,))
|
||||||
|
|
||||||
for rs in cursor.stored_results():
|
for rs in cursor.stored_results():
|
||||||
data = rs.fetchone()
|
data = rs.fetchone()
|
||||||
|
|
||||||
except mysql.connector.Error as e:
|
except mysql.connector.Error as e:
|
||||||
print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
|
print(f"Error fetching {self.itemCRUDMapping.name}: {e}")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
@@ -324,7 +363,7 @@ class ItemCRUD:
|
|||||||
f"User {current_user.id} checked '{childname}'"
|
f"User {current_user.id} checked '{childname}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not re.match(RegEx.patternAlphabetOnly, childname):
|
if not re.match(RegEx.allPattern, childname):
|
||||||
return HtmlHelper.json_response(
|
return HtmlHelper.json_response(
|
||||||
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
|
ResponseHandler.invalid_name(self.itemCRUDMapping.name), 400
|
||||||
)
|
)
|
||||||
@@ -353,7 +392,6 @@ class ItemCRUD:
|
|||||||
return HtmlHelper.json_response(
|
return HtmlHelper.json_response(
|
||||||
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
|
ResponseHandler.fetch_failure(self.itemCRUDMapping.name), 500
|
||||||
)
|
)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
29
model/Log.py
29
model/Log.py
@@ -1,10 +1,10 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash, jsonify, json
|
import os
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
|
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
|
||||||
|
|
||||||
import os
|
from datetime import datetime
|
||||||
|
|
||||||
|
from model.FolderAndFile import FolderAndFile
|
||||||
|
|
||||||
class LogHelper:
|
class LogHelper:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -14,22 +14,24 @@ class LogHelper:
|
|||||||
logData.WriteLog(action, details="")
|
logData.WriteLog(action, details="")
|
||||||
|
|
||||||
class LogData:
|
class LogData:
|
||||||
|
|
||||||
filepath = ""
|
filepath = ""
|
||||||
timestamp = None
|
timestamp = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.filepath = os.path.join(current_app.root_path, 'activity.log')
|
self.filepath = FolderAndFile.get_activity_log_path('activity.log')
|
||||||
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
self.user = LogData.get_current_user()
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_current_user():
|
||||||
if hasattr(current_user, "cn") and current_user.cn:
|
if hasattr(current_user, "cn") and current_user.cn:
|
||||||
self.user = current_user.cn
|
return current_user.cn
|
||||||
elif hasattr(current_user, "username") and current_user.username:
|
elif hasattr(current_user, "username") and current_user.username:
|
||||||
self.user = current_user.username
|
return current_user.username
|
||||||
elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName:
|
elif hasattr(current_user, "sAMAccountName") and current_user.sAMAccountName:
|
||||||
self.user = current_user.sAMAccountName
|
return current_user.sAMAccountName
|
||||||
else:
|
return "Unknown"
|
||||||
self.user = "Unknown"
|
|
||||||
|
|
||||||
def WriteLog(self, action, details=""):
|
def WriteLog(self, action, details=""):
|
||||||
"""Log user actions with timestamp, user, action, and details."""
|
"""Log user actions with timestamp, user, action, and details."""
|
||||||
@@ -42,7 +44,6 @@ class LogData:
|
|||||||
f"Details: {details}\n"
|
f"Details: {details}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def GetActivitiesLog(self):
|
def GetActivitiesLog(self):
|
||||||
logs = []
|
logs = []
|
||||||
|
|
||||||
@@ -60,7 +61,6 @@ class LogData:
|
|||||||
return logs
|
return logs
|
||||||
|
|
||||||
def GetFilteredActivitiesLog(self, startDate, endDate, userName):
|
def GetFilteredActivitiesLog(self, startDate, endDate, userName):
|
||||||
|
|
||||||
filtered_logs = self.GetActivitiesLog()
|
filtered_logs = self.GetActivitiesLog()
|
||||||
|
|
||||||
# Date filter
|
# Date filter
|
||||||
@@ -69,16 +69,13 @@ class LogData:
|
|||||||
start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min
|
start_dt = datetime.strptime(startDate, "%Y-%m-%d") if startDate else datetime.min
|
||||||
end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max
|
end_dt = datetime.strptime(endDate, "%Y-%m-%d") if endDate else datetime.max
|
||||||
|
|
||||||
|
|
||||||
filtered_logs = [
|
filtered_logs = [
|
||||||
log for log in filtered_logs
|
log for log in filtered_logs
|
||||||
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Date filter error:", e)
|
print("Date filter error:", e)
|
||||||
#Why catching all exceptions? Need to handle specific exceptions
|
|
||||||
|
|
||||||
# Username filter
|
# Username filter
|
||||||
if userName:
|
if userName:
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
from flask import request, redirect, url_for
|
|
||||||
from flask_login import current_user
|
|
||||||
|
|
||||||
from model.Utilities import RegEx, ResponseHandler, HtmlHelper, ItemCRUDType
|
|
||||||
from model.Log import LogHelper
|
|
||||||
from model.ItemCRUD import ItemCRUD
|
|
||||||
|
|
||||||
import config
|
import config
|
||||||
import re
|
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
|
from flask import request, redirect, url_for
|
||||||
|
|
||||||
|
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
|
||||||
|
from model.ItemCRUD import ItemCRUD
|
||||||
|
|
||||||
|
|
||||||
class State:
|
class State:
|
||||||
@@ -20,7 +16,6 @@ class State:
|
|||||||
# ADD STATE (USING ITEM CRUD)
|
# ADD STATE (USING ITEM CRUD)
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
def AddState(self, request):
|
def AddState(self, request):
|
||||||
|
|
||||||
state_name = request.form['state_Name'].strip()
|
state_name = request.form['state_Name'].strip()
|
||||||
|
|
||||||
crud = ItemCRUD(ItemCRUDType.State)
|
crud = ItemCRUD(ItemCRUDType.State)
|
||||||
@@ -42,16 +37,14 @@ class State:
|
|||||||
# GET ALL STATES (NO CHANGE - THIS IS CORRECT)
|
# GET ALL STATES (NO CHANGE - THIS IS CORRECT)
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
def GetAllStates(self, request):
|
def GetAllStates(self, request):
|
||||||
|
|
||||||
connection = config.get_db_connection()
|
connection = config.get_db_connection()
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
if not connection:
|
if not connection:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
cursor = connection.cursor()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
cursor = connection.cursor()
|
||||||
cursor.callproc("GetAllStates")
|
cursor.callproc("GetAllStates")
|
||||||
for res in cursor.stored_results():
|
for res in cursor.stored_results():
|
||||||
data = res.fetchall()
|
data = res.fetchall()
|
||||||
@@ -77,7 +70,6 @@ class State:
|
|||||||
# CHECK STATE (USING ITEM CRUD)
|
# CHECK STATE (USING ITEM CRUD)
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
def CheckState(self, request):
|
def CheckState(self, request):
|
||||||
|
|
||||||
state_name = request.json.get('state_Name', '').strip()
|
state_name = request.json.get('state_Name', '').strip()
|
||||||
|
|
||||||
crud = ItemCRUD(ItemCRUDType.State)
|
crud = ItemCRUD(ItemCRUDType.State)
|
||||||
@@ -94,7 +86,6 @@ class State:
|
|||||||
# DELETE STATE (USING ITEM CRUD)
|
# DELETE STATE (USING ITEM CRUD)
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
def DeleteState(self, request, id):
|
def DeleteState(self, request, id):
|
||||||
|
|
||||||
crud = ItemCRUD(ItemCRUDType.State)
|
crud = ItemCRUD(ItemCRUDType.State)
|
||||||
|
|
||||||
crud.DeleteItem(
|
crud.DeleteItem(
|
||||||
@@ -113,7 +104,6 @@ class State:
|
|||||||
# EDIT STATE (USING ITEM CRUD)
|
# EDIT STATE (USING ITEM CRUD)
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
def EditState(self, request, id):
|
def EditState(self, request, id):
|
||||||
|
|
||||||
state_name = request.form['state_Name'].strip()
|
state_name = request.form['state_Name'].strip()
|
||||||
|
|
||||||
crud = ItemCRUD(ItemCRUDType.State)
|
crud = ItemCRUD(ItemCRUDType.State)
|
||||||
@@ -136,16 +126,14 @@ class State:
|
|||||||
# GET STATE BY ID (KEEP SAME)
|
# GET STATE BY ID (KEEP SAME)
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
def GetStateByID(self, request, id):
|
def GetStateByID(self, request, id):
|
||||||
|
|
||||||
connection = config.get_db_connection()
|
connection = config.get_db_connection()
|
||||||
data = None
|
data = None
|
||||||
|
|
||||||
if not connection:
|
if not connection:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cursor = connection.cursor()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
cursor = connection.cursor()
|
||||||
cursor.callproc("GetStateByID", (id,))
|
cursor.callproc("GetStateByID", (id,))
|
||||||
for res in cursor.stored_results():
|
for res in cursor.stored_results():
|
||||||
data = res.fetchone()
|
data = res.fetchone()
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ class ItemCRUDType(Enum):
|
|||||||
State = 4
|
State = 4
|
||||||
HoldType = 5
|
HoldType = 5
|
||||||
Subcontractor = 6
|
Subcontractor = 6
|
||||||
|
GSTRelease = 7
|
||||||
|
|
||||||
class RegEx:
|
class RegEx:
|
||||||
patternAlphabetOnly = "^[A-Za-z ]+$"
|
patternAlphabetOnly = "^[A-Za-z ]+$"
|
||||||
|
allPattern = "^(?!\s*$).+"
|
||||||
|
|
||||||
|
|
||||||
class ResponseHandler:
|
class ResponseHandler:
|
||||||
@@ -62,5 +63,5 @@ class HtmlHelper:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def json_response(message_obj, status_code):
|
def json_response(message_obj, status_code):
|
||||||
return jsonify(message_obj), status_code
|
return jsonify(message_obj), status_code
|
||||||
#May need to refactor further
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# return blocks
|
|
||||||
|
|
||||||
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
|
from model.Utilities import ResponseHandler, HtmlHelper, ItemCRUDType
|
||||||
import config
|
import config
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
@@ -12,12 +13,19 @@ class Village:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = ""
|
self.resultMessage = ""
|
||||||
|
self.response = {} # ✅ ADDED
|
||||||
self.village = ItemCRUD(itemType=ItemCRUDType.Village)
|
self.village = ItemCRUD(itemType=ItemCRUDType.Village)
|
||||||
|
|
||||||
# 🔹 Helper: sync status
|
# 🔹 Helper: sync status
|
||||||
def _set_status(self, village):
|
def _set_status(self, village):
|
||||||
self.isSuccess = village.isSuccess
|
self.isSuccess = village.isSuccess
|
||||||
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
|
# 🔹 Helper: get request data
|
||||||
def _get_form_data(self, request):
|
def _get_form_data(self, request):
|
||||||
@@ -29,8 +37,9 @@ class Village:
|
|||||||
block_id, village_name = self._get_form_data(request)
|
block_id, village_name = self._get_form_data(request)
|
||||||
|
|
||||||
if not village_name:
|
if not village_name:
|
||||||
|
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
|
||||||
|
self.resultMessage = self.response["message"]
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = "Village name cannot be empty"
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -66,8 +75,9 @@ class Village:
|
|||||||
block_id, village_name = self._get_form_data(request)
|
block_id, village_name = self._get_form_data(request)
|
||||||
|
|
||||||
if not village_name:
|
if not village_name:
|
||||||
|
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
|
||||||
|
self.resultMessage = self.response["message"]
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = "Village name cannot be empty"
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -103,8 +113,9 @@ class Village:
|
|||||||
block_id, village_name = self._get_form_data(request)
|
block_id, village_name = self._get_form_data(request)
|
||||||
|
|
||||||
if not village_name:
|
if not village_name:
|
||||||
|
self.response = ResponseHandler.invalid_name("village") # ✅ UPDATED
|
||||||
|
self.resultMessage = self.response["message"]
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = "Village name cannot be empty"
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -164,9 +175,11 @@ class Village:
|
|||||||
except mysql.connector.Error as e:
|
except mysql.connector.Error as e:
|
||||||
print(f"Error fetching blocks: {e}")
|
print(f"Error fetching blocks: {e}")
|
||||||
self.isSuccess = False
|
self.isSuccess = False
|
||||||
self.resultMessage = HtmlHelper.json_response(
|
|
||||||
ResponseHandler.fetch_failure("block"), 500
|
# ✅ FIXED (removed jsonify response)
|
||||||
)
|
self.response = ResponseHandler.fetch_failure("block")
|
||||||
|
self.resultMessage = self.response["message"]
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
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,160 @@
|
|||||||
import config
|
# model/gst_release.py
|
||||||
import mysql.connector
|
from flask import request, jsonify
|
||||||
|
from model.ItemCRUD import ItemCRUD
|
||||||
|
from model.Utilities import ItemCRUDType
|
||||||
|
|
||||||
class GSTReleasemodel:
|
class GSTRelease:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.isSuccess = False
|
||||||
|
self.resultMessage = ""
|
||||||
|
|
||||||
|
# ------------------- Add GST Release -------------------
|
||||||
|
def AddGSTRelease(self, request):
|
||||||
|
try:
|
||||||
|
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||||
|
|
||||||
|
# Print the full form data
|
||||||
|
print("===== DEBUG: FORM DATA =====")
|
||||||
|
for key, value in request.form.items():
|
||||||
|
print(f"{key} : {value}")
|
||||||
|
print("=============================")
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
print("===== DEBUG: PARSED DATA =====")
|
||||||
|
print(data)
|
||||||
|
print("==============================")
|
||||||
|
|
||||||
|
# Add GST Release
|
||||||
|
gst.AddItem(
|
||||||
|
request=request,
|
||||||
|
data=data,
|
||||||
|
storedprocfetch="CheckGSTReleaseExists",
|
||||||
|
storedprocadd="AddGSTReleaseFromExcel"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"AddItem result: isSuccess={gst.isSuccess}, message={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})
|
||||||
|
|
||||||
|
def EditGSTRelease(self, request, gst_release_id):
|
||||||
|
try:
|
||||||
|
gst = ItemCRUD(itemType=ItemCRUDType.GSTRelease)
|
||||||
|
|
||||||
|
# Map form inputs to stored procedure parameters
|
||||||
|
data = {
|
||||||
|
"p_pmc_no": request.form.get("PMC_No", "").strip(),
|
||||||
|
"p_invoice_no": request.form.get("invoice_no", "").strip(),
|
||||||
|
"p_basic_amount": float(request.form.get("Basic_Amount", 0) or 0),
|
||||||
|
"p_final_amount": float(request.form.get("Final_Amount", 0) or 0),
|
||||||
|
"p_total_amount": float(request.form.get("Total_Amount", 0) or 0),
|
||||||
|
"p_utr": request.form.get("UTR", "").strip(),
|
||||||
|
"p_gst_release_id": gst_release_id
|
||||||
|
}
|
||||||
|
|
||||||
|
print("===== DEBUG: UPDATE DATA =====")
|
||||||
|
print(data)
|
||||||
|
print("==============================")
|
||||||
|
|
||||||
|
# Call your stored procedure
|
||||||
|
gst.EditItem(
|
||||||
|
request=request,
|
||||||
|
childid=gst_release_id,
|
||||||
|
data=data,
|
||||||
|
storedprocupdate="UpdateGSTRelease"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# ------------------- 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]
|
||||||
|
}
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_connection():
|
|
||||||
connection = config.get_db_connection()
|
|
||||||
if not connection:
|
|
||||||
return None
|
return None
|
||||||
return connection
|
|
||||||
|
|
||||||
@staticmethod
|
except Exception as e:
|
||||||
def fetch_all_gst_releases():
|
print("ERROR in GetGSTReleaseByID:", e)
|
||||||
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
|
|
||||||
try:
|
|
||||||
cursor = connection.cursor()
|
|
||||||
|
|
||||||
# Insert into gst_release
|
|
||||||
cursor.callproc(
|
|
||||||
'InsertGSTReleaseOnly',
|
|
||||||
[pmc_no, invoice_no, basic_amount, final_amount, total_amount, utr, contractor_id]
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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
|
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()
|
|
||||||
104
model/payment.py
104
model/payment.py
@@ -1,8 +1,13 @@
|
|||||||
import config
|
import config
|
||||||
import mysql.connector
|
import mysql.connector
|
||||||
|
import config
|
||||||
|
import mysql.connector
|
||||||
|
from enum import Enum
|
||||||
|
from model.Utilities import ItemCRUDType
|
||||||
|
|
||||||
class Paymentmodel:
|
class Paymentmodel:
|
||||||
|
|
||||||
|
# ---------------- Database Connection ----------------
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_connection():
|
def get_connection():
|
||||||
connection = config.get_db_connection()
|
connection = config.get_db_connection()
|
||||||
@@ -10,6 +15,7 @@ class Paymentmodel:
|
|||||||
return None
|
return None
|
||||||
return connection
|
return connection
|
||||||
|
|
||||||
|
# ---------------- Payment Methods ----------------
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fetch_all_payments():
|
def fetch_all_payments():
|
||||||
connection = Paymentmodel.get_connection()
|
connection = Paymentmodel.get_connection()
|
||||||
@@ -52,14 +58,8 @@ class Paymentmodel:
|
|||||||
try:
|
try:
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.callproc('UpdateInpaymentRecord', [
|
cursor.callproc('UpdateInpaymentRecord', [
|
||||||
subcontractor_id,
|
subcontractor_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr
|
||||||
pmc_no,
|
])
|
||||||
invoice_no,
|
|
||||||
amount,
|
|
||||||
tds_amount,
|
|
||||||
total_amount,
|
|
||||||
utr
|
|
||||||
])
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
return True
|
return True
|
||||||
except mysql.connector.Error as e:
|
except mysql.connector.Error as e:
|
||||||
@@ -80,7 +80,6 @@ class Paymentmodel:
|
|||||||
cursor.callproc("GetPaymentById", (payment_id,))
|
cursor.callproc("GetPaymentById", (payment_id,))
|
||||||
for result in cursor.stored_results():
|
for result in cursor.stored_results():
|
||||||
payment_data = result.fetchone()
|
payment_data = result.fetchone()
|
||||||
# Convert to array for template
|
|
||||||
if payment_data:
|
if payment_data:
|
||||||
payment_data = [
|
payment_data = [
|
||||||
payment_data.get('Payment_Id'),
|
payment_data.get('Payment_Id'),
|
||||||
@@ -117,42 +116,99 @@ class Paymentmodel:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_payment(payment_id):
|
def delete_payment(payment_id):
|
||||||
"""
|
|
||||||
Deletes a payment and resets the related inpayment fields in one go.
|
|
||||||
Returns (success, pmc_no, invoice_no)
|
|
||||||
"""
|
|
||||||
connection = Paymentmodel.get_connection()
|
connection = Paymentmodel.get_connection()
|
||||||
if not connection:
|
if not connection:
|
||||||
return False, None, None
|
return False, None, None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor = connection.cursor(dictionary=True)
|
cursor = connection.cursor(dictionary=True)
|
||||||
|
# Fetch PMC & Invoice before deleting
|
||||||
cursor.callproc('GetPaymentPMCInvoiceById', [payment_id])
|
cursor.callproc('GetPaymentPMCInvoiceById', [payment_id])
|
||||||
|
|
||||||
record = {}
|
record = {}
|
||||||
for result in cursor.stored_results():
|
for result in cursor.stored_results():
|
||||||
record = result.fetchone() or {}
|
record = result.fetchone() or {}
|
||||||
if not record:
|
if not record:
|
||||||
return False, None, None
|
return False, None, None
|
||||||
|
|
||||||
pmc_no = record['PMC_No']
|
pmc_no = record['PMC_No']
|
||||||
invoice_no = record['Invoice_No']
|
invoice_no = record['Invoice_No']
|
||||||
|
# Delete payment
|
||||||
# Step 2: Delete the payment using the stored procedure
|
|
||||||
cursor.callproc("DeletePayment", (payment_id,))
|
cursor.callproc("DeletePayment", (payment_id,))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
# Reset inpayment fields
|
||||||
# Step 3: Reset inpayment fields using the stored procedure
|
|
||||||
cursor.callproc("ResetInpayment", [pmc_no, invoice_no])
|
cursor.callproc("ResetInpayment", [pmc_no, invoice_no])
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
return True, pmc_no, invoice_no
|
return True, pmc_no, invoice_no
|
||||||
|
|
||||||
except mysql.connector.Error as e:
|
except mysql.connector.Error as e:
|
||||||
print(f"Error deleting payment: {e}")
|
print(f"Error deleting payment: {e}")
|
||||||
return False, None, None
|
return False, None, None
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
# ---------------- Item CRUD Methods ----------------
|
||||||
|
@staticmethod
|
||||||
|
def fetch_items(item_type: ItemCRUDType):
|
||||||
|
connection = Paymentmodel.get_connection()
|
||||||
|
items = []
|
||||||
|
if connection:
|
||||||
|
cursor = connection.cursor(dictionary=True)
|
||||||
|
try:
|
||||||
|
cursor.callproc('GetItemsByType', [item_type.value])
|
||||||
|
for result in cursor.stored_results():
|
||||||
|
items = result.fetchall()
|
||||||
|
except mysql.connector.Error as e:
|
||||||
|
print(f"Error fetching {item_type.name}: {e}")
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
connection.close()
|
||||||
|
return items
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def insert_item(item_type: ItemCRUDType, name: str):
|
||||||
|
connection = Paymentmodel.get_connection()
|
||||||
|
if not connection:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.callproc('InsertItem', [item_type.value, name])
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
except mysql.connector.Error as e:
|
||||||
|
print(f"Error inserting {item_type.name}: {e}")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_item(item_type: ItemCRUDType, item_id: int, new_name: str):
|
||||||
|
connection = Paymentmodel.get_connection()
|
||||||
|
if not connection:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.callproc('UpdateItem', [item_type.value, item_id, new_name])
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
except mysql.connector.Error as e:
|
||||||
|
print(f"Error updating {item_type.name}: {e}")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
cursor.close()
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete_item(item_type: ItemCRUDType, item_id: int):
|
||||||
|
connection = Paymentmodel.get_connection()
|
||||||
|
if not connection:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.callproc('DeleteItem', [item_type.value, item_id])
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
except mysql.connector.Error as e:
|
||||||
|
print(f"Error deleting {item_type.name}: {e}")
|
||||||
|
return False
|
||||||
finally:
|
finally:
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
@@ -3,7 +3,7 @@ $(document).ready(function () {
|
|||||||
let holdType = $(this).val().replace(/^\s+/, "");
|
let holdType = $(this).val().replace(/^\s+/, "");
|
||||||
$(this).val(holdType);
|
$(this).val(holdType);
|
||||||
|
|
||||||
let reg = /^[A-Za-z]/;
|
let reg = /^.+$/; // all pattern allow
|
||||||
|
|
||||||
if (!reg.test(holdType)) {
|
if (!reg.test(holdType)) {
|
||||||
$("#holdTypeMessage").text("Hold Type must start with a letter.").css("color", "red");
|
$("#holdTypeMessage").text("Hold Type must start with a letter.").css("color", "red");
|
||||||
|
|||||||
@@ -1,62 +1,271 @@
|
|||||||
// Subcontractor autocomplete functionality
|
// $(document).ready(function () {
|
||||||
$(document).ready(function () {
|
// // ===============================
|
||||||
$("#subcontractor").keyup(function () {
|
// // FORM / TABLE TOGGLE
|
||||||
|
// // ===============================
|
||||||
|
// if ($('#addForm').length && $('#addTable').length) {
|
||||||
|
// $('#addForm').show();
|
||||||
|
// $('#addTable').hide();
|
||||||
|
|
||||||
|
// $('#addButton').click(function () {
|
||||||
|
// $('#addForm').show();
|
||||||
|
// $('#addTable').hide();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// $('#displayButton').click(function () {
|
||||||
|
// $('#addForm').hide();
|
||||||
|
// $('#addTable').show();
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ===============================
|
||||||
|
// // Subcontractor autocomplete
|
||||||
|
// // ===============================
|
||||||
|
// $("#subcontractor").keyup(function () {
|
||||||
|
// let query = $(this).val();
|
||||||
|
// if (query !== "") {
|
||||||
|
// $.ajax({
|
||||||
|
// url: "/search_subcontractor",
|
||||||
|
// method: "POST",
|
||||||
|
// data: { query: query },
|
||||||
|
// success: function (data) {
|
||||||
|
// $("#subcontractor_list").fadeIn().html(data);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
// $("#subcontractor_list").fadeOut();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// $(document).on("click", "li", function () {
|
||||||
|
// $("#subcontractor").val($(this).text());
|
||||||
|
// $("#subcontractor_id").val($(this).attr("data-id"));
|
||||||
|
// $("#subcontractor_list").fadeOut();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// // Focus
|
||||||
|
// if (document.getElementById('subcontractor')) {
|
||||||
|
// document.getElementById('subcontractor').focus();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ===============================
|
||||||
|
// // ADD INVOICE
|
||||||
|
// // ===============================
|
||||||
|
// if ($('#invoiceForm').length && window.location.href.includes('add_invoice')) {
|
||||||
|
// $("#invoiceForm").on("submit", function (e) {
|
||||||
|
// e.preventDefault();
|
||||||
|
// let formData = $(this).serialize();
|
||||||
|
|
||||||
|
// $.ajax({
|
||||||
|
// url: '/add_invoice',
|
||||||
|
// method: 'POST',
|
||||||
|
// data: formData,
|
||||||
|
// dataType: 'json',
|
||||||
|
// success: function (response) {
|
||||||
|
// if (response.status === "success") {
|
||||||
|
// alert(response.message || "Invoice added successfully!");
|
||||||
|
// $('#invoiceForm')[0].reset(); // clear form
|
||||||
|
// $('#addForm').hide();
|
||||||
|
// $('#addTable').show(); // switch to table
|
||||||
|
// location.reload(); // optional refresh
|
||||||
|
// } else {
|
||||||
|
// alert(response.message || "Error adding invoice.");
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// error: function (xhr) {
|
||||||
|
// alert(xhr.responseJSON?.message || "Submission failed. Please try again.");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// // Example AJAX update function
|
||||||
|
// function updateInvoice(invoiceId, formElement) {
|
||||||
|
// const formData = $(formElement).serialize();
|
||||||
|
|
||||||
|
// $.ajax({
|
||||||
|
// url: '/update_invoice/' + invoiceId,
|
||||||
|
// method: 'POST',
|
||||||
|
// data: formData,
|
||||||
|
// dataType: 'json',
|
||||||
|
// success: function(response) {
|
||||||
|
// if(response.status === "success") {
|
||||||
|
// alert(response.message || "Invoice updated successfully!");
|
||||||
|
|
||||||
|
// // ✅ Hide Add Form, Show Table
|
||||||
|
// $('#addForm').hide();
|
||||||
|
// $('#addTable').show();
|
||||||
|
|
||||||
|
// // Optional: reload table or refresh page
|
||||||
|
// location.reload();
|
||||||
|
// } else {
|
||||||
|
// alert(response.message || "Update failed. Please try again.");
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// error: function(xhr) {
|
||||||
|
// alert(xhr.responseJSON?.message || "Error updating invoice.");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ===============================
|
||||||
|
// // DELETE INVOICE
|
||||||
|
// // ===============================
|
||||||
|
// function deleteInvoice(invoiceId, element) {
|
||||||
|
// if (!confirm("Are you sure you want to delete this invoice?")) return;
|
||||||
|
|
||||||
|
// $.ajax({
|
||||||
|
// url: '/delete_invoice/' + invoiceId,
|
||||||
|
// method: 'GET',
|
||||||
|
// dataType: 'json',
|
||||||
|
// success: function (response) {
|
||||||
|
// alert(response.message || "Invoice deleted successfully!");
|
||||||
|
// if (element) $(element).closest("tr").remove();
|
||||||
|
// },
|
||||||
|
// error: function (xhr) {
|
||||||
|
// alert(xhr.responseJSON?.message || "Error deleting invoice. Please try again.");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
// ===============================
|
||||||
|
// FORM / TABLE TOGGLE
|
||||||
|
// ===============================
|
||||||
|
if ($('#addForm').length && $('#addTable').length) {
|
||||||
|
// Default: show form, hide table
|
||||||
|
$('#addForm').show();
|
||||||
|
$('#addTable').hide();
|
||||||
|
|
||||||
|
// ✅ Check URL hash to show table instead
|
||||||
|
if (window.location.hash === "#addTable") {
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#addButton').click(function () {
|
||||||
|
$('#addForm').show();
|
||||||
|
$('#addTable').hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#displayButton').click(function () {
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// Subcontractor autocomplete
|
||||||
|
// ===============================
|
||||||
|
$("#subcontractor").keyup(function () {
|
||||||
let query = $(this).val();
|
let query = $(this).val();
|
||||||
if (query !== "") {
|
if (query !== "") {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/search_subcontractor",
|
url: "/search_subcontractor",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: { query: query },
|
data: { query: query },
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
$("#subcontractor_list").fadeIn().html(data);
|
$("#subcontractor_list").fadeIn().html(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
$("#subcontractor_list").fadeOut();
|
$("#subcontractor_list").fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("click", "li", function () {
|
$(document).on("click", "li", function () {
|
||||||
$("#subcontractor").val($(this).text());
|
$("#subcontractor").val($(this).text());
|
||||||
$("#subcontractor_id").val($(this).attr("data-id"));
|
$("#subcontractor_id").val($(this).attr("data-id"));
|
||||||
$("#subcontractor_list").fadeOut();
|
$("#subcontractor_list").fadeOut();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Success Alert: show alert and reload after 3 seconds
|
// Focus
|
||||||
function showSuccessAlert() {
|
if (document.getElementById('subcontractor')) {
|
||||||
const alertBox = document.getElementById("invoiceSuccessAlert");
|
document.getElementById('subcontractor').focus();
|
||||||
alertBox.classList.add("show");
|
|
||||||
setTimeout(() => {
|
|
||||||
alertBox.classList.remove("show");
|
|
||||||
// Reload page or redirect after alert hides
|
|
||||||
window.location.href = '/add_invoice';
|
|
||||||
}, 3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Submit form via AJAX
|
// ===============================
|
||||||
$("#invoiceForm").on("submit", function (e) {
|
// ADD INVOICE
|
||||||
e.preventDefault();
|
// ===============================
|
||||||
let formData = $(this).serialize();
|
if ($('#invoiceForm').length && window.location.href.includes('add_invoice')) {
|
||||||
$.ajax({
|
$("#invoiceForm").on("submit", function (e) {
|
||||||
url: '/add_invoice',
|
e.preventDefault();
|
||||||
method: 'POST',
|
let formData = $(this).serialize();
|
||||||
data: formData,
|
|
||||||
success: function (response) {
|
|
||||||
if(response.status === "success") {
|
|
||||||
showSuccessAlert();
|
|
||||||
} else {
|
|
||||||
alert(response.message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (xhr, status, error) {
|
|
||||||
alert("Submission failed: " + error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/add_invoice',
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
if (response.status === "success") {
|
||||||
|
alert(response.message || "Invoice added successfully!");
|
||||||
|
$('#invoiceForm')[0].reset(); // clear form
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show(); // switch to table
|
||||||
|
location.reload(); // optional refresh
|
||||||
|
} else {
|
||||||
|
alert(response.message || "Error adding invoice.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
let msg = xhr.responseJSON?.message || "Submission failed. Please try again.";
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// UPDATE INVOICE
|
||||||
|
// ===============================
|
||||||
|
function updateInvoice(invoiceId, formElement) {
|
||||||
|
const formData = $(formElement).serialize();
|
||||||
|
|
||||||
window.onload = function () {
|
$.ajax({
|
||||||
document.getElementById('subcontractor').focus();
|
url: '/update_invoice/' + invoiceId,
|
||||||
};
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if(response.status === "success") {
|
||||||
|
alert(response.message || "Invoice updated successfully!");
|
||||||
|
|
||||||
|
// Redirect to Add Invoice page's table part
|
||||||
|
window.location.href = "/add_invoice#addTable";
|
||||||
|
} else {
|
||||||
|
alert(response.message || "Update failed. Please try again.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
let msg = xhr.responseJSON?.message || "Error updating invoice.";
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.updateInvoice = updateInvoice; // make globally accessible
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// DELETE INVOICE
|
||||||
|
// ===============================
|
||||||
|
function deleteInvoice(invoiceId, element) {
|
||||||
|
if (!confirm("Are you sure you want to delete this invoice?")) return;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/delete_invoice/' + invoiceId,
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
if (response.status === "success") {
|
||||||
|
alert(response.message || "Invoice deleted successfully!");
|
||||||
|
if (element) $(element).closest("tr").remove();
|
||||||
|
} else {
|
||||||
|
alert(response.message || "Error deleting invoice.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
let msg = xhr.responseJSON?.message || "Error deleting invoice. Please try again.";
|
||||||
|
alert(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.deleteInvoice = deleteInvoice; // make globally accessible
|
||||||
|
});
|
||||||
@@ -1,26 +1,23 @@
|
|||||||
|
|
||||||
// Search on table using search inpute options
|
// Search on table using search inpute options
|
||||||
function searchTable() {
|
function searchTable() {
|
||||||
let input = document.getElementById("searchBar").value.toLowerCase();
|
let input = document.getElementById("searchBar").value.toLowerCase();
|
||||||
let rows = document.querySelectorAll("table tbody tr");
|
let tables = document.querySelectorAll("table");
|
||||||
|
|
||||||
rows.forEach(row => {
|
tables.forEach(table => {
|
||||||
let blockName = row.cells[1].textContent.toLowerCase();
|
let rows = table.querySelectorAll("tr");
|
||||||
let districtName = row.cells[2].textContent.toLowerCase();
|
|
||||||
let villageName = row.cells[3].textContent.toLowerCase();
|
|
||||||
|
|
||||||
if (blockName.includes(input) || districtName.includes(input)|| villageName.includes(input)) {
|
rows.forEach((row, index) => {
|
||||||
row.style.display = "";
|
if (index === 0) return; // header skip
|
||||||
} else {
|
|
||||||
row.style.display = "none";
|
let text = row.textContent.toLowerCase();
|
||||||
}
|
|
||||||
|
row.style.display = text.includes(input) ? "" : "none";
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Common Sorting Script for Tables
|
// Common Sorting Script for Tables
|
||||||
function sortTable(n, dir) {
|
function sortTable(n, dir) {
|
||||||
var table, rows, switching, i, x, y, shouldSwitch;
|
var table, rows, switching, i, x, y, shouldSwitch;
|
||||||
@@ -57,14 +54,14 @@ function sortTable(n, dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach sorting functionality to all sortable tables
|
// Attach sorting functionality to all sortable tables
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Find all elements with the class "sortable-header"
|
// Find all elements with the class "sortable-header"
|
||||||
var sortableHeaders = document.querySelectorAll(".sortable-header");
|
var sortableHeaders = document.querySelectorAll(".sortable-header");
|
||||||
|
|
||||||
sortableHeaders.forEach(function(header) {
|
sortableHeaders.forEach(function (header) {
|
||||||
// Attach click event for ascending sort
|
// Attach click event for ascending sort
|
||||||
if (header.querySelector(".sort-asc")) {
|
if (header.querySelector(".sort-asc")) {
|
||||||
header.querySelector(".sort-asc").addEventListener("click", function() {
|
header.querySelector(".sort-asc").addEventListener("click", function () {
|
||||||
var columnIndex = Array.from(header.parentNode.children).indexOf(header);
|
var columnIndex = Array.from(header.parentNode.children).indexOf(header);
|
||||||
sortTable(columnIndex, "asc");
|
sortTable(columnIndex, "asc");
|
||||||
});
|
});
|
||||||
@@ -72,7 +69,7 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
|
|
||||||
// Attach click event for descending sort
|
// Attach click event for descending sort
|
||||||
if (header.querySelector(".sort-desc")) {
|
if (header.querySelector(".sort-desc")) {
|
||||||
header.querySelector(".sort-desc").addEventListener("click", function() {
|
header.querySelector(".sort-desc").addEventListener("click", function () {
|
||||||
var columnIndex = Array.from(header.parentNode.children).indexOf(header);
|
var columnIndex = Array.from(header.parentNode.children).indexOf(header);
|
||||||
sortTable(columnIndex, "desc");
|
sortTable(columnIndex, "desc");
|
||||||
});
|
});
|
||||||
@@ -106,3 +103,30 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
addButton.classList.remove("active-button");
|
addButton.classList.remove("active-button");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|
||||||
|
let tables = document.querySelectorAll("table");
|
||||||
|
|
||||||
|
tables.forEach(table => {
|
||||||
|
let header = table.querySelector("tr:first-child");
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
header.style.position = "sticky";
|
||||||
|
header.style.top = "0";
|
||||||
|
header.style.background = "#fff";
|
||||||
|
header.style.zIndex = "2";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!table.parentElement.classList.contains("table-wrapper")) {
|
||||||
|
let wrapper = document.createElement("div");
|
||||||
|
wrapper.classList.add("table-wrapper");
|
||||||
|
wrapper.style.maxHeight = "65vh"
|
||||||
|
wrapper.style.overflowY = "auto";
|
||||||
|
|
||||||
|
table.parentNode.insertBefore(wrapper, table);
|
||||||
|
wrapper.appendChild(table);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,198 +1,250 @@
|
|||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
document.getElementById('Village_Name').focus();
|
if (document.getElementById('Village_Name')) {
|
||||||
|
document.getElementById('Village_Name').focus();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
|
// ✅ RUN ONLY IF THIS PAGE HAS FORM/TABLE
|
||||||
|
if ($('#addForm').length && $('#addTable').length) {
|
||||||
|
|
||||||
|
// ✅ DEFAULT VIEW → SHOW FORM
|
||||||
|
$('#addForm').show();
|
||||||
|
$('#addTable').hide();
|
||||||
|
|
||||||
|
|
||||||
|
// 🔥 BUTTON TOGGLE
|
||||||
|
$('#addButton').click(function () {
|
||||||
|
$('#addForm').show();
|
||||||
|
$('#addTable').hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#displayButton').click(function () {
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================
|
||||||
// STATE → DISTRICT
|
// STATE → DISTRICT
|
||||||
$('#state_Id').change(function () {
|
// ===============================
|
||||||
|
if ($('#state_Id').length) {
|
||||||
|
|
||||||
var stateId = $(this).val();
|
$('#state_Id').change(function () {
|
||||||
|
|
||||||
if (stateId) {
|
var stateId = $(this).val();
|
||||||
|
|
||||||
$.ajax({
|
if (stateId) {
|
||||||
url: '/get_districts/' + stateId,
|
|
||||||
type: 'GET',
|
|
||||||
|
|
||||||
success: function (data) {
|
$.ajax({
|
||||||
|
url: '/get_districts/' + stateId,
|
||||||
|
type: 'GET',
|
||||||
|
|
||||||
var districtDropdown = $('#district_Id');
|
success: function (data) {
|
||||||
|
|
||||||
districtDropdown.empty();
|
var districtDropdown = $('#district_Id');
|
||||||
districtDropdown.append('<option value="" disabled selected>Select District</option>');
|
|
||||||
|
|
||||||
data.forEach(function (district) {
|
districtDropdown.empty();
|
||||||
|
districtDropdown.append('<option value="" disabled selected>Select District</option>');
|
||||||
|
|
||||||
districtDropdown.append(
|
data.forEach(function (district) {
|
||||||
'<option value="' + district.id + '">' + district.name + '</option>'
|
districtDropdown.append(
|
||||||
);
|
'<option value="' + district.id + '">' + district.name + '</option>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
districtDropdown.prop('disabled', false);
|
||||||
|
}
|
||||||
districtDropdown.prop('disabled', false);
|
});
|
||||||
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================
|
||||||
// DISTRICT → BLOCK
|
// DISTRICT → BLOCK
|
||||||
$('#district_Id').change(function () {
|
// ===============================
|
||||||
|
if ($('#district_Id').length) {
|
||||||
|
|
||||||
var districtId = $(this).val();
|
$('#district_Id').change(function () {
|
||||||
|
|
||||||
if (districtId) {
|
var districtId = $(this).val();
|
||||||
|
|
||||||
$.ajax({
|
if (districtId) {
|
||||||
url: '/get_blocks/' + districtId,
|
|
||||||
type: 'GET',
|
|
||||||
|
|
||||||
success: function (data) {
|
$.ajax({
|
||||||
|
url: '/get_blocks/' + districtId,
|
||||||
|
type: 'GET',
|
||||||
|
|
||||||
var blockDropdown = $('#block_Id');
|
success: function (data) {
|
||||||
|
|
||||||
blockDropdown.empty();
|
var blockDropdown = $('#block_Id');
|
||||||
blockDropdown.append('<option value="" disabled selected>Select Block</option>');
|
|
||||||
|
|
||||||
data.forEach(function (block) {
|
blockDropdown.empty();
|
||||||
|
blockDropdown.append('<option value="" disabled selected>Select Block</option>');
|
||||||
|
|
||||||
blockDropdown.append(
|
data.forEach(function (block) {
|
||||||
'<option value="' + block.id + '">' + block.name + '</option>'
|
blockDropdown.append(
|
||||||
);
|
'<option value="' + block.id + '">' + block.name + '</option>'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
blockDropdown.prop('disabled', false);
|
||||||
|
}
|
||||||
blockDropdown.prop('disabled', false);
|
});
|
||||||
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================
|
||||||
// VILLAGE NAME VALIDATION
|
// VILLAGE NAME VALIDATION
|
||||||
$('#Village_Name').on('input', function () {
|
// ===============================
|
||||||
|
if ($('#Village_Name').length) {
|
||||||
|
|
||||||
var villageName = $(this).val();
|
$('#Village_Name').on('input', function () {
|
||||||
var validPattern = /^[A-Za-z ]*$/;
|
|
||||||
|
|
||||||
if (!validPattern.test(villageName)) {
|
var villageName = $(this).val();
|
||||||
|
var validPattern = /^[A-Za-z ]*$/;
|
||||||
|
|
||||||
$('#villageMessage')
|
if (!validPattern.test(villageName)) {
|
||||||
.text('Only letters and spaces are allowed!')
|
|
||||||
.css('color', 'red');
|
|
||||||
|
|
||||||
$('#submitVillage').prop('disabled', true);
|
$('#villageMessage')
|
||||||
|
.text('Only letters and spaces are allowed!')
|
||||||
|
.css('color', 'red');
|
||||||
|
|
||||||
} else {
|
$('#submitVillage').prop('disabled', true);
|
||||||
|
|
||||||
$('#villageMessage').text('');
|
} else {
|
||||||
$('#submitVillage').prop('disabled', false);
|
|
||||||
|
|
||||||
}
|
$('#villageMessage').text('');
|
||||||
|
$('#submitVillage').prop('disabled', false);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================
|
||||||
// CHECK DUPLICATE VILLAGE
|
// CHECK DUPLICATE VILLAGE
|
||||||
$('#Village_Name, #block_Id').on('change keyup', function () {
|
// ===============================
|
||||||
|
if ($('#Village_Name').length && $('#block_Id').length) {
|
||||||
|
|
||||||
var blockId = $('#block_Id').val();
|
$('#Village_Name, #block_Id').on('change keyup', function () {
|
||||||
var villageName = $('#Village_Name').val().trim();
|
|
||||||
|
|
||||||
if (blockId && villageName) {
|
var blockId = $('#block_Id').val();
|
||||||
|
var villageName = $('#Village_Name').val().trim();
|
||||||
|
|
||||||
$.ajax({
|
if (blockId && villageName) {
|
||||||
|
|
||||||
url: '/check_village',
|
$.ajax({
|
||||||
type: 'POST',
|
url: '/check_village',
|
||||||
|
type: 'POST',
|
||||||
|
|
||||||
data: {
|
data: {
|
||||||
block_Id: blockId,
|
block_Id: blockId,
|
||||||
Village_Name: villageName
|
Village_Name: villageName
|
||||||
},
|
},
|
||||||
|
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
|
|
||||||
if (response.status === 'exists') {
|
if (response.status === 'exists') {
|
||||||
|
|
||||||
|
$('#villageMessage')
|
||||||
|
.text(response.message)
|
||||||
|
.css('color', 'red');
|
||||||
|
|
||||||
|
$('#submitVillage').prop('disabled', true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$('#villageMessage')
|
||||||
|
.text(response.message)
|
||||||
|
.css('color', 'green');
|
||||||
|
|
||||||
|
$('#submitVillage').prop('disabled', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
error: function () {
|
||||||
|
|
||||||
$('#villageMessage')
|
$('#villageMessage')
|
||||||
.text(response.message)
|
.text('Error checking village name')
|
||||||
.css('color', 'red');
|
.css('color', 'red');
|
||||||
|
|
||||||
$('#submitVillage').prop('disabled', true);
|
$('#submitVillage').prop('disabled', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// ADD VILLAGE (SAFE SCOPE FIX)
|
||||||
|
// ===============================
|
||||||
|
if ($('#villageForm').length) {
|
||||||
|
|
||||||
|
$('#villageForm').submit(function (event) {
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/add_village',
|
||||||
|
type: 'POST',
|
||||||
|
data: $(this).serialize(),
|
||||||
|
|
||||||
|
success: function (response) {
|
||||||
|
|
||||||
|
if (response && response.status === 'success') {
|
||||||
|
|
||||||
|
alert(response.message || 'Village added successfully!');
|
||||||
|
|
||||||
|
// ✅ clear form
|
||||||
|
$('#villageForm')[0].reset();
|
||||||
|
|
||||||
|
// ✅ switch to table
|
||||||
|
$('#addForm').hide();
|
||||||
|
$('#addTable').show();
|
||||||
|
|
||||||
|
// optional refresh
|
||||||
|
location.reload();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
alert(response.message || 'Error adding village. Please try again.');
|
||||||
$('#villageMessage')
|
|
||||||
.text(response.message)
|
|
||||||
.css('color', 'green');
|
|
||||||
|
|
||||||
$('#submitVillage').prop('disabled', false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
error: function () {
|
error: function () {
|
||||||
|
alert('An error occurred. Please try again.');
|
||||||
$('#villageMessage')
|
|
||||||
.text('Error checking village name')
|
|
||||||
.css('color', 'red');
|
|
||||||
|
|
||||||
$('#submitVillage').prop('disabled', true);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// ADD VILLAGE
|
|
||||||
$('#villageForm').submit(function (event) {
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
|
|
||||||
url: '/add_village',
|
|
||||||
type: 'POST',
|
|
||||||
data: $(this).serialize(),
|
|
||||||
|
|
||||||
success: function (response) {
|
|
||||||
|
|
||||||
if (response.status === 'success') {
|
|
||||||
|
|
||||||
alert('Village added successfully!');
|
|
||||||
location.reload();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
alert('Error adding village. Please try again.');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
error: function () {
|
|
||||||
|
|
||||||
alert('An error occurred. Please try again.');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================
|
||||||
|
// DELETE FUNCTION (SAFE)
|
||||||
|
// ===============================
|
||||||
|
function deleteVillage(villageId, element) {
|
||||||
|
if (!confirm("Are you sure you want to delete this village?")) return;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/delete_village/' + villageId,
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (response) {
|
||||||
|
alert(response.message); // ✅ now shows "Village deleted successfully!"
|
||||||
|
if (element) $(element).closest("tr").remove();
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
alert("Error deleting village. Please try again.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Activity Logs</title>
|
<title>Activity Logs</title>
|
||||||
@@ -9,67 +10,79 @@
|
|||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
input, button {
|
|
||||||
|
input,
|
||||||
|
button {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background-color: #007bff;
|
background-color: #007bff;
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover {
|
button:hover {
|
||||||
background-color: #0056b3;
|
background-color: #0056b3;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 0 8px rgba(0,0,0,0.1);
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
th, td {
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
th {
|
||||||
background-color: #007bff;
|
background-color: #007bff;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr:nth-child(even) {
|
tr:nth-child(even) {
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h2>Activity Logs</h2>
|
<h2>Activity Logs</h2>
|
||||||
<form method="get" action="{{ url_for('activity_log') }}" class="filter-form">
|
|
||||||
|
|
||||||
<label for="username">Username:</label>
|
<form method="get" action="{{ url_for('log.activity_log') }}" class="filter-form">
|
||||||
<input type="text" id="username" name="username" placeholder="Enter username" value="{{ username or '' }}">
|
<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>
|
<label for="start_date">Start Date:</label>
|
||||||
<input type="date" id="start_date" name="start_date" value="{{ start_date or '' }}">
|
<input type="date" id="start_date" name="start_date" value="{{ start_date or '' }}">
|
||||||
|
|
||||||
<label for="end_date">End Date:</label>
|
<label for="end_date">End Date:</label>
|
||||||
<input type="date" id="end_date" name="end_date" value="{{ end_date or '' }}">
|
<input type="date" id="end_date" name="end_date" value="{{ end_date or '' }}">
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Filter</button>
|
||||||
<button type="submit" class="btn btn-primary">Filter</button>
|
<button type="button" style="background-color: #6c757d;" onclick="resetFilter()">Reset</button>
|
||||||
<!-- <button type="button" style="background-color: #6c757d;" onclick="resetFilter()">Reset</button> -->
|
</form>
|
||||||
</form>
|
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -94,9 +107,10 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function resetFilter() {
|
function resetFilter() {
|
||||||
window.location.href = "{{ url_for('activity_log') }}";
|
window.location.href = "{{ url_for('log.activity_log') }}";
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -20,12 +20,16 @@
|
|||||||
<h2>Add GST Release</h2>
|
<h2>Add GST Release</h2>
|
||||||
<form action="/add_gst_release" method="POST" onsubmit="showSuccessAlert(event)">
|
<form action="/add_gst_release" method="POST" onsubmit="showSuccessAlert(event)">
|
||||||
<div class="row1">
|
<div class="row1">
|
||||||
<div>
|
<div>
|
||||||
<label for="subcontractor">Subcontractor Name:</label>
|
<label for="subcontractor">Subcontractor Name:</label>
|
||||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
<!-- Text input for user-friendly autocomplete -->
|
||||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
|
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
||||||
<div id="subcontractor_list" class="autocomplete-items"></div>
|
|
||||||
</div>
|
<!-- Hidden input for backend; must match model's Contractor_ID -->
|
||||||
|
<input type="hidden" id="subcontractor_id" name="Contractor_ID"/>
|
||||||
|
|
||||||
|
<div id="subcontractor_list" class="autocomplete-items"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label for="PMC_No">PMC No:</label><br>
|
<label for="PMC_No">PMC No:</label><br>
|
||||||
@@ -37,19 +41,19 @@
|
|||||||
</select><br><br>
|
</select><br><br>
|
||||||
|
|
||||||
<label for="invoice_No">Invoice No:</label><br>
|
<label for="invoice_No">Invoice No:</label><br>
|
||||||
<input type="text" id="invoice_No" name="invoice_No" required><br><br>
|
<input type="text" id="invoice_No" name="Invoice_No" required><br><br>
|
||||||
|
|
||||||
<label for="basic_amount">Basic Amount:</label><br>
|
<label for="basic_amount">Basic Amount:</label><br>
|
||||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required><br><br>
|
<input type="number" step="0.01" id="basic_amount" name="Basic_Amount" placeholder="₹ - 00.00" required><br><br>
|
||||||
|
|
||||||
<label for="final_amount">Final Amount:</label><br>
|
<label for="final_amount">Final Amount:</label><br>
|
||||||
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required><br><br>
|
<input type="number" step="0.01" id="final_amount" name="Final_Amount" placeholder="₹ - 00.00" required><br><br>
|
||||||
|
|
||||||
<label for="total_amount">Total Amount:</label><br>
|
<label for="total_amount">Total Amount:</label><br>
|
||||||
<input type="number" step="0.01" id="total_amount" name="total_amount" placeholder="₹ - 00.00" required><br><br>
|
<input type="number" step="0.01" id="total_amount" name="Total_Amount" placeholder="₹ - 00.00" required><br><br>
|
||||||
|
|
||||||
<label for="utr">UTR:</label><br>
|
<label for="utr">UTR:</label><br>
|
||||||
<input type="text" id="utr" name="utr" required><br><br>
|
<input type="text" id="utr" name="UTR" required><br><br>
|
||||||
|
|
||||||
<button type="submit">Submit GST Release</button>
|
<button type="submit">Submit GST Release</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -68,96 +72,110 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- GST Release History Table -->
|
||||||
<div id="addTable" style="display: none;">
|
<div id="addTable" style="display: none;">
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<h2>GST Release History</h2>
|
<h2>GST Release History</h2>
|
||||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
<input type="text" id="searchBar" placeholder="Search..." onkeyup="searchTable()">
|
||||||
</div>
|
</div>
|
||||||
<table id="sortableTable" border="1">
|
<table id="sortableTable" border="1">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="sortable-header">GST_Release_Id</th>
|
<th class="sortable-header">GST_Release_Id</th>
|
||||||
<th class="sortable-header">PMC_No</th>
|
<th class="sortable-header">PMC_No</th>
|
||||||
<th>Invoice_No</th>
|
<th>Invoice_No</th>
|
||||||
<th>Basic_Amount</th>
|
<th>Basic_Amount</th>
|
||||||
<th>Final_Amount</th>
|
<th>Final_Amount</th>
|
||||||
<th>Total_Amount</th>
|
<th>Total_Amount</th>
|
||||||
<th>UTR</th>
|
<th>UTR</th>
|
||||||
<th>Update</th>
|
<th>Update</th>
|
||||||
<th>Delete</th>
|
<th>Delete</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for gst_rel in gst_releases %}
|
{% for gst_rel in gst_releases %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ gst_rel[0] }}</td>
|
<td>{{ gst_rel.gst_release_id }}</td>
|
||||||
<td>{{ gst_rel[1] }}</td>
|
<td>{{ gst_rel.pmc_no }}</td>
|
||||||
<td>{{ gst_rel[2] }}</td>
|
<td>{{ gst_rel.invoice_no }}</td>
|
||||||
<td>{{ gst_rel[3] }}</td>
|
<td>{{ gst_rel.basic_amount }}</td>
|
||||||
<td>{{ gst_rel[4] }}</td>
|
<td>{{ gst_rel.final_amount }}</td>
|
||||||
<td>{{ gst_rel[5] }}</td>
|
<td>{{ gst_rel.total_amount }}</td>
|
||||||
<td>{{ gst_rel[6] }}</td>
|
<td>{{ gst_rel.utr }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/edit_gst_release/{{ gst_rel[0] }}">
|
<a href="{{ url_for('gst_release_bp.edit_gst_release', gst_release_id=gst_rel.gst_release_id) }}">
|
||||||
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
|
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon">
|
||||||
class="icon">
|
</a>
|
||||||
</a>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
<a href="{{ url_for('gst_release_bp.delete_gst_release', gst_release_id=gst_rel.gst_release_id) }}"
|
||||||
<a href="/delete_gst_release/{{ gst_rel[0] }}"
|
onclick="return confirm('Are you sure you want to delete this GST Release?')">
|
||||||
onclick="return confirm('Are you sure you want to delete this GST Release?')">
|
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon">
|
||||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
|
</a>
|
||||||
class="icon">
|
</td>
|
||||||
</a>
|
</tr>
|
||||||
</td>
|
{% endfor %}
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Handle subcontractor autocomplete
|
|
||||||
document.getElementById("subcontractor").addEventListener("input", function () {
|
const subcontractorInput = document.getElementById("subcontractor");
|
||||||
|
const subcontractorIdInput = document.getElementById("subcontractor_id");
|
||||||
|
const subcontractorList = document.getElementById("subcontractor_list");
|
||||||
|
const pmcDropdown = document.getElementById("PMC_No");
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
|
||||||
|
// --------------------------
|
||||||
|
// Subcontractor autocomplete
|
||||||
|
// --------------------------
|
||||||
|
subcontractorInput.addEventListener("input", function () {
|
||||||
const query = this.value;
|
const query = this.value;
|
||||||
const list = document.getElementById("subcontractor_list");
|
|
||||||
|
|
||||||
if (query.length < 2) {
|
if (query.length < 2) {
|
||||||
list.innerHTML = '';
|
subcontractorList.innerHTML = '';
|
||||||
|
subcontractorIdInput.value = ''; // reset hidden id
|
||||||
|
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>'; // reset PMC dropdown
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
list.innerHTML = '';
|
subcontractorList.innerHTML = '';
|
||||||
data.results.forEach(item => {
|
data.results.forEach(item => {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.setAttribute("data-id", item.id);
|
div.setAttribute("data-id", item.id);
|
||||||
div.textContent = item.name;
|
div.textContent = item.name;
|
||||||
list.appendChild(div);
|
subcontractorList.appendChild(div);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle subcontractor selection
|
// --------------------------
|
||||||
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
|
// Subcontractor selection
|
||||||
|
// --------------------------
|
||||||
|
subcontractorList.addEventListener("click", function (e) {
|
||||||
const selectedId = e.target.getAttribute("data-id");
|
const selectedId = e.target.getAttribute("data-id");
|
||||||
const selectedName = e.target.textContent;
|
const selectedName = e.target.textContent;
|
||||||
|
|
||||||
if (selectedId) {
|
if (selectedId) {
|
||||||
document.getElementById("subcontractor_id").value = selectedId;
|
// Set hidden field for backend
|
||||||
document.getElementById("subcontractor").value = selectedName;
|
subcontractorIdInput.value = selectedId;
|
||||||
document.getElementById("subcontractor_list").innerHTML = "";
|
|
||||||
|
|
||||||
// Update PMC dropdown for selected subcontractor
|
// Set text input to selected name
|
||||||
|
subcontractorInput.value = selectedName;
|
||||||
|
|
||||||
|
// Clear the autocomplete list
|
||||||
|
subcontractorList.innerHTML = "";
|
||||||
|
|
||||||
|
// Fetch and populate PMC dropdown
|
||||||
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
|
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const pmcDropdown = document.getElementById("PMC_No");
|
|
||||||
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
|
pmcDropdown.innerHTML = '<option value="">Select PMC No</option>';
|
||||||
|
|
||||||
data.pmc_nos.forEach(pmc => {
|
data.pmc_nos.forEach(pmc => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = pmc;
|
option.value = pmc;
|
||||||
@@ -167,6 +185,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// --------------------------
|
||||||
|
// Form submit validation
|
||||||
|
// --------------------------
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
if (!subcontractorIdInput.value) {
|
||||||
|
e.preventDefault();
|
||||||
|
alert("Please select a subcontractor from the list.");
|
||||||
|
subcontractorInput.focus();
|
||||||
|
} else if (!pmcDropdown.value) {
|
||||||
|
e.preventDefault();
|
||||||
|
alert("Please select a PMC No.");
|
||||||
|
pmcDropdown.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,59 +1,73 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Manage Hold Types</title>
|
<title>Manage Hold Types</title>
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
<!-- <script src="{{ url_for('static', filename='js/hold_types.js') }}"></script> -->
|
<!-- <script src="{{ url_for('static', filename='js/hold_types.js') }}"></script> -->
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<button id="addButton" class="action-button">Add</button>
|
<button id="addButton" class="action-button">Add</button>
|
||||||
<button id="displayButton" class="action-button">Display</button>
|
<button id="displayButton" class="action-button">Display</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addForm">
|
<div id="addForm">
|
||||||
<h2>Add Hold Types</h2>
|
<h2>Add Hold Types</h2>
|
||||||
<form id="holdTypeForm" method="POST" action="{{ url_for('hold_types.add_hold_type') }}">
|
<form id="holdTypeForm" method="POST" action="{{ url_for('hold_types.add_hold_type') }}">
|
||||||
<label>Enter Hold Amount Type:</label>
|
<label>Enter Hold Amount Type:</label>
|
||||||
<input type="text" id="hold_type" name="hold_type" placeholder="Enter Type" required>
|
<input type="text" id="hold_type" name="hold_type" placeholder="Enter Type" required>
|
||||||
<span id="holdTypeMessage"></span>
|
<span id="holdTypeMessage"></span>
|
||||||
<button type="submit" value="Add Hold Type" id="successPopup">Add Hold Type</button>
|
<button type="submit" value="Add Hold Type" id="successPopup">Add Hold Type</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addTable" style="display: none;">
|
<div id="addTable" style="display: none;">
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<h2>Hold Type List</h2>
|
<h2>Hold Type List</h2>
|
||||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
||||||
</div>
|
</div>
|
||||||
<table id="sortableTable" border="1">
|
<table id="sortableTable" border="1">
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th class="sortable-header">
|
<th class="sortable-header">
|
||||||
Hold Type
|
Hold Type
|
||||||
<span class="sort-buttons">
|
<span class="sort-buttons">
|
||||||
<span class="sort-asc">⬆️</span>
|
<span class="sort-asc">⬆️</span>
|
||||||
<span class="sort-desc">⬇️</span>
|
<span class="sort-desc">⬇️</span>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
<th>Update</th>
|
<th>Update</th>
|
||||||
<th>Delete</th>
|
<th>Delete</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for htd in Hold_Types_data %}
|
{% for htd in Hold_Types_data %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ htd['hold_type_id'] }}</td>
|
<td>{{ htd['hold_type_id'] }}</td>
|
||||||
<td>{{ htd['hold_type'] }}</td>
|
<td>{{ htd['hold_type'] }}</td>
|
||||||
<td><a href="{{ url_for('hold_types.edit_hold_type', id=htd['hold_type_id']) }}">Edit</a></td>
|
<td style="text-align:center;">
|
||||||
<td><button class="delete-button" data-id="{{ htd['hold_type_id'] }}">Delete</button></td>
|
<a href="{{ url_for('hold_types.edit_hold_type', id=htd['hold_type_id']) }}">
|
||||||
</tr>
|
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
|
||||||
{% endfor %}
|
class="icon">
|
||||||
</table>
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
<a href="/">Back to Dashboard</a>
|
<td style="text-align:center;">
|
||||||
</div>
|
<a href="{{ url_for('hold_types.delete_hold_type', id=htd['hold_type_id']) }}"
|
||||||
|
onclick="return confirm('Are you sure you want to delete this hold type?');">
|
||||||
|
|
||||||
|
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
|
||||||
|
class="icon">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<a href="/">Back to Dashboard</a>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,176 +1,322 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %} {% block content %}
|
||||||
{% block content %}
|
|
||||||
<head xmlns="http://www.w3.org/1999/html">
|
<head xmlns="http://www.w3.org/1999/html">
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Add Invoice</title>
|
<title>Add Invoice</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/invoice.css') }}">
|
<link
|
||||||
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
|
rel="stylesheet"
|
||||||
<script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script>
|
type="text/css"
|
||||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
href="{{ url_for('static', filename='css/invoice.css') }}"
|
||||||
|
/>
|
||||||
|
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/holdAmount.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% if success == 'true' %}
|
{% if success == 'true' %}
|
||||||
<div class="alert alert-success alert-dismissible fade show mt-3" role="alert">
|
<div
|
||||||
|
class="alert alert-success alert-dismissible fade show mt-3"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
✅ Invoice added successfully!
|
✅ Invoice added successfully!
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
<button
|
||||||
</div>
|
type="button"
|
||||||
{% endif %}
|
class="btn-close"
|
||||||
|
data-bs-dismiss="alert"
|
||||||
|
aria-label="Close"
|
||||||
<!-- Flash Messages -->
|
></button>
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
</div>
|
||||||
{% if messages %}
|
|
||||||
<div class="flash-messages">
|
|
||||||
{% for category, message in messages %}
|
|
||||||
<div class="alert {{ category }}">{{ message }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<div class="button-container">
|
<!-- Flash Messages -->
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %} {% if
|
||||||
|
messages %}
|
||||||
|
<div class="flash-messages">
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="alert {{ category }}">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %} {% endwith %}
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
<button id="addButton" class="action-button">Add</button>
|
<button id="addButton" class="action-button">Add</button>
|
||||||
<button id="displayButton" class="action-button">Display</button>
|
<button id="displayButton" class="action-button">Display</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addForm" style="display: none;">
|
<div id="addForm" style="display: none">
|
||||||
<h2>Add Invoice</h2>
|
<h2>Add Invoice</h2>
|
||||||
|
|
||||||
<form id="invoiceForm" action="{{ url_for('invoice.add_invoice') }}" method="POST">
|
<form
|
||||||
<div class="row1">
|
id="invoiceForm"
|
||||||
<div>
|
action="{{ url_for('invoice.add_invoice') }}"
|
||||||
<label for="subcontractor">Subcontractor Name:</label>
|
method="POST"
|
||||||
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off"/>
|
>
|
||||||
<input type="hidden" id="subcontractor_id" name="subcontractor_id"/>
|
<div class="row1">
|
||||||
<div id="subcontractor_list"></div>
|
<div>
|
||||||
</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"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row2">
|
<div class="row2">
|
||||||
<div>
|
<div>
|
||||||
<label for="village">Village Name:</label>
|
<label for="village">Village Name:</label>
|
||||||
<select id="village" name="village" required>
|
<select id="village" name="village" required>
|
||||||
<option value="">-- Select Village --</option>
|
<option value="">-- Select Village --</option>
|
||||||
{% for village in villages %}
|
{% for village in villages %}
|
||||||
<option value="{{ village.Village_Name }}">{{ village.Village_Name }}</option>
|
<option value="{{ village.Village_Name }}">
|
||||||
{% endfor %}
|
{{ village.Village_Name }}
|
||||||
</select>
|
</option>
|
||||||
</div>
|
{% endfor %}
|
||||||
<div>
|
</select>
|
||||||
<label for="pmc_no">PMC No:</label>
|
|
||||||
<input type="text" id="pmc_no" name="pmc_no" required/>
|
|
||||||
<div id="pmc_no_list" class="autocomplete-list"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div class="row2">
|
<label for="pmc_no">PMC No:</label>
|
||||||
<div>
|
<input type="text" id="pmc_no" name="pmc_no" required />
|
||||||
<label for="work_type">Work Type:</label>
|
<div id="pmc_no_list" class="autocomplete-list"></div>
|
||||||
<input type="text" id="work_type" name="work_type" required/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="invoice_details">Invoice Details:</label>
|
|
||||||
<textarea id="invoice_details" name="invoice_details" required></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row2">
|
<div class="row2">
|
||||||
<div>
|
<div>
|
||||||
<label for="invoice_no">Invoice No:</label>
|
<label for="work_type">Work Type:</label>
|
||||||
<input type="text" id="invoice_no" name="invoice_no" required/>
|
<input type="text" id="work_type" name="work_type" required />
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="invoice_date">Invoice Date:</label>
|
|
||||||
<input type="date" id="invoice_date" name="invoice_date" required/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
<div class="row3">
|
<label for="invoice_details">Invoice Details:</label>
|
||||||
<div>
|
<textarea
|
||||||
<label for="basic_amount">Basic Amount:</label>
|
id="invoice_details"
|
||||||
<input type="number" step="0.01" id="basic_amount" name="basic_amount" placeholder="₹ - 00.00" required/>
|
name="invoice_details"
|
||||||
</div>
|
required
|
||||||
<div>
|
></textarea>
|
||||||
<label for="debit_amount">Debit Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="debit_amount" name="debit_amount" placeholder="₹ - 00.00" required/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="after_debit_amount">After Debit Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="after_debit_amount" name="after_debit_amount" placeholder="₹ - 00.00" readonly required/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row3">
|
|
||||||
<div class="percentage-field">
|
|
||||||
<label for="gst_percentage">GST %:</label>
|
|
||||||
<input type="number" step="0.01" id="gst_percentage" name="gst_percentage" placeholder="%"/>
|
|
||||||
<label for="gst_amount">GST Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="gst_amount" name="gst_amount" placeholder="₹ - 00.00" readonly required/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="amount">Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="amount" name="amount" placeholder="₹ - 00.00" readonly required/>
|
|
||||||
</div>
|
|
||||||
<div class="percentage-field">
|
|
||||||
<label for="tds_percentage">TDS %:</label>
|
|
||||||
<input type="number" step="0.01" id="tds_percentage" name="tds_percentage" placeholder="%"/>
|
|
||||||
<label for="tds_amount">TDS Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="tds_amount" name="tds_amount" placeholder="₹ - 00.00" readonly required/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row3">
|
|
||||||
<div class="percentage-field">
|
|
||||||
<label for="sd_percentage">SD %:</label>
|
|
||||||
<input type="number" step="0.01" id="sd_percentage" name="sd_percentage" placeholder="%"/>
|
|
||||||
<label for="sd_amount">SD Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="sd_amount" name="sd_amount" placeholder="₹ - 00.00" readonly required>
|
|
||||||
</div>
|
|
||||||
<div class="percentage-field">
|
|
||||||
<label for="commission_percentage">On Commission %:</label>
|
|
||||||
<input type="number" step="0.01" id="commission_percentage" name="commission_percentage" placeholder="%"/>
|
|
||||||
<label for="on_commission">On Commission:</label>
|
|
||||||
<input type="number" step="0.01" id="on_commission" name="on_commission" placeholder="₹ - 00.00" readonly required>
|
|
||||||
</div>
|
|
||||||
<div class="percentage-field">
|
|
||||||
<label for="hydro_percentage">Hydro Testing %:</label>
|
|
||||||
<input type="number" step="0.01" id="hydro_percentage" name="hydro_percentage" placeholder="%"/>
|
|
||||||
<label for="hydro_testing">Hydro Testing:</label>
|
|
||||||
<input type="number" step="0.01" id="hydro_testing" name="hydro_testing" placeholder="₹ - 00.00" readonly required>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="hold-row">
|
|
||||||
<button type="button" id="add_hold_amount" class="button">+ Add Hold Amount</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Dynamically added hold amount fields -->
|
<div class="row2">
|
||||||
<div id="hold_amount_container"></div>
|
<div>
|
||||||
|
<label for="invoice_no">Invoice No:</label>
|
||||||
<div class="row2">
|
<input type="text" id="invoice_no" name="invoice_no" required />
|
||||||
<div>
|
|
||||||
<label for="gst_sd_amount">GST SD Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="gst_sd_amount" name="gst_sd_amount" placeholder="₹ - 00.00" required/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="final_amount">Final Amount:</label>
|
|
||||||
<input type="number" step="0.01" id="final_amount" name="final_amount" placeholder="₹ - 00.00" required/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="invoice_date">Invoice Date:</label>
|
||||||
|
<input type="date" id="invoice_date" name="invoice_date" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="button">Submit</button>
|
<div class="row3">
|
||||||
|
<div>
|
||||||
|
<label for="basic_amount">Basic Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="basic_amount"
|
||||||
|
name="basic_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="debit_amount">Debit Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="debit_amount"
|
||||||
|
name="debit_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="after_debit_amount">After Debit Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="after_debit_amount"
|
||||||
|
name="after_debit_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row3">
|
||||||
|
<div class="percentage-field">
|
||||||
|
<label for="gst_percentage">GST %:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="gst_percentage"
|
||||||
|
name="gst_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
|
<label for="gst_amount">GST Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="gst_amount"
|
||||||
|
name="gst_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="amount">Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="amount"
|
||||||
|
name="amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="percentage-field">
|
||||||
|
<label for="tds_percentage">TDS %:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="tds_percentage"
|
||||||
|
name="tds_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
|
<label for="tds_amount">TDS Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="tds_amount"
|
||||||
|
name="tds_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row3">
|
||||||
|
<div class="percentage-field">
|
||||||
|
<label for="sd_percentage">SD %:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="sd_percentage"
|
||||||
|
name="sd_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
|
<label for="sd_amount">SD Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="sd_amount"
|
||||||
|
name="sd_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="percentage-field">
|
||||||
|
<label for="commission_percentage">On Commission %:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="commission_percentage"
|
||||||
|
name="commission_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
|
<label for="on_commission">On Commission:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="on_commission"
|
||||||
|
name="on_commission"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="percentage-field">
|
||||||
|
<label for="hydro_percentage">Hydro Testing %:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="hydro_percentage"
|
||||||
|
name="hydro_percentage"
|
||||||
|
placeholder="%"
|
||||||
|
/>
|
||||||
|
<label for="hydro_testing">Hydro Testing:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="hydro_testing"
|
||||||
|
name="hydro_testing"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
readonly
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hold-row">
|
||||||
|
<button type="button" id="add_hold_amount" class="button">
|
||||||
|
+ Add Hold Amount
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dynamically added hold amount fields -->
|
||||||
|
<div id="hold_amount_container"></div>
|
||||||
|
|
||||||
|
<div class="row2">
|
||||||
|
<div>
|
||||||
|
<label for="gst_sd_amount">GST SD Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="gst_sd_amount"
|
||||||
|
name="gst_sd_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="final_amount">Final Amount:</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
id="final_amount"
|
||||||
|
name="final_amount"
|
||||||
|
placeholder="₹ - 00.00"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="button">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addTable" style="display: none;">
|
<div id="addTable" style="display: none">
|
||||||
<!-- Invoice Table Section -->
|
<!-- Invoice Table Section -->
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<h2>Invoice List</h2>
|
<h2>Invoice List</h2>
|
||||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
<input
|
||||||
{% if invoices %}
|
type="text"
|
||||||
<table class="invoice-table">
|
id="searchBar"
|
||||||
|
placeholder="Searching..."
|
||||||
|
onkeyup="searchTable()"
|
||||||
|
/>
|
||||||
|
{% if invoices %}
|
||||||
|
<table class="invoice-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Invoice Id</th>
|
<th>Invoice Id</th>
|
||||||
<th>SubContractor Name</th>
|
<th>SubContractor Name</th>
|
||||||
<th>PMC No</th>
|
<th>PMC No</th>
|
||||||
@@ -192,11 +338,11 @@
|
|||||||
<th>Final Amount</th>
|
<th>Final Amount</th>
|
||||||
<th>Update</th>
|
<th>Update</th>
|
||||||
<th>Delete</th>
|
<th>Delete</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for invoice in invoices %}
|
{% for invoice in invoices %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ invoice.Invoice_Id }}</td>
|
<td>{{ invoice.Invoice_Id }}</td>
|
||||||
<td>{{ invoice.Contractor_Name }}</td>
|
<td>{{ invoice.Contractor_Name }}</td>
|
||||||
<td>{{ invoice.PMC_No }}</td>
|
<td>{{ invoice.PMC_No }}</td>
|
||||||
@@ -217,62 +363,77 @@
|
|||||||
<td>{{ invoice.GST_SD_Amount }}</td>
|
<td>{{ invoice.GST_SD_Amount }}</td>
|
||||||
<td>{{ invoice.Final_Amount }}</td>
|
<td>{{ invoice.Final_Amount }}</td>
|
||||||
<td>
|
<td>
|
||||||
<!-- Edit -->
|
<!-- Edit -->
|
||||||
<a href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}">
|
<a
|
||||||
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit" class="icon">
|
href="{{ url_for('invoice.edit_invoice', invoice_id=invoice.Invoice_Id) }}"
|
||||||
</a>
|
>
|
||||||
|
<img
|
||||||
|
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
|
||||||
|
alt="Edit"
|
||||||
|
class="icon edit-btn"
|
||||||
|
data-id="{{ invoice.Invoice_Id }}"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<!-- Delete -->
|
<a
|
||||||
<a href="{{ url_for('invoice.delete_invoice_route', invoice_id=invoice.Invoice_Id) }}" onclick="return confirm('Are you sure?')">
|
href="javascript:void(0);"
|
||||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete" class="icon">
|
onclick="deleteInvoice({{ invoice.Invoice_Id }}, this)"
|
||||||
</a>
|
>
|
||||||
|
<img
|
||||||
|
src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
|
||||||
|
alt="Delete"
|
||||||
|
class="icon"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>No invoices found.</p>
|
<p>No invoices found.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Get all the input fields
|
// Get all the input fields
|
||||||
const basicAmount = document.getElementById('basic_amount');
|
const basicAmount = document.getElementById("basic_amount");
|
||||||
const debitAmount = document.getElementById('debit_amount');
|
const debitAmount = document.getElementById("debit_amount");
|
||||||
const afterDebitAmount = document.getElementById('after_debit_amount');
|
const afterDebitAmount = document.getElementById("after_debit_amount");
|
||||||
const amount = document.getElementById('amount');
|
const amount = document.getElementById("amount");
|
||||||
|
|
||||||
// Percentage fields
|
// Percentage fields
|
||||||
const gstPercentage = document.getElementById('gst_percentage');
|
const gstPercentage = document.getElementById("gst_percentage");
|
||||||
const gstAmount = document.getElementById('gst_amount');
|
const gstAmount = document.getElementById("gst_amount");
|
||||||
const tdsPercentage = document.getElementById('tds_percentage');
|
const tdsPercentage = document.getElementById("tds_percentage");
|
||||||
const tdsAmount = document.getElementById('tds_amount');
|
const tdsAmount = document.getElementById("tds_amount");
|
||||||
const sdPercentage = document.getElementById('sd_percentage');
|
const sdPercentage = document.getElementById("sd_percentage");
|
||||||
const sdAmountInput = document.getElementById('sd_amount');
|
const sdAmountInput = document.getElementById("sd_amount");
|
||||||
const commissionPercentage = document.getElementById('commission_percentage');
|
const commissionPercentage = document.getElementById(
|
||||||
const onCommission = document.getElementById('on_commission');
|
"commission_percentage",
|
||||||
const hydroPercentage = document.getElementById('hydro_percentage');
|
);
|
||||||
const hydroTesting = document.getElementById('hydro_testing');
|
const onCommission = document.getElementById("on_commission");
|
||||||
const gstSdAmount = document.getElementById('gst_sd_amount');
|
const hydroPercentage = document.getElementById("hydro_percentage");
|
||||||
const finalAmount = document.getElementById('final_amount');
|
const hydroTesting = document.getElementById("hydro_testing");
|
||||||
|
const gstSdAmount = document.getElementById("gst_sd_amount");
|
||||||
|
const finalAmount = document.getElementById("final_amount");
|
||||||
|
|
||||||
// Calculate after debit amount when basic or debit amount changes
|
// Calculate after debit amount when basic or debit amount changes
|
||||||
function calculateAfterDebitAmount() {
|
function calculateAfterDebitAmount() {
|
||||||
const basic = parseFloat(basicAmount.value) || 0;
|
const basic = parseFloat(basicAmount.value) || 0;
|
||||||
const debit = parseFloat(debitAmount.value) || 0;
|
const debit = parseFloat(debitAmount.value) || 0;
|
||||||
const afterDebit = basic - debit;
|
const afterDebit = basic - debit;
|
||||||
afterDebitAmount.value = afterDebit.toFixed(2);
|
afterDebitAmount.value = afterDebit.toFixed(2);
|
||||||
calculateGST();
|
calculateGST();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate GST and Amount
|
// Calculate GST and Amount
|
||||||
function calculateGST() {
|
function calculateGST() {
|
||||||
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
||||||
|
|
||||||
if (gstPercentage.value) {
|
if (gstPercentage.value) {
|
||||||
const gstPerc = parseFloat(gstPercentage.value) || 0;
|
const gstPerc = parseFloat(gstPercentage.value) || 0;
|
||||||
const gstAmt = (baseAmount * gstPerc) / 100;
|
const gstAmt = (baseAmount * gstPerc) / 100;
|
||||||
gstAmount.value = gstAmt.toFixed(2);
|
gstAmount.value = gstAmt.toFixed(2);
|
||||||
@@ -280,91 +441,96 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
// Calculate Amount (After Debit + GST)
|
// Calculate Amount (After Debit + GST)
|
||||||
amount.value = (baseAmount + gstAmt).toFixed(2);
|
amount.value = (baseAmount + gstAmt).toFixed(2);
|
||||||
} else {
|
} else {
|
||||||
amount.value = baseAmount.toFixed(2);
|
amount.value = baseAmount.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateOtherDeductions();
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateOtherDeductions();
|
// Calculate other deductions (TDS, SD, Commission, Hydro)
|
||||||
}
|
function calculateOtherDeductions() {
|
||||||
|
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
||||||
|
|
||||||
// Calculate other deductions (TDS, SD, Commission, Hydro)
|
// Calculate TDS
|
||||||
function calculateOtherDeductions() {
|
if (tdsPercentage.value) {
|
||||||
const baseAmount = parseFloat(afterDebitAmount.value) || 0;
|
|
||||||
|
|
||||||
// Calculate TDS
|
|
||||||
if (tdsPercentage.value) {
|
|
||||||
const tdsPerc = parseFloat(tdsPercentage.value) || 0;
|
const tdsPerc = parseFloat(tdsPercentage.value) || 0;
|
||||||
const tdsAmt = (baseAmount * tdsPerc) / 100;
|
const tdsAmt = (baseAmount * tdsPerc) / 100;
|
||||||
tdsAmount.value = tdsAmt.toFixed(2);
|
tdsAmount.value = tdsAmt.toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate SD
|
// Calculate SD
|
||||||
if (sdPercentage.value) {
|
if (sdPercentage.value) {
|
||||||
const sdPerc = parseFloat(sdPercentage.value) || 0;
|
const sdPerc = parseFloat(sdPercentage.value) || 0;
|
||||||
const sdAmt = (baseAmount * sdPerc) / 100;
|
const sdAmt = (baseAmount * sdPerc) / 100;
|
||||||
sdAmountInput.value = sdAmt.toFixed(2);
|
sdAmountInput.value = sdAmt.toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate Commission
|
// Calculate Commission
|
||||||
if (commissionPercentage.value) {
|
if (commissionPercentage.value) {
|
||||||
const commPerc = parseFloat(commissionPercentage.value) || 0;
|
const commPerc = parseFloat(commissionPercentage.value) || 0;
|
||||||
const commAmt = (baseAmount * commPerc) / 100;
|
const commAmt = (baseAmount * commPerc) / 100;
|
||||||
onCommission.value = commAmt.toFixed(2);
|
onCommission.value = commAmt.toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate Hydro Testing
|
// Calculate Hydro Testing
|
||||||
if (hydroPercentage.value) {
|
if (hydroPercentage.value) {
|
||||||
const hydroPerc = parseFloat(hydroPercentage.value) || 0;
|
const hydroPerc = parseFloat(hydroPercentage.value) || 0;
|
||||||
const hydroAmt = (baseAmount * hydroPerc) / 100;
|
const hydroAmt = (baseAmount * hydroPerc) / 100;
|
||||||
hydroTesting.value = hydroAmt.toFixed(2);
|
hydroTesting.value = hydroAmt.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateFinalAmount();
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateFinalAmount();
|
// Calculate final amount
|
||||||
}
|
function calculateFinalAmount() {
|
||||||
|
const amt = parseFloat(amount.value) || 0;
|
||||||
|
const tds = parseFloat(tdsAmount.value) || 0;
|
||||||
|
const sd = parseFloat(sdAmountInput.value) || 0;
|
||||||
|
const commission = parseFloat(onCommission.value) || 0;
|
||||||
|
const hydro = parseFloat(hydroTesting.value) || 0;
|
||||||
|
const gstSd = parseFloat(gstSdAmount.value) || 0;
|
||||||
|
|
||||||
// Calculate final amount
|
// Get hold amounts
|
||||||
function calculateFinalAmount() {
|
let totalHold = 0;
|
||||||
const amt = parseFloat(amount.value) || 0;
|
document
|
||||||
const tds = parseFloat(tdsAmount.value) || 0;
|
.querySelectorAll('input[name="hold_amount[]"]')
|
||||||
const sd = parseFloat(sdAmountInput.value) || 0;
|
.forEach((input) => {
|
||||||
const commission = parseFloat(onCommission.value) || 0;
|
totalHold += parseFloat(input.value) || 0;
|
||||||
const hydro = parseFloat(hydroTesting.value) || 0;
|
});
|
||||||
const gstSd = parseFloat(gstSdAmount.value) || 0;
|
|
||||||
|
|
||||||
// Get hold amounts
|
// Final Amount = Amount - TDS - SD - Commission - Hydro - GST SD - Hold Amounts
|
||||||
let totalHold = 0;
|
const final = amt - tds - sd - commission - hydro - gstSd - totalHold;
|
||||||
document.querySelectorAll('input[name="hold_amount[]"]').forEach(input => {
|
finalAmount.value = final.toFixed(2);
|
||||||
totalHold += parseFloat(input.value) || 0;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Final Amount = Amount - TDS - SD - Commission - Hydro - GST SD - Hold Amounts
|
// Add event listeners
|
||||||
const final = amt - tds - sd - commission - hydro - gstSd - totalHold;
|
basicAmount.addEventListener("input", calculateAfterDebitAmount);
|
||||||
finalAmount.value = final.toFixed(2);
|
debitAmount.addEventListener("input", calculateAfterDebitAmount);
|
||||||
}
|
|
||||||
|
|
||||||
// Add event listeners
|
// Percentage fields
|
||||||
basicAmount.addEventListener('input', calculateAfterDebitAmount);
|
gstPercentage.addEventListener("input", calculateGST);
|
||||||
debitAmount.addEventListener('input', calculateAfterDebitAmount);
|
tdsPercentage.addEventListener("input", calculateOtherDeductions);
|
||||||
|
sdPercentage.addEventListener("input", calculateOtherDeductions);
|
||||||
|
commissionPercentage.addEventListener(
|
||||||
|
"input",
|
||||||
|
calculateOtherDeductions,
|
||||||
|
);
|
||||||
|
hydroPercentage.addEventListener("input", calculateOtherDeductions);
|
||||||
|
|
||||||
// Percentage fields
|
// Listen for changes in hold amounts
|
||||||
gstPercentage.addEventListener('input', calculateGST);
|
document.addEventListener("holdAmountChanged", calculateFinalAmount);
|
||||||
tdsPercentage.addEventListener('input', calculateOtherDeductions);
|
});
|
||||||
sdPercentage.addEventListener('input', calculateOtherDeductions);
|
|
||||||
commissionPercentage.addEventListener('input', calculateOtherDeductions);
|
|
||||||
hydroPercentage.addEventListener('input', calculateOtherDeductions);
|
|
||||||
|
|
||||||
// Listen for changes in hold amounts
|
// Optional JS for auto-hiding flash
|
||||||
document.addEventListener('holdAmountChanged', calculateFinalAmount);
|
setTimeout(() => {
|
||||||
});
|
document
|
||||||
|
.querySelectorAll(".alert")
|
||||||
// Optional JS for auto-hiding flash
|
.forEach((el) => (el.style.display = "none"));
|
||||||
setTimeout(() => {
|
}, 5000);
|
||||||
document.querySelectorAll('.alert').forEach(el => el.style.display = 'none');
|
</script>
|
||||||
}, 5000);
|
</div>
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
@@ -8,91 +9,104 @@
|
|||||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/invoice.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<button id="addButton" class="action-button">Add</button>
|
<button id="addButton" class="action-button">Add</button>
|
||||||
<button id="displayButton" class="action-button">Display</button>
|
<button id="displayButton" class="action-button">Display</button>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div 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>
|
</div>
|
||||||
<table id="sortableTable" border="1">
|
|
||||||
<thead>
|
<div id="addForm" style="display: none;">
|
||||||
<tr>
|
<h2>Add Payment</h2>
|
||||||
<th class="sortable-header">Payment ID</th>
|
|
||||||
<th class="sortable-header">PMC No</th>
|
<form action="/add_payment" method="POST" onsubmit="showSuccessAlert(event)">
|
||||||
<th>Invoice No</th>
|
<div class="row1">
|
||||||
<th>Payment Amount</th>
|
<div>
|
||||||
<th>TDS Amount</th>
|
<label for="subcontractor">Subcontractor Name:</label>
|
||||||
<th>Total Amount</th>
|
<input type="text" id="subcontractor" name="subcontractor" required autocomplete="off" />
|
||||||
<th>UTR</th>
|
<input type="hidden" id="subcontractor_id" name="subcontractor_id" />
|
||||||
<th>Update</th>
|
<div id="subcontractor_list" class="autocomplete-items"></div>
|
||||||
<th>Delete</th>
|
</div>
|
||||||
</tr>
|
</div>
|
||||||
</thead>
|
|
||||||
<tbody>
|
<label for="PMC_No">PMC No:</label><br>
|
||||||
{% for payment in payments %}
|
<select id="PMC_No" name="PMC_No" required>
|
||||||
<tr>
|
<option value="">Select PMC No</option>
|
||||||
<td>{{ payment[0] }}</td>
|
</select><br><br>
|
||||||
<td>{{ payment[1] }}</td>
|
|
||||||
<td>{{ payment[2] }}</td>
|
<label for="invoice_No">Invoice No:</label><br>
|
||||||
<td>{{ payment[3] }}</td>
|
<input type="number" step="0.01" id="invoice_No" name="invoice_No"><br><br>
|
||||||
<td>{{ payment[4] }}</td>
|
|
||||||
<td>{{ payment[5] }}</td>
|
<label for="Payment_Amount">Amount:</label><br>
|
||||||
<td>{{ payment[6] }}</td>
|
<input type="number" step="0.01" id="Payment_Amount" name="Payment_Amount" required
|
||||||
<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>
|
oninput="calculateTDSAndTotal()"><br><br>
|
||||||
<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>
|
<label for="TDS_Percentage">TDS Percentage (%):</label><br>
|
||||||
<!-- <tr>
|
<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['Payment_Id'] }}</td>
|
||||||
<td>{{ payment['PMC_No'] }}</td>
|
<td>{{ payment['PMC_No'] }}</td>
|
||||||
<td>{{ payment['invoice_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="/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>
|
<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> -->
|
</tr> -->
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById("subcontractor").addEventListener("input", function () {
|
document.getElementById("subcontractor").addEventListener("input", function () {
|
||||||
const query = this.value;
|
const query = this.value;
|
||||||
const list = document.getElementById("subcontractor_list");
|
const list = document.getElementById("subcontractor_list");
|
||||||
|
|
||||||
if (query.length < 2) {
|
if (query.length < 2) {
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
fetch(`/search_subcontractor?query=${encodeURIComponent(query)}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
data.results.forEach(item => {
|
data.results.forEach(item => {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.setAttribute("data-id", item.id);
|
div.setAttribute("data-id", item.id);
|
||||||
div.textContent = item.name;
|
div.textContent = item.name;
|
||||||
list.appendChild(div);
|
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);
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (data.pmc_nos.length === 0) {
|
document.getElementById("subcontractor_list").addEventListener("click", function (e) {
|
||||||
alert("No PMC Nos found for this subcontractor.");
|
const selectedId = e.target.getAttribute("data-id");
|
||||||
}
|
const selectedName = e.target.textContent;
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error("Error fetching PMC Nos:", error);
|
|
||||||
alert("Failed to fetch PMC numbers.");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
if (selectedId) {
|
||||||
function calculateTDSAndTotal() {
|
document.getElementById("subcontractor_id").value = selectedId;
|
||||||
const amount = parseFloat(document.getElementById("Payment_Amount").value) || 0;
|
document.getElementById("subcontractor").value = selectedName;
|
||||||
const tdsPercent = parseFloat(document.getElementById("TDS_Percentage").value) || 0;
|
document.getElementById("subcontractor_list").innerHTML = ""; // hide the list
|
||||||
|
|
||||||
const tdsAmount = (amount * tdsPercent / 100).toFixed(2);
|
console.log("Contractor id is", selectedId);
|
||||||
const totalAmount = (amount - tdsAmount).toFixed(2);
|
|
||||||
|
|
||||||
document.getElementById("TDS_Payment_Amount").value = tdsAmount;
|
// Fetch PMC numbers
|
||||||
document.getElementById("total_amount").value = totalAmount;
|
fetch(`/get_pmc_nos_by_subcontractor/${encodeURIComponent(selectedId)}`)
|
||||||
}
|
.then(response => response.json())
|
||||||
</script>
|
.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>
|
</body>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,99 +1,129 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %} {% block content %}
|
||||||
{% block content %}
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Village Management</title>
|
<title>Village Management</title>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
|
<link
|
||||||
<script src="{{ url_for('static', filename='js/village.js') }}"></script>
|
rel="stylesheet"
|
||||||
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
type="text/css"
|
||||||
|
href="{{ url_for('static', filename='css/style.css') }}"
|
||||||
|
/>
|
||||||
|
<script src="{{ url_for('static', filename='js/village.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/search_on_table.js') }}"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Button Container to Center Buttons -->
|
||||||
<!-- Button Container to Center Buttons -->
|
<div class="button-container">
|
||||||
<div class="button-container">
|
|
||||||
<button id="addButton" class="action-button">Add</button>
|
<button id="addButton" class="action-button">Add</button>
|
||||||
<button id="displayButton" class="action-button">Display</button>
|
<button id="displayButton" class="action-button">Display</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addForm" style="display: none;">
|
<div id="addForm" style="display: none">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="form-block">
|
<div class="form-block">
|
||||||
<h2>Add a New Village</h2>
|
<h2>Add a New Village</h2>
|
||||||
<form id="villageForm" method="POST">
|
<form id="villageForm" method="POST">
|
||||||
<label for="state_Id">State:</label>
|
<label for="state_Id">State:</label>
|
||||||
<select id="state_Id" name="state_Id" required>
|
<select id="state_Id" name="state_Id" required>
|
||||||
<option value="" disabled selected>Select State</option>
|
<option value="" disabled selected>Select State</option>
|
||||||
{% for state in states %}
|
{% for state in states %}
|
||||||
<option value="{{ state[0] }}">{{ state[1] }}</option>
|
<option value="{{ state[0] }}">{{ state[1] }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="district_Id">District:</label>
|
<label for="district_Id">District:</label>
|
||||||
<select id="district_Id" name="district_Id" required disabled>
|
<select id="district_Id" name="district_Id" required disabled>
|
||||||
<option value="" disabled selected>Select District</option>
|
<option value="" disabled selected>Select District</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="block_Id">Block:</label>
|
<label for="block_Id">Block:</label>
|
||||||
<select id="block_Id" name="block_Id" required disabled>
|
<select id="block_Id" name="block_Id" required disabled>
|
||||||
<option value="" disabled selected>Select Block</option>
|
<option value="" disabled selected>Select Block</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="Village_Name">Village Name:</label>
|
<label for="Village_Name">Village Name:</label>
|
||||||
<input type="text" id="Village_Name" name="Village_Name" placeholder="Enter Village Name" required>
|
<input
|
||||||
<span id="villageMessage"></span>
|
type="text"
|
||||||
|
id="Village_Name"
|
||||||
|
name="Village_Name"
|
||||||
|
placeholder="Enter Village Name"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<span id="villageMessage"></span>
|
||||||
|
|
||||||
<button type="submit" id="submitVillage" disabled>Add Village</button>
|
<button type="submit" id="submitVillage" disabled>Add Village</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="addTable" style="display: none;">
|
<div id="addTable" style="display: none">
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<h2>Display Villages</h2>
|
<h2>Display Villages</h2>
|
||||||
<input type="text" id="searchBar" placeholder="Searching..." onkeyup="searchTable()">
|
<input
|
||||||
|
type="text"
|
||||||
|
id="searchBar"
|
||||||
|
placeholder="Searching..."
|
||||||
|
onkeyup="searchTable()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table id="sortableTable" border="1">
|
<table id="sortableTable" border="1">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Village Sr No</th>
|
<th>Village Sr No</th>
|
||||||
<th class="sortable-header">
|
<th class="sortable-header">
|
||||||
Village Name
|
Village Name
|
||||||
<span class="sort-buttons">
|
<span class="sort-buttons">
|
||||||
<span class="sort-asc">⬆️</span>
|
<span class="sort-asc">⬆️</span>
|
||||||
<span class="sort-desc">⬇️</span>
|
<span class="sort-desc">⬇️</span>
|
||||||
</span></th>
|
</span>
|
||||||
<th class="sortable-header">Block Name
|
</th>
|
||||||
<span class="sort-buttons">
|
<th class="sortable-header">
|
||||||
<span class="sort-asc">⬆️</span>
|
Block Name
|
||||||
<span class="sort-desc">⬇️</span>
|
<span class="sort-buttons">
|
||||||
</span></th>
|
<span class="sort-asc">⬆️</span>
|
||||||
<th>Update</th>
|
<span class="sort-desc">⬇️</span>
|
||||||
<th>Delete</th>
|
</span>
|
||||||
</tr>
|
</th>
|
||||||
{% for village in villages %}
|
<th>Update</th>
|
||||||
<tr>
|
<th>Delete</th>
|
||||||
<td>{{ village[0] }}</td>
|
</tr>
|
||||||
<td>{{ village[1] }}</td>
|
{% for village in villages %}
|
||||||
<td>{{ village[2] }}</td>
|
<tr>
|
||||||
<td>
|
<td>{{ village[0] }}</td>
|
||||||
<a href="{{ url_for('village.edit_village', village_id=village[0]) }}">
|
<td>{{ village[1] }}</td>
|
||||||
<img src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}" alt="Edit"
|
<td>{{ village[2] }}</td>
|
||||||
class="icon">
|
<td>
|
||||||
</a>
|
<a
|
||||||
</td>
|
href="{{ url_for('village.edit_village', village_id=village[0]) }}"
|
||||||
<td>
|
>
|
||||||
<a href="{{ url_for('village.delete_village', village_id=village[0]) }}"
|
<img
|
||||||
onclick="return confirm('Are you sure you want to delete this village?');">
|
src="{{ url_for('static', filename='images/icons/pen_blue_icon.png') }}"
|
||||||
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}" alt="Delete"
|
alt="Edit"
|
||||||
class="icon">
|
class="icon"
|
||||||
</a>
|
/>
|
||||||
</td>
|
</a>
|
||||||
</tr>
|
</td>
|
||||||
{% endfor %}
|
<td>
|
||||||
|
<a href="javascript:void(0);"
|
||||||
|
onclick="deleteVillage({{ village[0] }}, this)">
|
||||||
|
<img src="{{ url_for('static', filename='images/icons/bin_red_icon.png') }}"
|
||||||
|
class="icon">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Flash Alerts -->
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
<script>
|
||||||
|
{% for category, message in messages %}
|
||||||
|
alert("{{ message }}");
|
||||||
|
{% endfor %}
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
</body>
|
</body>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user