diff --git a/MAIN2.PY b/MAIN2.PY new file mode 100644 index 0000000..394fb24 --- /dev/null +++ b/MAIN2.PY @@ -0,0 +1,26 @@ +from flask import Blueprint, request, jsonify, send_file, current_app, render_template +from datetime import datetime +import ast +import os + +from db import DB # Your existing DB wrapper + + +# ============================================================= +# Base Service (common DB operations) +# ============================================================= +class BaseService: + def __init__(self): + self.db = DB() + + def fetch_all(self, proc, params=None): + return self.db.fetch_all_proc(proc, params) + + def fetch_one(self, proc, params=None): + return self.db.fetch_one_proc(proc, params) + + def execute_proc(self, proc, params=None): + return self.db.exec_proc(proc, params) + + def get_conn(self): + return self.db.get_connection() diff --git a/activity.log b/activity.log index d657d1e..2ee7a1d 100644 --- a/activity.log +++ b/activity.log @@ -4506,3 +4506,512 @@ Timestamp: 2025-10-04 17:27:24 | User: Unknown | Action: Search contractor | Det Timestamp: 2025-10-04 17:27:24 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' Timestamp: 2025-10-04 17:27:24 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aad' Timestamp: 2025-10-04 17:27:25 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aad' +Timestamp: 2025-10-31 11:24:34 | User: Unknown | Action: Login | Details: User admin logged in (static user) +Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aj' +Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aja' +Timestamp: 2025-10-31 11:25:19 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ajay' +Timestamp: 2025-10-31 11:25:21 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ajay' +Timestamp: 2025-10-31 11:26:31 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 11:26:52 | User: Unknown | Action: Check State | Details: User admin Checked state 'U' +Timestamp: 2025-10-31 11:26:54 | User: Unknown | Action: Check State | Details: User admin Checked state 'Uttar Pradesh' +Timestamp: 2025-10-31 11:26:55 | User: Unknown | Action: Add State | Details: User admin added state 'Uttar Pradesh' +Timestamp: 2025-10-31 11:27:04 | User: Unknown | Action: Check District | Details: User admin Checked District 'Shamli' +Timestamp: 2025-10-31 11:27:04 | User: Unknown | Action: Check District | Details: User admin Checked District 'Shamli' +Timestamp: 2025-10-31 11:27:05 | User: Unknown | Action: Add District | Details: User admin Added District 'Shamli' +Timestamp: 2025-10-31 11:27:12 | User: Unknown | Action: Get District | Details: User admin Get District '5' +Timestamp: 2025-10-31 11:27:15 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Shamli' +Timestamp: 2025-10-31 11:27:15 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Shamli' +Timestamp: 2025-10-31 11:27:16 | User: Unknown | Action: Add Block | Details: User admin Added block 'Shamli' +Timestamp: 2025-10-31 11:27:37 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 11:27:41 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:33 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:38 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:00:54 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 12:00:56 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 12:05:17 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-31 12:05:17 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aa' +Timestamp: 2025-10-31 12:05:17 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aar' +Timestamp: 2025-10-31 12:05:21 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aarh' +Timestamp: 2025-10-31 12:05:23 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aarh' +Timestamp: 2025-10-31 13:35:19 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:35:23 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:37:19 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 13:37:25 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:52:13 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 14:52:35 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 14:52:46 | User: Unknown | Action: Check State | Details: User admin Checked state 'm' +Timestamp: 2025-10-31 14:52:48 | User: Unknown | Action: Check State | Details: User admin Checked state 'Madhya Pradesh' +Timestamp: 2025-10-31 14:52:50 | User: Unknown | Action: Add State | Details: User admin added state 'Madhya Pradesh' +Timestamp: 2025-10-31 14:52:58 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' +Timestamp: 2025-10-31 14:52:58 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' +Timestamp: 2025-10-31 14:52:58 | User: Unknown | Action: Add District | Details: User admin Added District 'Chhindawara' +Timestamp: 2025-10-31 14:53:06 | User: Unknown | Action: Get District | Details: User admin Get District '7' +Timestamp: 2025-10-31 14:53:09 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' +Timestamp: 2025-10-31 14:53:09 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' +Timestamp: 2025-10-31 14:53:09 | User: Unknown | Action: Add Block | Details: User admin Added block 'Chhindawara' +Timestamp: 2025-10-31 14:53:25 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 14:53:38 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' +Timestamp: 2025-10-31 14:53:38 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chhindawara' +Timestamp: 2025-10-31 14:53:43 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chindawara' +Timestamp: 2025-10-31 14:53:44 | User: Unknown | Action: Check District | Details: User admin Checked District 'Chindawara' +Timestamp: 2025-10-31 14:53:45 | User: Unknown | Action: Add District | Details: User admin Added District 'Chindawara' +Timestamp: 2025-10-31 14:53:51 | User: Unknown | Action: Get District | Details: User admin Get District '7' +Timestamp: 2025-10-31 14:53:54 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' +Timestamp: 2025-10-31 14:53:54 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chhindawara' +Timestamp: 2025-10-31 14:53:56 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chindawara' +Timestamp: 2025-10-31 14:53:56 | User: Unknown | Action: Check Block | Details: User admin Checked block 'Chindawara' +Timestamp: 2025-10-31 14:53:57 | User: Unknown | Action: Add Block | Details: User admin Added block 'Chindawara' +Timestamp: 2025-10-31 14:54:07 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:54:11 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:54:18 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'b' +Timestamp: 2025-10-31 14:54:18 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'bg' +Timestamp: 2025-10-31 14:54:20 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'bg' +Timestamp: 2025-10-31 14:57:08 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 14:57:25 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:35 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:36 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:37 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 14:57:52 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'j' +Timestamp: 2025-10-31 14:57:52 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ja' +Timestamp: 2025-10-31 14:57:54 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'ja' +Timestamp: 2025-10-31 16:17:29 | User: Unknown | Action: Upload Excel File | Details: User adminUpload Excel File'' +Timestamp: 2025-10-31 16:17:32 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 16:17:32 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 16:17:32 | User: Unknown | Action: Data saved | Details: User admin Data saved'None' +Timestamp: 2025-10-31 16:17:41 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'a' +Timestamp: 2025-10-31 16:17:41 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aj' +Timestamp: 2025-10-31 16:17:44 | User: Unknown | Action: Search contractor | Details: User admin Search contractor'aj' +Timestamp: 2025-11-03 11:03:32 | User: admin | Action: Logout | Details: User admin logged out +Timestamp: 2025-11-03 11:03:39 | User: admin | Action: Login | Details: Static admin logged in: admin diff --git a/config.py b/config.py index 969c7a8..73e490f 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ import os # Get MySQL credentials from environment variables MYSQL_HOST = os.getenv("MYSQL_HOST", "127.0.0.1") MYSQL_USER = os.getenv("MYSQL_USER", "root") -MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "admin") +MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "tiger") MYSQL_DB = os.getenv("MYSQL_DB", "test") # Connect to MySQL diff --git a/main.py b/main.py index cf8415c..19be04e 100644 --- a/main.py +++ b/main.py @@ -20,17 +20,10 @@ from flask import Flask, request, render_template from ldap3 import Server, Connection, ALL, SUBTREE -# audit_logger = logging.getLogger("audit") -# audit_logger.setLevel(logging.INFO) -# file_handler = RotatingFileHandler("/tmp/audit.log", maxBytes=1000000, backupCount=5) -# formatter = logging.Formatter('%(asctime)s | %(message)s') -# file_handler.setFormatter(formatter) - -# audit_logger.addHandler(file_handler) from flask import request, session - +0 def log_action(action, details=""): """Log user actions with timestamp, user, action, and details.""" log_file = os.path.join(current_app.root_path, 'activity.log') @@ -668,12 +661,7 @@ def add_block(): # Fetch all blocks to display try: - # cursor.execute( - # """SELECT b.Block_Id, b.Block_Name, d.District_Name - # FROM blocks b - # JOIN districts d ON b.District_id = d.District_id""" - # ) - # block_data = cursor.fetchall() + cursor.callproc("GetAllBlocks") for blocks in cursor.stored_results(): block_data = blocks.fetchall() @@ -699,8 +687,7 @@ def check_block(): if not re.match(str_pattern_reg, block_name): return json_response(ResponseHandler.invalid_name("block"), 400) - # cursor.execute("SELECT * FROM blocks WHERE Block_Name = %s AND District_id = %s", (block_name, district_id)) - # existing_block = cursor.fetchone() + cursor.callproc("GetBlockByNameAndDistrict", (block_name, district_id)) for rs in cursor.stored_results(): existing_block = rs.fetchone() @@ -711,60 +698,7 @@ def check_block(): return json_response(ResponseHandler.is_available("block"), 200) -# update block by id -# @app.route('/edit_block/', methods=['GET', 'POST']) -# def edit_block(block_id): -# connection = config.get_db_connection() -# block_data, states, districts = [], [], [] -# -# if connection: -# cursor = connection.cursor() -# try: -# # cursor.execute("SELECT State_ID, State_Name FROM states") -# # states = cursor.fetchall() -# cursor.callproc("GetAllStates") -# for res in cursor.stored_results(): -# states = res.fetchall() -# -# # cursor.execute("SELECT Block_Name, District_id FROM blocks WHERE Block_Id = %s", (block_id,)) -# # block_data = cursor.fetchone() -# cursor.callproc("GetBlockById",(block_id,)) -# for block in cursor.stored_results(): -# block_data = block.fetchone() -# -# except mysql.connector.Error as e: -# print(f"Error fetching block data: {e}") -# return json_response(ResponseHandler.fetch_failure("block data"), 500) -# -# if request.method == 'POST': -# block_name = request.form['block_Name'] -# district_id = request.form['district_Id'] -# -# try: -# # cursor.execute("UPDATE blocks SET Block_Name = %s, District_id = %s WHERE Block_Id = %s", -# # (block_name, district_id, block_id)) -# -# cursor.callproc("UpdateBlock",(block_id,block_name, district_id)) -# connection.commit() -# except mysql.connector.Error as e: -# print(f"Error updating block: {e}") -# return json_response(ResponseHandler.update_failure("block"), 500) -# -# return redirect('/add_block') -# -# try: -# # cursor.execute("SELECT District_id, District_Name FROM districts") -# # districts = cursor.fetchall() -# -# cursor.callproc("GetAllDistricts") -# for dis in cursor.stored_results(): -# districts = dis.fetchall() -# -# except mysql.connector.Error as e: -# print(f"Error fetching districts: {e}") -# return json_response(ResponseHandler.fetch_failure("districts"), 500) -# -# return render_template('edit_block.html', block_data=block_data, states=states, districts=districts) + @app.route('/edit_block/', methods=['GET', 'POST']) @login_required @@ -778,8 +712,7 @@ def edit_block(block_id): cursor = connection.cursor() # Retrieve all states try: - # cursor.execute("SELECT State_ID, State_Name FROM states") - # states = cursor.fetchall() + cursor.callproc("GetAllStates") for rs in cursor.stored_results(): states = rs.fetchall() @@ -789,8 +722,7 @@ def edit_block(block_id): # Retrieve block data try: - # cursor.execute("SELECT Block_Name, District_id FROM blocks WHERE Block_Id = %s", (block_id,)) - # block_data = cursor.fetchone() + cursor.callproc("GetBlockDataByID", (block_id,)) for rs in cursor.stored_results(): block_data = rs.fetchone() @@ -804,8 +736,7 @@ def edit_block(block_id): block_name = request.form['block_Name'] district_id = request.form['district_Id'] try: - # cursor.execute("UPDATE blocks SET Block_Name = %s, District_id = %s WHERE Block_Id = %s", - # (block_name, district_id, block_id)) + cursor.callproc("UpdateBlockById", (block_name, district_id, block_id,)) connection.commit() flash("Block updated successfully!", "success") @@ -816,8 +747,7 @@ def edit_block(block_id): # Retrieve districts for the dropdown try: - # cursor.execute("SELECT District_id, District_Name FROM districts") - # districts = cursor.fetchall() + cursor.callproc("GetAllDistrictsData") for rs in cursor.stored_results(): districts = rs.fetchall() @@ -827,11 +757,6 @@ def edit_block(block_id): return render_template('edit_block.html', block_data=block_data, states=states, districts=districts) - - - - - # delete block by id @app.route('/delete_block/', methods=['GET', 'POST']) @login_required @@ -865,8 +790,7 @@ def get_districts(state_id): if connection: cursor = connection.cursor() try: - # cursor.execute("SELECT District_id, District_Name FROM districts WHERE State_Id = %s", (state_id,)) - # districts = cursor.fetchall() + cursor.callproc("GetDistrictsByStateId", (state_id,)) for dis in cursor.stored_results(): @@ -897,21 +821,13 @@ def add_village(): villages = [] try: - # Fetch all states - # cursor.execute("SELECT State_ID, State_Name FROM states") - # states = cursor.fetchall() + cursor.callproc("GetAllStates") for res in cursor.stored_results(): states = res.fetchall() - # Fetch all villages with their block names - # cursor.execute(""" - # SELECT v.Village_Id, v.Village_Name, b.Block_Name - # FROM villages v - # JOIN blocks b ON v.Block_Id = b.Block_Id - # """) - # villages = cursor.fetchall() + cursor.callproc("GetAllVillages") for result in cursor.stored_results(): villages = result.fetchall() @@ -926,9 +842,7 @@ def add_village(): if not re.match(str_pattern_reg, village_name): return json_response(ResponseHandler.invalid_name("village"), 400) - # Check if the village already exists in the block - # cursor.execute("SELECT * FROM villages WHERE Village_Name = %s AND Block_Id = %s", (village_name, block_id)) - # existing_village = cursor.fetchone() + cursor.callproc("GetVillageByNameAndBlock", (village_name, block_id,)) for rs in cursor.stored_results(): existing_village = rs.fetchone() @@ -960,8 +874,7 @@ def get_blocks(district_id): blocks = [] try: - # cursor.execute("SELECT Block_Id, Block_Name FROM blocks WHERE District_id = %s", (district_id,)) - # blocks = cursor.fetchall() + cursor.callproc("GetBlocksByDistrict", (district_id,)) for rs in cursor.stored_results(): blocks = rs.fetchall() @@ -1008,55 +921,7 @@ def check_village(): return json_response(ResponseHandler.is_available("village"), 200) -# update village -# @app.route('/edit_village/', methods=['GET', 'POST']) -# def edit_village(village_id): -# connection = config.get_db_connection() -# village_data = None -# blocks = [] -# -# try: -# cursor = connection.cursor() -# # Fetch village details -# # cursor.execute("SELECT Village_Name, Block_Id FROM villages WHERE Village_Id = %s", (village_id,)) -# # village_data = cursor.fetchone() -# cursor.callproc("GetVillageById", (village_id,)) -# for result in cursor.stored_results(): -# village_data = result.fetchone() -# -# # Fetch all blocks for dropdown -# # cursor.execute("SELECT Block_Id, Block_Name FROM blocks") -# # blocks = cursor.fetchall() -# -# cursor.callproc("GetAllBlocks") -# for result in cursor.stored_results(): -# blocks = result.fetchall() -# -# if request.method == 'POST': -# village_name = request.form['Village_Name'] -# block_id = request.form['block_Id'] -# -# if not re.match(str_pattern_reg, village_name): -# return json_response(ResponseHandler.invalid_name("village"), 400) -# -# # cursor.execute("UPDATE villages SET Village_Name = %s, Block_Id = %s WHERE Village_Id = %s", -# # (village_name, block_id, village_id)) -# -# cursor.callproc("UpdateVillage", (village_id, village_name, block_id,)) -# -# connection.commit() -# return json_response(ResponseHandler.update_success("village"), 200) -# -# except mysql.connector.Error as e: -# print(f"Error: {e}") -# return json_response(ResponseHandler.update_failure("village"), 500) -# finally: -# if cursor: -# cursor.close() -# if connection: -# connection.close() -# -# return render_template('edit_village.html', village_data=village_data, blocks=blocks) + @app.route('/edit_village/', methods=['GET', 'POST']) @login_required @@ -1067,15 +932,11 @@ def edit_village(village_id): try: cursor = connection.cursor() - # # Fetch village details - # cursor.execute("SELECT Village_Name, Block_Id FROM villages WHERE Village_Id = %s", (village_id,)) - # village_data = cursor.fetchone() + cursor.callproc("GetVillageDetailsById", (village_id,)) for rs in cursor.stored_results(): village_data = rs.fetchone() - # Fetch all blocks for dropdown - # cursor.execute("SELECT Block_Id, Block_Name FROM blocks") - # blocks = cursor.fetchall() + cursor.callproc('GetAllBlocks') for result in cursor.stored_results(): blocks = result.fetchall() @@ -1151,9 +1012,7 @@ def add_invoice(): print("village name", village_name) - # Query the database to get the corresponding Village_Id based on the village name - # cursor.execute("SELECT Village_Id FROM villages WHERE Village_Name = %s", (village_name,)) - # village_result = cursor.fetchone() + cursor.callproc("GetVillageIdByName", (village_name,)) for rs in cursor.stored_results(): village_result = rs.fetchone() @@ -1192,21 +1051,7 @@ def add_invoice(): final_amount = request.form.get('final_amount') final_amount=float(final_amount) if final_amount else 0.0 - # insert_invoice_query = ''' - # INSERT INTO invoice ( - # PMC_No, Village_Id, Work_Type, Invoice_Details, Invoice_Date, Invoice_No, - # Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount, - # SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount - # ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) - # ''' - # invoice_values = ( - # pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, - # basic_amount, debit_amount, after_debit_amount, amount, gst_amount, tds_amount, - # sd_amount, on_commission, hydro_testing, gst_sd_amount, final_amount - # ) - # cursor.execute(insert_invoice_query, invoice_values) - # connection.commit() - # invoice_id = cursor.lastrowid + cursor.callproc('InsertInvoice', [ pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, basic_amount, debit_amount, after_debit_amount, amount, gst_amount, tds_amount, @@ -1218,14 +1063,7 @@ def add_invoice(): print("This is the invocie id from the invoice table ", invoice_id) - # Insert into assign_subcontractors table - # subcontractor_id = request.form.get('subcontractor_id') - # insert_assign_query = ''' - # INSERT INTO assign_subcontractors (PMC_no, Contractor_Id, Village_Id) - # VALUES (%s, %s, %s) - # ''' - # cursor.execute(insert_assign_query, (pmc_no, subcontractor_id, village_id)) - # connection.commit() + subcontractor_id = request.form.get('subcontractor_id') cursor.callproc('AssignSubcontractor', [pmc_no, subcontractor_id, village_id]) connection.commit() @@ -1245,14 +1083,7 @@ def add_invoice(): if not hold_type_result: return jsonify({"status": "error", "message": f"Invalid Hold Type: {hold_type}"}), 400 hold_type_id = hold_type_result['hold_type_id'] - # insert_hold_query = ''' - # INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount) - # VALUES (%s, %s, %s, %s) - # ''' - # cursor.execute(insert_hold_query, (subcontractor_id, invoice_id, hold_type_id, hold_amount)) - # hold_count += 1 - - # connection.commit() + cursor.callproc('InsertInvoiceSubcontractorHold', [ subcontractor_id, invoice_id, hold_type_id, hold_amount ]) @@ -1273,8 +1104,7 @@ def add_invoice(): # GET request: fetch and display all invoices (all fields) along with the form try: cursor = connection.cursor(dictionary=True) - # cursor.execute("SELECT * FROM view_invoice_details") - # invoices = cursor.fetchall() + cursor.callproc('GetAllInvoiceDetails') for result in cursor.stored_results(): invoices = result.fetchall() @@ -1305,11 +1135,7 @@ def search_subcontractor(): sub_query = request.form.get("query") try: cursor = connection.cursor(dictionary=True) - # cursor.execute( - # "SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name LIKE %s", - # (f"%{sub_query}%",) - # ) - # results = cursor.fetchall() + cursor.callproc('SearchContractorsByName', [sub_query]) for result in cursor.stored_results(): results = result.fetchall() @@ -1340,8 +1166,7 @@ def get_hold_types(): connection = config.get_db_connection() try: cursor = connection.cursor(dictionary=True) - # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types") - # hold_types = cursor.fetchall() + cursor.callproc("GetAllHoldTypes") for hold in cursor.stored_results(): @@ -1374,8 +1199,7 @@ def edit_invoice(invoice_id): subcontractor_id = int(subcontractor_id) if subcontractor_id else None village_name = request.form.get('village') - # cursor.execute("SELECT Village_Id FROM villages WHERE Village_Name = %s", (village_name,)) - # village_result = cursor.fetchone() + cursor.callproc("GetVillageIdByName", (village_name,)) for rs in cursor.stored_results(): village_result = rs.fetchone() @@ -1405,21 +1229,7 @@ def edit_invoice(invoice_id): } numeric_fields = {k: float(v) if v else 0 for k, v in numeric_fields.items()} - # # Update invoice - # update_invoice_query = ''' - # UPDATE invoice - # SET PMC_No=%s, Village_Id=%s, Work_Type=%s, Invoice_Details=%s, Invoice_Date=%s, - # Invoice_No=%s, Basic_Amount=%s, Debit_Amount=%s, After_Debit_Amount=%s, - # Amount=%s, GST_Amount=%s, TDS_Amount=%s, SD_Amount=%s, On_Commission=%s, - # Hydro_Testing=%s, GST_SD_Amount=%s, Final_Amount=%s - # WHERE Invoice_Id=%s - # ''' - # invoice_values = ( - # pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, - # *numeric_fields.values(), invoice_id - # ) - # cursor.execute(update_invoice_query, invoice_values) - # connection.commit() + cursor.callproc('UpdateInvoice', [ pmc_no, village_id, work_type, invoice_details, invoice_date, invoice_no, *numeric_fields.values(), invoice_id @@ -1434,19 +1244,12 @@ def edit_invoice(invoice_id): if not hold_type: continue # skip empty hold types - # Get or insert hold type - # cursor.execute("SELECT hold_type_id FROM hold_types WHERE hold_type = %s", (hold_type,)) - # hold_type_result = cursor.fetchone() + cursor.callproc('GetHoldTypeIdByName', [hold_type]) for result in cursor.stored_results(): hold_type_result = result.fetchone() - # if not hold_type_result: - # cursor.execute("INSERT INTO hold_types (hold_type) VALUES (%s)", (hold_type,)) - # connection.commit() - # hold_type_id = cursor.lastrowid - # else: - # hold_type_id = hold_type_result['hold_type_id'] + if not hold_type_result: # Call stored procedure to insert and return new ID @@ -1462,30 +1265,18 @@ def edit_invoice(invoice_id): hold_amount = float(hold_amount) if hold_amount else 0 - # Check if join exists - # cursor.execute(""" - # SELECT join_id FROM invoice_subcontractor_hold_join - # WHERE Invoice_Id = %s AND Contractor_Id = %s AND hold_type_id = %s - # """, (invoice_id, subcontractor_id, hold_type_id)) - # join_result = cursor.fetchone() + cursor.callproc('GetHoldJoinId', [invoice_id, subcontractor_id, hold_type_id]) for result in cursor.stored_results(): join_result = result.fetchone() if join_result: - # cursor.execute(""" - # UPDATE invoice_subcontractor_hold_join - # SET hold_amount = %s - # WHERE join_id = %s - # """, (hold_amount, join_result['join_id'])) + cursor.callproc('UpdateHoldAmountByJoinId', [hold_amount, join_result['join_id']]) connection.commit() else: - # cursor.execute(""" - # INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount) - # VALUES (%s, %s, %s, %s) - # """, (subcontractor_id, invoice_id, hold_type_id, hold_amount)) + cursor.callproc('InsertInvoiceSubcontractorHold', [ subcontractor_id, invoice_id, hold_type_id, hold_amount ]) @@ -1504,16 +1295,7 @@ def edit_invoice(invoice_id): # ------------------ GET Request ------------------ try: - # Fetch invoice data - # cursor.execute( - # """SELECT i.*, s.Contractor_Name, v.Village_Name - # FROM invoice i - # LEFT JOIN assign_subcontractors a ON i.PMC_No = a.PMC_no AND i.Village_Id = a.Village_Id - # LEFT JOIN subcontractors s ON a.Contractor_Id = s.Contractor_Id - # LEFT JOIN villages v ON i.Village_Id = v.Village_Id - # WHERE i.Invoice_Id = %s""", (invoice_id,) - # ) - # invoice = cursor.fetchone() + cursor.callproc('GetInvoiceDetailsById', [invoice_id]) for result in cursor.stored_results(): invoice = result.fetchone() @@ -1525,15 +1307,7 @@ def edit_invoice(invoice_id): while cursor.nextset(): pass - # Fetch hold amounts - # cursor.execute( - # """SELECT h.hold_type, ihj.hold_amount - # FROM invoice_subcontractor_hold_join ihj - # JOIN hold_types h ON ihj.hold_type_id = h.hold_type_id - # WHERE ihj.Invoice_Id = %s""", (invoice_id,) - # ) - # hold_amounts = cursor.fetchall() - # invoice["hold_amounts"] = hold_amounts + cursor.callproc('GetHoldAmountsByInvoiceId', [invoice_id]) for result in cursor.stored_results(): hold_amounts = result.fetchall() @@ -1581,61 +1355,8 @@ def delete_invoice(invoice_id): connection.close() -# ---------- end Invoice controller ------------------ -# ----------------------------- Payment controller ------------------------------------------ -# this is Payment Page to add data -# @app.route('/add_payment', methods=['GET', 'POST']) -# def add_payment(): -# connection = config.get_db_connection() -# payments = [] # List to hold payment history -# -# if not connection: -# return json_response(ResponseHandler.fetch_failure("payment"), 500) -# -# try: -# cursor = connection.cursor() -# -# # Retrieve payment history -# # cursor.execute( -# # "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment" -# # ) -# # payments = cursor.fetchall() -# cursor.callproc("GetAllPayments") -# for result in cursor.stored_results(): -# payments = result.fetchall() -# -# except mysql.connector.Error as e: -# print(f"Error fetching payment history: {e}") -# return json_response(ResponseHandler.fetch_failure("payment"), 500) -# finally: -# cursor.close() -# -# if request.method == 'POST': -# pmc_no = request.form['PMC_No'] -# invoice_no = request.form['invoice_No'] -# amount = request.form['Payment_Amount'] -# tds_amount = request.form['TDS_Payment_Amount'] -# total_amount = request.form['total_amount'] -# utr = request.form['utr'] -# -# try: -# cursor = connection.cursor() -# cursor.callproc('SavePayment', ( -# pmc_no, invoice_no, amount, tds_amount, total_amount, utr -# )) -# connection.commit() -# return redirect(url_for('add_payment')) # Redirect to add_payment page to reload the form -# except mysql.connector.Error as e: -# print(f"Error inserting payment: {e}") -# return json_response(ResponseHandler.add_failure("payment"), 500) -# finally: -# cursor.close() -# connection.close() -# -# return render_template('add_payment.html', payments=payments) - @app.route('/add_payment', methods=['GET', 'POST']) @login_required def add_payment(): @@ -1646,10 +1367,7 @@ def add_payment(): cursor = connection.cursor() try: - # cursor.execute( - # "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment" - # ) - # payments = cursor.fetchall() + cursor.callproc('GetAllPayments') for result in cursor.stored_results(): payments = result.fetchall() @@ -1670,10 +1388,7 @@ def add_payment(): log_action("Add Payment", f"User {current_user.id} Add Payment'{ pmc_no}'") try: cursor = connection.cursor() - # cursor.execute('''INSERT INTO payment (PMC_No, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) - # VALUES (%s, %s, %s, %s, %s, %s)''', - # (pmc_no, invoice_no, amount, tds_amount, total_amount, utr)) - # connection.commit() + cursor.callproc('InsertPayments', [ pmc_no, invoice_no, amount, tds_amount, total_amount, utr ]) @@ -1697,15 +1412,7 @@ def get_pmc_nos_by_subcontractor(subcontractorId): connection = config.get_db_connection() cur = connection.cursor() print(subcontractorId) - # query = """ - # SELECT DISTINCT i.PMC_No - # FROM invoice i - # JOIN assign_subcontractors a ON i.PMC_No = a.PMC_no - # JOIN subcontractors s ON a.Contractor_Id = s.Contractor_Id - # WHERE s.Contractor_Id=%s; - # """ - # cur.execute(query, (subcontractorId,)) - # results = cur.fetchall() + cur.callproc('GetDistinctPMCNoByContractorId', [subcontractorId]) for result in cur.stored_results(): results = result.fetchall() @@ -1729,12 +1436,7 @@ def edit_payment(payment_id): try: cursor = connection.cursor() - # Fetch the existing payment data for the given payment_id - # cursor.execute( - # "SELECT Payment_Id, PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR FROM payment WHERE Payment_Id = %s", - # (payment_id,) - # ) - # payment_data = cursor.fetchone() + cursor.callproc("GetPaymentById", (payment_id,)) for result in cursor.stored_results(): @@ -1750,9 +1452,7 @@ def edit_payment(payment_id): utr = request.form['utr'] log_action("Edit Payment", f"User {current_user.id} Edit Payment'{ pmc_no}'") try: - # cursor.execute('''UPDATE payment SET PMC_No=%s, Invoice_No=%s, Payment_Amount=%s, TDS_Payment_Amount=%s, - # Total_Amount=%s, UTR=%s WHERE Payment_Id=%s''', - # (pmc_no, invoice_no, amount, tds_amount, total_amount, utr, payment_id)) + cursor.callproc("UpdatePayment", (payment_id, pmc_no, invoice_no, amount, tds_amount, total_amount, utr,)) @@ -1782,8 +1482,7 @@ def delete_payment(payment_id): return json_response(ResponseHandler.fetch_failure("payment"), 500) try: cursor = connection.cursor() - # cursor.execute("DELETE FROM payment WHERE Payment_Id = %s", (payment_id,)) - + cursor.callproc("DeletePayment", (payment_id,)) log_action("Delete Payment", f"User {current_user.id} Delete Payment'{ payment_id}'") connection.commit() @@ -1820,9 +1519,7 @@ def add_gst_release(): cursor.execute("SELECT GST_Release_Id, PMC_No, Invoice_No, Basic_Amount, Final_Amount,Total_Amount,UTR FROM gst_release") gst_releases = cursor.fetchall() - # cursor.callproc("GetAllGSTReleases") - # for result in cursor.stored_results(): - # gst_releases = result.fetchall() + if request.method == 'POST': pmc_no = request.form['PMC_No'] @@ -1833,10 +1530,7 @@ def add_gst_release(): utr = request.form['utr'] contractor_id = request.form['subcontractor_id'] log_action("Add gst_release", f"User {current_user.id} Add gst_release'{ pmc_no}'") - # cursor.callproc('SaveGSTRelease', ( - # pmc_no, invoice_no, basic_amount, final_amount,total_amount, utr - # )) - # connection.commit() + cursor.execute(""" INSERT INTO gst_release (PMC_No, invoice_no, @@ -1891,9 +1585,6 @@ def edit_gst_release(gst_release_id): ) gst_release_data = cursor.fetchone() - # cursor.callproc("GetGSTReleaseById", (gst_release_id,)) - # for result in cursor.stored_results(): - # gst_release_data = result.fetchone() if request.method == 'POST': pmc_no = request.form['PMC_No'] @@ -1923,9 +1614,7 @@ def edit_gst_release(gst_release_id): gst_release_id )) - # cursor.callproc("UpdateGSTRelease", (gst_release_id, pmc_id, invoice_no, basic_amount, final_amount)) - # - # connection.commit() + return redirect(url_for('add_gst_release')) # Redirect to the page to view the updated list @@ -1953,7 +1642,7 @@ def delete_gst_release(gst_release_id): return json_response(ResponseHandler.fetch_failure("GST Release"), 500) try: cursor = connection.cursor() - # cursor.execute("DELETE FROM gst_release WHERE GST_Release_Id = %s", (gst_release_id,)) + cursor.callproc("DeleteGSTRelease", (gst_release_id,)) log_action("delete gst_release", f"User {current_user.id} delete gst_release'{ gst_release_id}'") connection.commit() @@ -1986,9 +1675,7 @@ def subcontract(): if request.method == 'GET': try: - # cursor.execute('SELECT * FROM subcontractors;') - # subcontractor = cursor.fetchall() # Fetch the current subcontractor list - # connection.commit() + cursor.callproc('GetAllSubcontractors') for result in cursor.stored_results(): subcontractor = result.fetchall() @@ -2024,9 +1711,7 @@ def subcontract(): )) connection.commit() - # Re-fetch subcontractors after inserting the new one - # cursor.execute('SELECT * FROM subcontractors') - # subcontractor = cursor.fetchall() + cursor.callproc('GetAllSubcontractors') for result in cursor.stored_results(): subcontractor = result.fetchall() @@ -2060,9 +1745,7 @@ def edit_subcontractor(id): cursor = connection.cursor() subcontractor = None - # Fetch existing subcontractor data by ID - # cursor.execute('SELECT * FROM subcontractors WHERE Contractor_Id = %s', (id,)) - # subcontractor = cursor.fetchone() + cursor.callproc("GetSubcontractorById", (id,)) for contractors in cursor.stored_results(): @@ -2086,17 +1769,7 @@ def edit_subcontractor(id): } log_action("Edit Subcontractor", f"User {current_user.id}Edit Subcontractor'{ id}'") try: - # cursor.execute("""UPDATE subcontractors SET - # Contractor_Name=%(Contractor_Name)s, - # Address=%(Address)s, - # Mobile_No=%(Mobile_No)s, - # PAN_No=%(PAN_No)s, - # Email=%(Email)s, - # Gender=%(Gender)s, - # GST_Registration_Type=%(GST_Registration_Type)s, - # GST_No=%(GST_No)s, - # Contractor_password=%(Contractor_password)s - # WHERE Contractor_Id=%(id)s""", updated_data) + cursor.callproc("UpdateSubcontractor", ( id, @@ -2128,34 +1801,7 @@ def edit_subcontractor(id): return render_template('edit_subcontractor.html', subcontractor=subcontractor) -# delete Sub-Contractor methods by id .. -# @app.route('/deleteSubContractor/', methods=['GET', 'POST']) -# def deleteSubContractor(id): -# connection = config.get_db_connection() -# if not connection: -# return json_response(ResponseHandler.fetch_failure("Subcontractor"), 500) - -# try: -# cursor = connection.cursor() - -# # cursor.execute("DELETE FROM subcontractors WHERE Contractor_Id = %s", (id,)) -# cursor.callproc("DeleteSubcontractor", (id,)) -# connection.commit() - -# # Check if any row was deleted (subcontractor found) -# if cursor.rowcount == 0: -# return json_response(ResponseHandler.fetch_failure("Subcontractor"), 404) - -# except Error as e: -# print(f"Error deleting subcontractor: {e}") -# return json_response(ResponseHandler.delete_failure("Subcontractor"), 500) - -# finally: -# cursor.close() -# connection.close() - -# return redirect(url_for('subcontract')) @app.route('/deleteSubContractor/', methods=['GET', 'POST']) @login_required def deleteSubContractor(id): @@ -2218,183 +1864,7 @@ def upload(): return render_template('uploadExcelFile.html') -# Show excel data in tables6 -# @app.route('/show_table/') -# def show_table(filename): -# global data -# data = [] -# -# filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) -# wb = openpyxl.load_workbook(filepath, data_only=True) -# sheet = wb.active -# -# # Extract key file information from the first 4 rows -# file_info = { -# "Subcontractor": sheet.cell(row=1, column=2).value, -# "State": sheet.cell(row=2, column=2).value, -# "District": sheet.cell(row=3, column=2).value, -# "Block": sheet.cell(row=4, column=2).value, -# } -# -# errors = [] -# subcontractor_data = None -# state_data = None -# district_data = None -# block_data = None -# -# # Database connection -# connection = config.get_db_connection() -# if connection: -# try: -# cursor = connection.cursor(dictionary=True) -# -# # Validate State -# # cursor.execute("SELECT State_ID, State_Name FROM states WHERE State_Name = %s", (file_info['State'],)) -# # state_data = cursor.fetchone() -# cursor.callproc('GetStateByName', [file_info['State']]) -# for result in cursor.stored_results(): -# state_data = result.fetchone() -# -# if not state_data: -# errors.append(f"State '{file_info['State']}' is not valid. Please add it.") -# -# # Validate District -# if state_data: -# # cursor.execute( -# # "SELECT District_ID, District_Name FROM districts WHERE District_Name = %s AND State_ID = %s", -# # (file_info['District'], state_data['State_ID']) -# # ) -# # district_data = cursor.fetchone() -# cursor.callproc('GetDistrictByNameAndStates', [file_info['District'], state_data['State_ID']]) -# for result in cursor.stored_results(): -# district_data = result.fetchone() -# -# if not district_data: -# errors.append( -# f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.") -# -# # Validate Block -# if district_data: -# # cursor.execute( -# # "SELECT Block_Id, Block_Name FROM blocks WHERE Block_Name = %s AND District_ID = %s", -# # (file_info['Block'], district_data['District_ID']) -# # ) -# # block_data = cursor.fetchone() -# cursor.callproc('GetBlockByNameAndDistricts', [file_info['Block'], district_data['District_ID']]) -# for result in cursor.stored_results(): -# block_data = result.fetchone() -# -# if not block_data: -# errors.append( -# f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.") -# -# # old code -# # # Validate Subcontractor -# # cursor.execute("SELECT Contractor_Id, Contractor_Name FROM SubContractors WHERE Contractor_Name = %s", -# # (file_info['Subcontractor'],)) -# # subcontractor_data = cursor.fetchone() -# cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']]) -# for result in cursor.stored_results(): -# subcontractor_data = result.fetchone() -# -# if not subcontractor_data: -# # cursor.execute("INSERT INTO subcontractors (Contractor_Name) VALUES (%s)", -# # (file_info['Subcontractor'],)) -# # connection.commit() -# cursor.callproc('InsertSubcontractor', [file_info['Subcontractor']]) -# connection.commit() -# -# # cursor.execute("SELECT Contractor_Id, Contractor_Name FROM SubContractors WHERE Contractor_Name = %s", -# # (file_info['Subcontractor'],)) -# # subcontractor_data = cursor.fetchone() -# cursor.callproc('GetSubcontractorByName', [file_info['Subcontractor']]) -# for result in cursor.stored_results(): -# subcontractor_data = result.fetchone() -# -# # new code -# # cursor.callproc('ValidateAndInsertSubcontractor', (file_info['Subcontractor'], 0, '')) -# # -# # for con in cursor.stored_results(): -# # subcontractor_data = con.fetchone() -# # print("subcon:",subcontractor_data) -# # -# # print("subcontractor_data",subcontractor_data) -# -# # Get hold types data from database (for faster lookup) -# # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types") -# # hold_types_data = cursor.fetchall() -# -# cursor.callproc("GetAllHoldTypes") -# for ht in cursor.stored_results(): -# hold_types_data = ht.fetchall() -# -# hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if -# row['hold_type']} -# -# cursor.close() -# except mysql.connector.Error as e: -# print(f"Database error: {e}") -# return "Database operation failed", 500 -# finally: -# connection.close() -# -# # Extract dynamic variable names from row 5 and detect "hold" columns -# variables = {} -# hold_columns = [] -# hold_counter = 0 -# -# for j in range(1, sheet.max_column + 1): -# col_value = sheet.cell(row=5, column=j).value -# if col_value: -# variables[col_value] = j # Store column name with its position -# -# # Check if the column header contains the word 'hold' -# if 'hold' in str(col_value).lower(): -# hold_counter += 1 -# # Lookup hold type id from database -# hold_type_key = str(col_value).lower().strip() -# hold_type_id = hold_types_lookup.get(hold_type_key, None) -# hold_columns.append({ -# 'column_name': col_value, -# 'column_number': j, -# 'hold_type_id': hold_type_id -# }) -# -# # Extract data dynamically based on row numbers -# for i in range(6, sheet.max_row + 1): -# row_data = {} -# if sheet.cell(row=i, column=1).value: -# row_data["Row Number"] = i # Store row number -# for var_name, col_num in variables.items(): -# row_data[var_name] = sheet.cell(row=i, column=col_num).value -# # Check if at least 4 non-empty cells exist in the row -# if sum(1 for value in row_data.values() if value) >= 4: -# data.append(row_data) -# -# # For debugging or console output, you can print the hold columns info -# for hold in hold_columns: -# if hold['hold_type_id']: -# print( -# f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") -# else: -# errors.append( -# f"Hold Type not added ! Column name '{hold['column_name']}'.") -# print( -# f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") -# -# return render_template( -# 'show_excel_file.html', -# file_info=file_info, -# variables=variables, -# data=data, -# subcontractor_data=subcontractor_data, -# state_data=state_data, -# district_data=district_data, -# block_data=block_data, -# errors=errors, -# hold_columns=hold_columns, -# hold_counter=hold_counter -# ) + @app.route('/show_table/') def show_table(filename): @@ -2526,166 +1996,7 @@ def show_table(filename): hold_counter=hold_counter ) -# Show excel data in tables6 -# @app.route('/show_table/') -# def show_table(filename): -# global data -# data = [] -# -# filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) -# wb = openpyxl.load_workbook(filepath, data_only=True) -# sheet = wb.active -# -# # Extract key file information from the first 4 rows -# file_info = { -# "Subcontractor": sheet.cell(row=1, column=2).value, -# "State": sheet.cell(row=2, column=2).value, -# "District": sheet.cell(row=3, column=2).value, -# "Block": sheet.cell(row=4, column=2).value, -# } -# -# errors = [] -# subcontractor_data = None -# state_data = None -# district_data = None -# block_data = None -# -# # Database connection -# connection = config.get_db_connection() -# if connection: -# try: -# cursor = connection.cursor(dictionary=True) -# -# # Validate State -# cursor.execute("SELECT State_ID, State_Name FROM states WHERE State_Name = %s", (file_info['State'],)) -# state_data = cursor.fetchone() -# if not state_data: -# errors.append(f"State '{file_info['State']}' is not valid. Please add it.") -# -# # Validate District -# if state_data: -# cursor.execute( -# "SELECT District_ID, District_Name FROM districts WHERE District_Name = %s AND State_ID = %s", -# (file_info['District'], state_data['State_ID']) -# ) -# district_data = cursor.fetchone() -# if not district_data: -# errors.append( -# f"District '{file_info['District']}' is not valid under state '{file_info['State']}'.") -# -# # Validate Block -# if district_data: -# cursor.execute( -# "SELECT Block_Id, Block_Name FROM blocks WHERE Block_Name = %s AND District_ID = %s", -# (file_info['Block'], district_data['District_ID']) -# ) -# block_data = cursor.fetchone() -# if not block_data: -# errors.append( -# f"Block '{file_info['Block']}' is not valid under district '{file_info['District']}'.") -# -# -# # old code -# # # Validate Subcontractor -# cursor.execute("SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name = %s", -# (file_info['Subcontractor'],)) -# subcontractor_data = cursor.fetchone() -# -# if not subcontractor_data: -# cursor.execute("INSERT INTO subcontractors (Contractor_Name) VALUES (%s)", -# (file_info['Subcontractor'],)) -# connection.commit() -# cursor.execute("SELECT Contractor_Id, Contractor_Name FROM subcontractors WHERE Contractor_Name = %s", -# (file_info['Subcontractor'],)) -# subcontractor_data = cursor.fetchone() -# -# # new code -# # cursor.callproc('ValidateAndInsertSubcontractor', (file_info['Subcontractor'], 0, '')) -# # -# # for con in cursor.stored_results(): -# # subcontractor_data = con.fetchone() -# # print("subcon:",subcontractor_data) -# # -# # print("subcontractor_data",subcontractor_data) -# -# # Get hold types data from database (for faster lookup) -# # cursor.execute("SELECT hold_type_id, hold_type FROM hold_types") -# # hold_types_data = cursor.fetchall() -# -# cursor.callproc("GetAllHoldTypes") -# for ht in cursor.stored_results(): -# hold_types_data = ht.fetchall() -# -# -# hold_types_lookup = {row['hold_type'].lower(): row['hold_type_id'] for row in hold_types_data if row['hold_type']} -# -# -# cursor.close() -# except mysql.connector.Error as e: -# print(f"Database error: {e}") -# -# # return "Database operation failed", 500 -# return f"{e}",500 -# finally: -# connection.close() -# -# # Extract dynamic variable names from row 5 and detect "hold" columns -# variables = {} -# hold_columns = [] -# hold_counter = 0 -# -# for j in range(1, sheet.max_column + 1): -# col_value = sheet.cell(row=5, column=j).value -# if col_value: -# variables[col_value] = j # Store column name with its position -# -# # Check if the column header contains the word 'hold' -# if 'hold' in str(col_value).lower(): -# hold_counter += 1 -# # Lookup hold type id from database -# hold_type_key = str(col_value).lower().strip() -# hold_type_id = hold_types_lookup.get(hold_type_key, None) -# hold_columns.append({ -# 'column_name': col_value, -# 'column_number': j, -# 'hold_type_id': hold_type_id -# }) -# -# # Extract data dynamically based on row numbers -# for i in range(6, sheet.max_row + 1): -# row_data = {} -# if sheet.cell(row=i, column=1).value: -# row_data["Row Number"] = i # Store row number -# for var_name, col_num in variables.items(): -# row_data[var_name] = sheet.cell(row=i, column=col_num).value -# # Check if at least 4 non-empty cells exist in the row -# if sum(1 for value in row_data.values() if value) >= 4: -# data.append(row_data) -# -# # For debugging or console output, you can print the hold columns info -# for hold in hold_columns: -# if hold['hold_type_id']: -# print( -# f" if Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") -# else: -# errors.append( -# f"Hold Type not added ! Column name '{hold['column_name']}'.") -# print( -# f" else Column: {hold['column_name']}, Column Number: {hold['column_number']}, Hold Type ID: {hold['hold_type_id']}") -# -# return render_template( -# 'show_excel_file.html', -# file_info=file_info, -# variables=variables, -# data=data, -# subcontractor_data=subcontractor_data, -# state_data=state_data, -# district_data=district_data, -# block_data=block_data, -# errors=errors, -# hold_columns=hold_columns, -# hold_counter=hold_counter -# ) + # save Excel data @app.route('/save_data', methods=['POST']) @@ -2700,13 +2011,13 @@ def save_data(): hold_columns = request.form.get("hold_columns") hold_counter = request.form.get("hold_counter") - # print("Info: ", subcontractor_id, state_id, district_id, block_id) + if not data: return jsonify({"error": "No data provided to save"}), 400 if data: - # print("Total number of entries in data:", len(data)) + connection = config.get_db_connection() cursor = connection.cursor() @@ -2778,8 +2089,7 @@ def save_data(): print("village_name ::", village_name, "|| work_type ::", work_type) if block_id and village_name: village_id = None - # cursor.execute("SELECT Village_Id FROM villages WHERE Block_Id = %s AND Village_Name = %s",(block_id, village_name)) - # result = cursor.fetchone() + cursor.callproc("GetVillageId", (block_id, village_name)) for result in cursor.stored_results(): result = result.fetchone() @@ -2789,8 +2099,7 @@ def save_data(): # cursor.execute("INSERT INTO villages (Village_Name, Block_Id) VALUES (%s, %s)", (village_name, block_id)) cursor.callproc("SaveVillage", (village_name, block_id)) - # cursor.execute("SELECT Village_Id FROM villages WHERE Block_Id = %s AND Village_Name = %s",(block_id, village_name)) - # result = cursor.fetchone() + cursor.callproc("GetVillageId", (block_id, village_name)) for result in cursor.stored_results(): result = result.fetchone() @@ -2801,16 +2110,7 @@ def save_data(): print("invoice :", PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No, Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount, SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount) - # - # cursor.execute("SET @p_invoice_id = 0") - # cursor.callproc("SaveInvoice", ( - # PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No, - # Basic_Amount, Debit_Amount, After_Debit_Amount, Amount, GST_Amount, TDS_Amount, - # SD_Amount, On_Commission, Hydro_Testing, GST_SD_Amount, Final_Amount, - # subcontractor_id, "@p_invoice_id" - # )) - # cursor.execute("SELECT @p_invoice_id") - # invoice_id = cursor.fetchone()[0] + args = ( PMC_No, village_id, work_type, Invoice_Details, Invoice_Date, Invoice_No, @@ -2822,8 +2122,7 @@ def save_data(): # invoice_id = result.fetchone()['invoice_id'] print("All invoice Details ",args) results = cursor.callproc('SaveInvoice', args) - # cursor.callproc("SaveInvoice",args) - # for re in cursor.stored_results(): + invoice_id = results[-1] print("invoice id from the excel ", invoice_id) @@ -2852,11 +2151,7 @@ def save_data(): "hold_amount": hold_amount } - # insert_hold_query = """INSERT INTO invoice_subcontractor_hold_join (Contractor_Id, Invoice_Id, hold_type_id, hold_amount) - # VALUES (%(Contractor_Id)s, %(Invoice_Id)s, %(hold_type_id)s, %(hold_amount)s); - # """ - # cursor.execute(insert_hold_query, hold_join_data) - # print(f"Inserted hold join data: {hold_join_data}") + cursor.callproc('InsertHoldJoinData', [ hold_join_data['Contractor_Id'], hold_join_data['Invoice_Id'], hold_join_data['hold_type_id'], hold_join_data['hold_amount'] @@ -2917,15 +2212,11 @@ def save_data(): """INSERT INTO gst_release (PMC_No, Invoice_No, Basic_Amount, Final_Amount,Total_Amount,UTR, Contractor_Id) VALUES (%s,%s, %s, %s, %s, %s, %s)""", (PMC_No, Invoice_No, Basic_Amount, Final_Amount, Total_Amount, UTR, subcontractor_id)) - # insert_payment = """INSERT INTO payment (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) VALUES (%s, %s, %s, %s, %s, %s)""" - # cursor.execute(insert_payment, - # (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR)) + if PMC_No and Total_Amount and UTR: print("Payment :", PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR ) - # insert_payment = """INSERT INTO payment (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR) VALUES (%s, %s, %s, %s, %s, %s)""" - # cursor.execute(insert_payment, - # (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR)) + cursor.callproc("SavePayment", (PMC_No, Invoice_No, Payment_Amount, TDS_Payment_Amount, Total_Amount, UTR )) @@ -2995,21 +2286,7 @@ def search_contractor(): if not conditions: return jsonify({"error": "At least one field is required for search."}), 400 - # query = f""" - # SELECT DISTINCT s.Contractor_Id, s.Contractor_Name, i.PMC_No, st.State_Name, - # d.District_Name, b.Block_Name, v.Village_Name - # FROM subcontractors s - # INNER JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id - # INNER JOIN villages v ON asg.Village_Id = v.Village_Id - # INNER JOIN invoice i ON i.Village_Id = asg.Village_Id AND i.PMC_No = asg.PMC_No - # LEFT JOIN blocks b ON v.Block_Id = b.Block_Id - # LEFT JOIN districts d ON b.District_id = d.District_id - # LEFT JOIN states st ON d.State_Id = st.State_Id - # WHERE {' AND '.join(conditions)} - # ORDER BY s.Contractor_Name ASC, i.PMC_No ASC - # """ - # cursor.execute(query, tuple(params)) - # data = cursor.fetchall() + cursor.callproc("search_contractor_info", [ subcontractor_name or None, pmc_no or None, @@ -3386,8 +2663,17 @@ def download_report(contractor_id): for ht_id in hold_type_map: row.append(invoice_holds.get(ht_id, "")) - # Payment values - payment = payments_map.get(key, [None])[0] + + + payment_list = payments_map.get(key, []) + + # Pop the first payment if available + payment = payment_list.pop(0) if payment_list else None + + # If the list becomes empty, remove the key + if not payment_list and key in payments_map: + del payments_map[key] + row += [ inv["Final_Amount"], payment["Payment_Amount"] if payment else "", @@ -3395,6 +2681,7 @@ def download_report(contractor_id): payment["Total_amount"] if payment else "", payment["utr"] if payment and payment.get("utr") else "" ] + sheet.append(row) # ---------------- Extra Payments for this PMC ---------------- @@ -3488,21 +2775,7 @@ def pmc_report(pmc_no): cursor = connection.cursor(dictionary=True, buffered=True) try: - # 1. Fetch PMC info using stored procedure - # cursor.execute(""" - # SELECT DISTINCT a.PMC_No, a.Village_Id, v.Village_Name, b.Block_Name, - # d.District_Name, s.State_Name, sc.Contractor_Id, sc.Contractor_Name, - # sc.Address, sc.Mobile_No, sc.PAN_No, sc.Email, sc.Gender, - # sc.GST_Registration_Type, sc.GST_No - # FROM assign_subcontractors a - # INNER JOIN villages v ON a.Village_Id = v.Village_Id - # INNER JOIN blocks b ON v.Block_Id = b.Block_Id - # INNER JOIN districts d ON b.District_id = d.District_id - # INNER JOIN states s ON d.State_Id = s.State_Id - # INNER JOIN subcontractors sc ON a.Contractor_Id = sc.Contractor_Id - # WHERE a.pmc_no = %s - # """, (pmc_no,)) - # pmc_info = cursor.fetchone() + cursor.callproc("GetContractorInfoByPmcNo", (pmc_no,)) pmc_info = next(cursor.stored_results()).fetchone() @@ -3510,16 +2783,7 @@ def pmc_report(pmc_no): if not pmc_info: return "No PMC found with this number", 404 - # 2. Fetch hold types using stored procedure - # cursor.execute(""" - # SELECT DISTINCT ht.hold_type_id, ht.hold_type - # FROM invoice_subcontractor_hold_join h - # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id - # JOIN invoice i ON h.Invoice_Id = i.Invoice_Id - # JOIN assign_subcontractors a ON i.PMC_No = a.PMC_No - # WHERE a.PMC_No = %s AND a.Contractor_Id = %s - # """, (pmc_no, pmc_info["Contractor_Id"])) - # hold_types = cursor.fetchall() + cursor.callproc("Get_pmc_hold_types", (pmc_no, pmc_info["Contractor_Id"])) hold_types = next(cursor.stored_results()).fetchall() hold_type_ids = [ht['hold_type_id'] for ht in hold_types] @@ -3578,13 +2842,7 @@ def pmc_report(pmc_no): ORDER BY invoice_no ASC """, (pmc_no,)) gst_rel = cursor.fetchall() - # gst_rel = cursor.fetchall() - # cursor.callproc('GetGSTReleaseByPMC', [pmc_no]) - # - # # Fetch results - # for result in cursor.stored_results(): - # gst_rel = result.fetchall() - + total_gst_basic = sum(row.get('basic_amount', 0) or 0 for row in gst_rel) total_gst_final = sum(row.get('final_amount', 0) or 0 for row in gst_rel) @@ -3657,883 +2915,7 @@ def pmc_report(pmc_no): ) -# # Download report by PMC No -# @app.route('/download_pmc_report/') -# def download_pmc_report(pmc_no): -# connection = config.get_db_connection() -# output_folder = "static/download" -# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx") -# if not os.path.exists(output_folder): -# os.makedirs(output_folder) - -# cursor = connection.cursor(dictionary=True) - -# try: -# # # Fetch Contractor Details using PMC No -# # cursor.execute(""" -# # SELECT DISTINCT s.Contractor_Id, s.Contractor_Name, st.State_Name, d.District_Name, b.Block_Name, -# # s.Mobile_No, s.GST_Registration_Type, s.GST_No, s.PAN_No, s.Email, s.Address -# # FROM subcontractors s -# # LEFT JOIN assign_subcontractors asg ON s.Contractor_Id = asg.Contractor_Id -# # LEFT JOIN villages v ON asg.Village_Id = v.Village_Id -# # LEFT JOIN blocks b ON v.Block_Id = b.Block_Id -# # LEFT JOIN districts d ON b.District_id = d.District_id -# # LEFT JOIN states st ON d.State_Id = st.State_Id -# # WHERE asg.PMC_No = %s -# # """, (pmc_no,)) -# # contractor_info = cursor.fetchone() -# cursor.callproc('GetContractorDetailsByPMC', [pmc_no]) - -# # Now fetch the result: -# for result in cursor.stored_results(): -# contractor_info = result.fetchone() - -# if not contractor_info: -# return "No contractor found for this PMC No", 404 - -# # # Fetch distinct hold types present for the contractor -# # cursor.execute(""" -# # SELECT DISTINCT ht.hold_type_id, ht.hold_type -# # FROM invoice_subcontractor_hold_join h -# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id -# # WHERE h.Contractor_Id = %s -# # """, (contractor_info["Contractor_Id"],)) -# # hold_types = cursor.fetchall() -# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]]) - -# for result in cursor.stored_results(): -# hold_types = result.fetchall() - -# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types} - -# # # # Fetch Invoices & GST Releases -# # cursor.execute(""" -# # SELECT DISTINCT i.Invoice_Id, i.PMC_No, v.Village_Name, i.Work_Type, i.Invoice_Details, -# # i.Invoice_Date, i.Invoice_No, i.Basic_Amount, i.Debit_Amount, -# # i.After_Debit_Amount, i.GST_Amount, i.Amount, i.TDS_Amount, i.SD_Amount, -# # i.On_Commission, i.Hydro_Testing, i.GST_SD_Amount, i.Final_Amount, -# # g.pmc_no AS gst_pmc_no, g.invoice_no AS gst_invoice_no, -# # g.basic_amount AS gst_basic_amount, g.final_amount AS gst_final_amount -# # FROM invoice i -# # LEFT JOIN assign_subcontractors asg ON i.PMC_No = asg.PMC_No -# # LEFT JOIN villages v ON i.Village_Id = v.Village_Id -# # LEFT JOIN gst_release g ON i.PMC_No = g.pmc_no AND i.Invoice_No = g.invoice_no -# # WHERE asg.PMC_No = %s -# # ORDER BY i.Invoice_Date, i.Invoice_No -# # """, (pmc_no,)) -# # invoices = cursor.fetchall() - -# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no]) - -# for result in cursor.stored_results(): -# invoices = result.fetchall() -# print("pmc_report invoice data:",invoices) - -# # cursor.callproc('GetInvoicesAndGSTReleasesByPMC', [pmc_no]) - -# # for result in cursor.stored_results(): -# # invoices = result.fetchall() - -# # # Fetch Hold Amounts separately -# # cursor.execute(""" -# # SELECT h.Invoice_Id, ht.hold_type_id, h.hold_amount -# # FROM invoice_subcontractor_hold_join h -# # JOIN hold_types ht ON h.hold_type_id = ht.hold_type_id -# # WHERE h.Contractor_Id = %s -# # """, (contractor_info["Contractor_Id"],)) -# # hold_amounts = cursor.fetchall() -# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]]) - -# for result in cursor.stored_results(): -# hold_amounts = result.fetchall() - -# # Create a mapping of invoice_id to hold amounts by type -# hold_data = {} -# for h in hold_amounts: -# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount'] - -# # # Fetch all Payments for the PMC number -# # cursor.execute(""" -# # SELECT pmc_no, invoice_no, Payment_Amount, TDS_Payment_Amount, Total_amount, UTR -# # FROM payment -# # WHERE pmc_no = %s -# # ORDER BY invoice_no -# # """, (pmc_no,)) -# # all_payments = cursor.fetchall() -# cursor.callproc('GetAllPaymentsByPMC', [pmc_no]) - -# for result in cursor.stored_results(): -# all_payments = result.fetchall() - -# # Organize payments by Invoice No (both regular and GST release notes) -# payments_map = {} -# extra_payments = [] -# for pay in all_payments: -# if pay['invoice_no']: -# key = pay['invoice_no'] -# if key not in payments_map: -# payments_map[key] = [] -# payments_map[key].append(pay) -# else: -# extra_payments.append(pay) - -# # Create Excel workbook -# workbook = openpyxl.Workbook() -# sheet = workbook.active -# sheet.title = "PMC Report" - -# # Write Contractor Details -# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD.", "", ""]) -# sheet.append( -# ["Contractor Name", contractor_info["Contractor_Name"], " ", "GST No", contractor_info["GST_No"], " ", -# "GST Type", contractor_info["GST_Registration_Type"]]) -# sheet.append(["State", contractor_info["State_Name"], " ", "PAN No", contractor_info["PAN_No"], " ", "Address", -# contractor_info["Address"]]) -# sheet.append(["District", contractor_info["District_Name"], " ", "Mobile No", contractor_info["Mobile_No"]]) -# sheet.append(["Block", contractor_info["Block_Name"], " ", "Email", contractor_info["Email"]]) -# sheet.append([]) - -# # Table Headers - include all hold types as separate columns -# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No", -# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)", -# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"] - -# hold_headers = [ht['hold_type'] for ht in hold_types] - -# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"] - -# sheet.append(base_headers + hold_headers + payment_headers) - -# seen_invoices = set() -# seen_gst_notes = set() -# processed_payments = set() - -# # Process invoices -# for inv in invoices: -# invoice_no = inv["Invoice_No"] -# payments = payments_map.get(invoice_no, []) - -# # Process invoice row with first payment (if exists) -# if invoice_no not in seen_invoices: -# seen_invoices.add(invoice_no) -# first_payment = payments[0] if len(payments) > 0 else None - -# # Base invoice data -# row = [ -# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"], -# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"], -# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"], -# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"] -# ] - -# # Add hold amounts for each hold type -# invoice_holds = hold_data.get(inv["Invoice_Id"], {}) -# for ht_id in hold_type_map.keys(): -# row.append(invoice_holds.get(ht_id, "")) - -# # Add payment information -# row += [ -# inv["Final_Amount"], -# first_payment["Payment_Amount"] if first_payment else "", -# first_payment["TDS_Payment_Amount"] if first_payment else "", -# first_payment["Total_amount"] if first_payment else "", -# first_payment["UTR"] if first_payment else "" -# ] - -# sheet.append(row) - -# if first_payment: -# payment_id = f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}" -# processed_payments.add(payment_id) - -# # Process GST release if exists (only if we have a matching GST record) -# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: -# seen_gst_notes.add(inv["gst_invoice_no"]) - -# # Find the payment that matches this GST release -# gst_payment = None -# for payment in payments[1:]: # Skip first payment (already used for invoice) -# if payment['invoice_no'] == inv["gst_invoice_no"]: -# gst_payment = payment -# break - -# # If no payment found in the invoice's payments, check all payments -# if not gst_payment: -# gst_payments = payments_map.get(inv["gst_invoice_no"], []) -# if gst_payments: -# gst_payment = gst_payments[0] - -# # GST release row -# row = [ -# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], -# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount -# ] - -# # Empty holds for GST release -# row += ["" for _ in hold_headers] - -# # Add payment information -# row += [ -# inv["gst_final_amount"], -# gst_payment["Payment_Amount"] if gst_payment else "", -# gst_payment["TDS_Payment_Amount"] if gst_payment else "", -# gst_payment["Total_amount"] if gst_payment else "", -# gst_payment["UTR"] if gst_payment else "" -# ] - -# sheet.append(row) - -# if gst_payment: -# payment_id = f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}" -# processed_payments.add(payment_id) - -# # Process remaining payments as extra payments -# for payment in payments[1:]: -# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" -# if payment_id not in processed_payments: -# row = [ -# pmc_no, "", "", "", "", payment['invoice_no'], -# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount -# ] - -# # Empty holds for extra payments -# row += ["" for _ in hold_headers] - -# # Add payment information -# row += [ -# "", -# payment["Payment_Amount"], -# payment["TDS_Payment_Amount"], -# payment["Total_amount"], -# payment["UTR"] -# ] - -# sheet.append(row) -# processed_payments.add(payment_id) - -# # Process extra payments (null invoice_no) -# for payment in extra_payments: -# payment_id = f"null-{payment['Payment_Amount']}-{payment.get('UTR', '')}" -# if payment_id not in processed_payments: -# row = [ -# pmc_no, "", "", "", "", "", -# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount -# ] - -# # Empty holds for null invoice payments -# row += ["" for _ in hold_headers] - -# # Add payment information -# row += [ -# "", -# payment["Payment_Amount"], -# payment["TDS_Payment_Amount"], -# payment["Total_amount"], -# payment["UTR"] -# ] - -# sheet.append(row) -# processed_payments.add(payment_id) - -# # Calculate totals -# total_basic_amount = 0 -# total_tds_amount = 0 -# total_sd_amount = 0 -# total_on_commission = 0 -# total_hold_amount = 0 -# total_final_amount = 0 -# total_payment_amount = 0 -# total_tds_payment_amount = 0 -# total_total_paid = 0 - -# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True): -# try: -# total_basic_amount += float(row[6] or 0) # Basic_Amount -# total_tds_amount += float(row[11] or 0) # TDS_Amount -# total_sd_amount += float(row[12] or 0) # SD_Amount -# total_on_commission += float(row[13] or 0) # On_Commission -# total_final_amount += float(row[-5] or 0) # Final_Amount -# total_payment_amount += float(row[-4] or 0) # Payment_Amount -# total_tds_payment_amount += float(row[-3] or 0) # TDS_Payment -# total_total_paid += float(row[-2] or 0) # Total_Paid - -# # Sum of hold amounts -# hold_start_col = len(base_headers) -# hold_end_col = hold_start_col + len(hold_headers) -# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col)) -# except (ValueError, IndexError, TypeError): -# continue - -# # Append totals row -# totals_row = [ -# "TOTAL", "", "", "", "", "", -# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, -# total_on_commission, "", "", # Empty GST SD Amount -# ] - -# # Add hold totals -# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1) - -# # Add payment totals -# totals_row += [ -# total_final_amount, -# total_payment_amount, -# total_tds_payment_amount, -# total_total_paid, -# "" # UTR column remains empty -# ] - -# sheet.append([]) -# sheet.append(totals_row) - -# # Make totals row bold -# for cell in sheet[sheet.max_row]: -# cell.font = Font(bold=True) - -# # Save Excel file -# workbook.save(output_file) -# workbook.close() - -# finally: -# cursor.close() -# connection.close() - -# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True) - - - -# @app.route('/download_pmc_report/') -# def download_pmc_report(pmc_no): -# connection = config.get_db_connection() -# output_folder = "static/download" -# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx") -# -# if not os.path.exists(output_folder): -# os.makedirs(output_folder) -# -# cursor = connection.cursor(dictionary=True) -# -# try: -# cursor.callproc('GetContractorDetailsByPMC', [pmc_no]) -# -# for result in cursor.stored_results(): -# contractor_info = result.fetchone() -# -# if not contractor_info: -# return "No contractor found for this PMC No", 404 -# -# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]]) -# -# for result in cursor.stored_results(): -# hold_types = result.fetchall() -# -# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types} -# -# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no]) -# -# for result in cursor.stored_results(): -# invoices = result.fetchall() -# total_tds=Decimal('0.00') -# final_amount=Decimal('0.00') -# # total_hold_amount=Decimal('0.00') -# for data in invoices: -# total_tds=total_tds+data.get('TDS_Amount',Decimal('0.00')) -# final_amount=final_amount+data.get('Final_Amount',Decimal('0.00')) -# -# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]]) -# -# for result in cursor.stored_results(): -# hold_amounts = result.fetchall() -# -# hold_data = {} -# for h in hold_amounts: -# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount'] -# -# cursor.callproc('GetAllPaymentsByPMC', [pmc_no]) -# -# for result in cursor.stored_results(): -# all_payments = result.fetchall() -# total_amount=Decimal('0.00') -# for d in all_payments: -# total_amount=total_amount+ d.get('Total_Amount',Decimal('0.00')) -# total_amount_paid= final_amount- total_amount; -# payments_map = {} -# extra_payments = [] -# for pay in all_payments: -# if pay['invoice_no']: -# key = pay['invoice_no'] -# if key not in payments_map: -# payments_map[key] = [] -# payments_map[key].append(pay) -# else: -# extra_payments.append(pay) -# -# workbook = openpyxl.Workbook() -# sheet = workbook.active -# sheet.title = "PMC Report" -# -# # Write Contractor Details -# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD.", "", ""]) -# sheet.append( -# ["Contractor Name", contractor_info["Contractor_Name"], " ", "GST No", contractor_info["GST_No"], " ", -# "GST Type", contractor_info["GST_Registration_Type"]]) -# sheet.append(["State", contractor_info["State_Name"], " ", "PAN No", contractor_info["PAN_No"], " ", "Address", -# contractor_info["Address"]]) -# sheet.append(["District", contractor_info["District_Name"], " ", "Mobile No", contractor_info["Mobile_No"]]) -# sheet.append(["Block", contractor_info["Block_Name"], " ", "Email", contractor_info["Email"]]) -# sheet.append([]) -# -# # Table Headers - include all hold types as separate columns -# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No", -# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)", -# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"] -# -# hold_headers = [ht['hold_type'] for ht in hold_types] -# -# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"] -# -# sheet.append(base_headers + hold_headers + payment_headers) -# -# seen_invoices = set() -# seen_gst_notes = set() -# processed_payments = set() -# -# # Process invoices -# for inv in invoices: -# invoice_no = inv["Invoice_No"] -# payments = payments_map.get(invoice_no, []) -# -# # Process invoice row with first payment (if exists) -# if invoice_no not in seen_invoices: -# seen_invoices.add(invoice_no) -# first_payment = payments[0] if len(payments) > 0 else None -# -# # Base invoice data -# row = [ -# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"], -# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"], -# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"], -# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"] -# ] -# -# # Add hold amounts for each hold type -# invoice_holds = hold_data.get(inv["Invoice_Id"], {}) -# for ht_id in hold_type_map.keys(): -# row.append(invoice_holds.get(ht_id, "")) -# -# # Add payment information -# row += [ -# inv["Final_Amount"], -# first_payment["Payment_Amount"] if first_payment else "", -# first_payment["TDS_Payment_Amount"] if first_payment else "", -# first_payment["Total_amount"] if first_payment else "", -# first_payment["UTR"] if first_payment else "" -# ] -# -# sheet.append(row) -# -# if first_payment: -# payment_id = f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}" -# processed_payments.add(payment_id) -# -# # Process GST release if exists (only if we have a matching GST record) -# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: -# seen_gst_notes.add(inv["gst_invoice_no"]) -# -# # Find the payment that matches this GST release -# gst_payment = None -# for payment in payments[1:]: # Skip first payment (already used for invoice) -# if payment['invoice_no'] == inv["gst_invoice_no"]: -# gst_payment = payment -# break -# -# # If no payment found in the invoice's payments, check all payments -# if not gst_payment: -# gst_payments = payments_map.get(inv["gst_invoice_no"], []) -# if gst_payments: -# gst_payment = gst_payments[0] -# -# # GST release row (this will be in the same row, after the invoice information) -# gst_row = [ -# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], -# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" # Empty GST SD Amount -# ] -# -# # Empty holds for GST release -# gst_row += ["" for _ in hold_headers] -# -# # Add GST payment information (same columns as invoice payment information) -# gst_row += [ -# inv["gst_final_amount"], -# gst_payment["Payment_Amount"] if gst_payment else "", -# gst_payment["TDS_Payment_Amount"] if gst_payment else "", -# gst_payment["Total_amount"] if gst_payment else "", -# gst_payment["UTR"] if gst_payment else "" -# ] -# -# sheet.append(gst_row) -# -# if gst_payment: -# payment_id = f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}" -# processed_payments.add(payment_id) -# -# # Process remaining payments as extra payments (if any) -# for payment in payments[1:]: -# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" -# if payment_id not in processed_payments: -# row = [ -# pmc_no, "", "", "", "", payment['invoice_no'], -# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount -# ] -# -# # Empty holds for extra payments -# row += ["" for _ in hold_headers] -# -# # Add payment information -# row += [ -# "", -# payment["Payment_Amount"], -# payment["TDS_Payment_Amount"], -# payment["Total_amount"], -# payment["UTR"] -# ] -# -# sheet.append(row) -# processed_payments.add(payment_id) -# -# # Process extra payments (null invoice_no) -# for payment in extra_payments: -# payment_id = f"null-{payment['Payment_Amount']}-{payment.get('UTR', '')}" -# if payment_id not in processed_payments: -# row = [ -# pmc_no, "", "", "", "", "", -# "", "", "", "", "", "", "", "", "", "" # Empty GST SD Amount -# ] -# -# # Empty holds for null invoice payments -# row += ["" for _ in hold_headers] -# -# # Add payment information -# row += [ -# "", -# payment["Payment_Amount"], -# payment["TDS_Payment_Amount"], -# payment["Total_amount"], -# payment["UTR"] -# ] -# -# sheet.append(row) -# processed_payments.add(payment_id) -# -# # Calculate totals -# total_basic_amount = 0 -# total_tds_amount = 0 -# total_sd_amount = 0 -# total_on_commission = 0 -# total_hold_amount = 0 -# total_final_amount = 0 -# total_payment_amount = 0 -# total_tds_payment_amount = 0 -# total_total_paid = 0 -# -# for row in sheet.iter_rows(min_row=2, max_row=sheet.max_row, values_only=True): -# try: -# total_basic_amount += float(row[6] or 0) # Basic_Amount -# total_tds_amount += float(row[11] or 0) # TDS_Amount -# total_sd_amount += float(row[12] or 0) # SD_Amount -# total_on_commission += float(row[13] or 0) # On_Commission -# total_final_amount += float(row[-5] or 0) # Final_Amount -# total_payment_amount += float(row[-4] or 0) # Payment_Amount -# total_tds_payment_amount += float(row[-3] or 0) # TDS_Payment -# total_total_paid += float(row[-2] or 0) # Total_Paid -# -# # Sum of hold amounts -# hold_start_col = len(base_headers) -# hold_end_col = hold_start_col + len(hold_headers) -# total_hold_amount += sum(float(row[i] or 0) for i in range(hold_start_col, hold_end_col)) -# except (ValueError, IndexError, TypeError): -# continue -# -# # Append totals row -# totals_row = [ -# "TOTAL", "", "", "", "", "", -# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, -# total_on_commission, "", "", # Empty GST SD Amount -# ] -# if hold_headers: -# totals_row += [total_hold_amount] + [""] * (len(hold_headers) - 1) -# -# # Add payment totals -# totals_row += [ -# total_final_amount, -# total_payment_amount, -# total_tds_payment_amount, -# total_total_paid, -# "" # UTR column remains empty -# ] -# -# sheet.append([]) -# sheet.append(totals_row) -# #new code added for small chart---summary -# total_hold_amount=Decimal('0.00') -# for d in invoices: -# total_hold_amount = total_hold_amount + d.get('SD_Amount', Decimal('0.00')) + d.get('On_Commission', -# Decimal( -# '0.00')) + d.get( -# 'Hydro_Testing', Decimal('0.00')) -# for data in hold_amounts: -# total_hold_amount = total_hold_amount + data.get('hold_amount', Decimal('0.00')) -# print("Total Hold Amount after adding the hold amount ", total_hold_amount) -# -# # Add payment information -# # Get today's date -# today_date = datetime.today().strftime('%A,%Y-%m-%d') -# # Add headers (optional) -# sheet.append(["Contractor Name", contractor_info["Contractor_Name"]]) -# sheet.append(["Date", today_date]) -# sheet.append(["Description", "Amount"]) -# # Add your values -# sheet.append(["Advance/Surplus", str(total_final_amount-total_payment_amount)]) -# sheet.append(["Total Hold Amount", str(total_hold_amount)]) -# sheet.append(["Amount With TDS", str(total_tds_payment_amount)]) -# # new coded ended here for summary chart -# # Make totals row bold -# for cell in sheet[sheet.max_row]: -# cell.font = Font(bold=True) -# -# # Save Excel file -# workbook.save(output_file) -# workbook.close() -# -# finally: -# cursor.close() -# connection.close() -# -# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True) - -# @app.route('/download_pmc_report/') -# def download_pmc_report(pmc_no): -# connection = config.get_db_connection() -# output_folder = "static/download" -# output_file = os.path.join(output_folder, f"PMC_Report_{pmc_no}.xlsx") -# -# if not os.path.exists(output_folder): -# os.makedirs(output_folder) -# -# cursor = connection.cursor(dictionary=True) -# -# try: -# cursor.callproc('GetContractorDetailsByPMC', [pmc_no]) -# contractor_info = next(cursor.stored_results()).fetchone() -# -# if not contractor_info: -# return "No contractor found for this PMC No", 404 -# -# cursor.callproc('GetHoldTypesByContractor', [contractor_info["Contractor_Id"]]) -# hold_types = next(cursor.stored_results()).fetchall() -# hold_type_map = {ht['hold_type_id']: ht['hold_type'] for ht in hold_types} -# -# cursor.callproc('GetInvoicesAndGstReleaseByPmcNo', [pmc_no]) -# invoices = next(cursor.stored_results()).fetchall() -# -# cursor.callproc('GetHoldAmountsByContractor', [contractor_info["Contractor_Id"]]) -# hold_amounts = next(cursor.stored_results()).fetchall() -# hold_data = {} -# for h in hold_amounts: -# hold_data.setdefault(h['Invoice_Id'], {})[h['hold_type_id']] = h['hold_amount'] -# -# cursor.callproc('GetAllPaymentsByPMC', [pmc_no]) -# all_payments = next(cursor.stored_results()).fetchall() -# -# payments_map = {} -# extra_payments = [] -# for pay in all_payments: -# if pay['invoice_no']: -# payments_map.setdefault(pay['invoice_no'], []).append(pay) -# else: -# extra_payments.append(pay) -# -# workbook = openpyxl.Workbook() -# sheet = workbook.active -# sheet.title = "PMC Report" -# -# # Write contractor header -# sheet.append(["", "", "Laxmi Civil Engineering Services PVT. LTD."]) -# sheet.append(["Contractor Name", contractor_info["Contractor_Name"], "", "GST No", contractor_info["GST_No"], "", "GST Type", contractor_info["GST_Registration_Type"]]) -# sheet.append(["State", contractor_info["State_Name"], "", "PAN No", contractor_info["PAN_No"], "", "Address", contractor_info["Address"]]) -# sheet.append(["District", contractor_info["District_Name"], "", "Mobile No", contractor_info["Mobile_No"]]) -# sheet.append(["Block", contractor_info["Block_Name"], "", "Email", contractor_info["Email"]]) -# sheet.append([]) -# -# base_headers = ["PMC No", "Village", "Work Type", "Invoice Details", "Invoice Date", "Invoice No", -# "Basic Amount", "Debit", "After Debit Amount", "GST (18%)", "Amount", "TDS (1%)", -# "SD (5%)", "On Commission", "Hydro Testing", "GST SD Amount"] -# -# hold_headers = [ht['hold_type'] for ht in hold_types] -# payment_headers = ["Final Amount", "Payment Amount", "TDS Payment", "Total Paid", "UTR"] -# sheet.append(base_headers + hold_headers + payment_headers) -# # Style the headers -# header_fill=PatternFill(start_color="ADD8E6",end_color="ADD8E6",fill_type="solid") -# header_font=Font(bold=True) -# for cell in sheet[sheet.max_row]: -# cell.font=header_font -# cell.fill=header_fill -# -# seen_invoices = set() -# seen_gst_notes = set() -# processed_payments = set() -# -# for inv in invoices: -# invoice_no = inv["Invoice_No"] -# payments = payments_map.get(invoice_no, []) -# -# if invoice_no not in seen_invoices: -# seen_invoices.add(invoice_no) -# first_payment = payments[0] if payments else None -# -# row = [ -# pmc_no, inv["Village_Name"], inv["Work_Type"], inv["Invoice_Details"], -# inv["Invoice_Date"], invoice_no, inv["Basic_Amount"], inv["Debit_Amount"], -# inv["After_Debit_Amount"], inv["GST_Amount"], inv["Amount"], inv["TDS_Amount"], -# inv["SD_Amount"], inv["On_Commission"], inv["Hydro_Testing"], inv["GST_SD_Amount"] -# ] -# -# invoice_holds = hold_data.get(inv["Invoice_Id"], {}) -# for ht_id in hold_type_map.keys(): -# row.append(invoice_holds.get(ht_id, "")) -# -# row += [ -# inv["Final_Amount"], -# first_payment["Payment_Amount"] if first_payment else "", -# first_payment["TDS_Payment_Amount"] if first_payment else "", -# first_payment["Total_amount"] if first_payment else "", -# first_payment["UTR"] if first_payment else "" -# ] -# -# sheet.append(row) -# -# if first_payment: -# processed_payments.add(f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}") -# -# if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: -# seen_gst_notes.add(inv["gst_invoice_no"]) -# gst_payment = None -# for payment in payments[1:]: -# if payment['invoice_no'] == inv["gst_invoice_no"]: -# gst_payment = payment -# break -# if not gst_payment: -# gst_payment = payments_map.get(inv["gst_invoice_no"], [None])[0] -# -# gst_row = [ -# pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], -# inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" -# ] -# gst_row += ["" for _ in hold_headers] -# gst_row += [ -# inv["gst_final_amount"], -# gst_payment["Payment_Amount"] if gst_payment else "", -# gst_payment["TDS_Payment_Amount"] if gst_payment else "", -# gst_payment["Total_amount"] if gst_payment else "", -# gst_payment["UTR"] if gst_payment else "" -# ] -# sheet.append(gst_row) -# if gst_payment: -# processed_payments.add(f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}") -# -# for payment in payments[1:]: -# payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" -# if payment_id not in processed_payments: -# row = [pmc_no, "", "", "", "", payment['invoice_no']] + [""] * 10 -# row += ["" for _ in hold_headers] -# row += [ -# "", payment["Payment_Amount"], payment["TDS_Payment_Amount"], -# payment["Total_amount"], payment["UTR"] -# ] -# sheet.append(row) -# processed_payments.add(payment_id) -# -# for payment in extra_payments: -# row = [pmc_no, "", "", "", "", ""] + [""] * 10 -# row += ["" for _ in hold_headers] -# row += [ -# "", payment["Payment_Amount"], payment["TDS_Payment_Amount"], -# payment["Total_amount"], payment["UTR"] -# ] -# sheet.append(row) -# -# # Totals -# total_basic_amount = Decimal('0.00') -# total_tds_amount = Decimal('0.00') -# total_sd_amount = Decimal('0.00') -# total_on_commission = Decimal('0.00') -# total_final_amount = Decimal('0.00') -# total_payment_amount = Decimal('0.00') -# total_tds_payment_amount = Decimal('0.00') -# total_total_paid = Decimal('0.00') -# total_hold_amount_dynamic = Decimal('0.00') -# -# for row in sheet.iter_rows(min_row=8, max_row=sheet.max_row, values_only=True): -# try: -# total_basic_amount += Decimal(str(row[6] or 0)) -# total_tds_amount += Decimal(str(row[11] or 0)) -# total_sd_amount += Decimal(str(row[12] or 0)) -# total_on_commission += Decimal(str(row[13] or 0)) -# total_final_amount += Decimal(str(row[-5] or 0)) -# total_payment_amount += Decimal(str(row[-4] or 0)) -# total_tds_payment_amount += Decimal(str(row[-3] or 0)) -# total_total_paid += Decimal(str(row[-2] or 0)) -# -# for i in range(len(base_headers), len(base_headers) + len(hold_headers)): -# total_hold_amount_dynamic += Decimal(str(row[i] or 0)) -# except: -# continue -# -# totals_row = [ -# "TOTAL", "", "", "", "", "", -# total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, -# total_on_commission, "", "" -# ] -# totals_row += [total_hold_amount_dynamic] + [""] * (len(hold_headers) - 1) -# totals_row += [ -# total_final_amount, -# total_payment_amount, -# total_tds_payment_amount, -# total_total_paid, -# "" -# ] -# -# sheet.append([]) -# sheet.append(totals_row) -# -# # Summary -# summary_hold = Decimal('0.00') -# for d in invoices: -# summary_hold += Decimal(str(d.get('SD_Amount', 0.00))) + Decimal(str(d.get('On_Commission', 0.00))) + Decimal(str(d.get('Hydro_Testing', 0.00))) -# for h in hold_amounts: -# summary_hold += Decimal(str(h.get('hold_amount', 0.00))) -# -# sheet.append([]) -# today = datetime.today().strftime('%A, %Y-%m-%d') -# sheet.append(["Contractor Name", contractor_info["Contractor_Name"]]) -# sheet.append(["Date", today]) -# sheet.append(["Description", "Amount"]) -# sheet.append(["Advance/Surplus", str(total_final_amount - total_payment_amount)]) -# sheet.append(["Total Hold Amount", str(summary_hold)]) -# sheet.append(["Amount With TDS", str(total_payment_amount + total_tds_payment_amount)]) -# -# for cell in sheet[sheet.max_row]: -# cell.font = Font(bold=True) -# -# workbook.save(output_file) -# workbook.close() -# -# finally: -# cursor.close() -# connection.close() -# -# return send_from_directory(output_folder, f"PMC_Report_{pmc_no}.xlsx", as_attachment=True) @app.route('/download_pmc_report/') @@ -4685,29 +3067,7 @@ def download_pmc_report(pmc_no): if first_payment: processed_payments.add(f"{invoice_no}-{first_payment['Payment_Amount']}-{first_payment.get('UTR', '')}") - # if inv["gst_pmc_no"] and inv["gst_invoice_no"] and inv["gst_invoice_no"] not in seen_gst_notes: - # seen_gst_notes.add(inv["gst_invoice_no"]) - # gst_payment = None - # for payment in payments[1:]: - # if payment['invoice_no'] == inv["gst_invoice_no"]: - # gst_payment = payment - # break - # if not gst_payment: - # gst_payment = payments_map.get(inv["gst_invoice_no"], [None])[0] - # - # gst_row = [ - # pmc_no, "", "", "GST Release Note", "", inv["gst_invoice_no"], - # inv["gst_basic_amount"], "", "", "", "", "", "", "", "", "" - # ] - # gst_row += ["" for _ in hold_headers] - # gst_row += [ - # inv["gst_final_amount"], - # gst_payment["Payment_Amount"] if gst_payment else "", - # gst_payment["TDS_Payment_Amount"] if gst_payment else "", - # gst_payment["Total_amount"] if gst_payment else "", - # gst_payment["UTR"] if gst_payment else "" - # ] - # sheet.append(gst_row) + # Add GST Release Note(s) for this invoice if any if invoice_no in gst_release_map: for gr in gst_release_map[invoice_no]: @@ -4727,8 +3087,7 @@ def download_pmc_report(pmc_no): sheet.append(gst_row) - # if gst_payment: - # processed_payments.add(f"{inv['gst_invoice_no']}-{gst_payment['Payment_Amount']}-{gst_payment.get('UTR', '')}") + for payment in payments[1:]: payment_id = f"{payment['invoice_no']}-{payment['Payment_Amount']}-{payment.get('UTR', '')}" @@ -4804,21 +3163,7 @@ def download_pmc_report(pmc_no): except: continue - # totals_row = [ - # "TOTAL", "", "", "", "", "", - # total_basic_amount, "", "", "", "", total_tds_amount, total_sd_amount, - # total_on_commission, "", "" - # ] - # if total_hold_amount_dynamic: - # totals_row += [total_hold_amount_dynamic] + [""] * (len(hold_headers) - 1) - # totals_row += [ - # total_final_amount, - # total_payment_amount, - # total_tds_payment_amount, - # total_total_paid, - # "" - # ] - # Prepare empty totals_row with length of base_headers + totals_row = [""] * len(base_headers) # Fill in specific columns @@ -4846,12 +3191,7 @@ def download_pmc_report(pmc_no): sheet.append([]) sheet.append(totals_row) - # Summary - # summary_hold = Decimal('0.00') - # for d in invoices: - # summary_hold += Decimal(str(d.get('SD_Amount', 0))) + Decimal(str(d.get('On_Commission', 0))) + Decimal(str(d.get('Hydro_Testing', 0))) - # for h in hold_amounts: - # summary_hold += Decimal(str(h.get('hold_amount', 0))) + sheet.append([]) today = datetime.today().strftime('%A, %Y-%m-%d') @@ -4895,18 +3235,12 @@ def add_hold_type(): if not hold_type or not hold_type[0].isalpha(): return jsonify({"status": "error", "message": "Hold Type must start with a letter."}), 400 - # Validation: Check if it already exists (case-insensitive) - # cursor.execute("SELECT COUNT(*) AS count FROM hold_types WHERE LOWER(hold_type) = LOWER(%s)", (hold_type,)) - # if cursor.fetchone()['count'] > 0: - # return jsonify({"status": "error", "message": "This Hold Type already exists."}), 400 - # Call the procedure to check if the hold_type exists + cursor.callproc('CheckHoldTypeExists', [hold_type]) try: - # Insert new hold type into the database - # cursor.execute("INSERT INTO hold_types (hold_type) VALUES (%s)", (hold_type,)) - # connection.commit() + cursor.callproc('SaveHoldType', [hold_type]) connection.commit() @@ -4925,62 +3259,7 @@ def add_hold_type(): return render_template('add_hold_type.html', Hold_Types_data=hold_types) -# Route to Update Hold Type -# @app.route('/update_hold_type/', methods=['POST', 'GET']) -# def update_hold_type(id): -# # GET request: Show the form with the current hold type -# if request.method == 'GET': -# connection = config.get_db_connection() -# cursor = connection.cursor() -# # cursor.execute("SELECT * FROM hold_types WHERE hold_type_id = %s", (id,)) -# # hold_type = cursor.fetchone() -# -# cursor.callproc("GetHoldTypesById", (id,)) -# for hold in cursor.stored_results(): -# hold_type = hold.fetchone() -# -# cursor.close() -# connection.close() -# -# if not hold_type: -# return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404 -# -# return render_template('edit_hold_type.html', hold_type=hold_type) -# -# # POST request: Update the hold type -# if request.method == 'POST': -# new_hold_type = request.form.get('hold_type').strip() -# -# # Validation: Must start with a letter -# if not new_hold_type or not new_hold_type[0].isalpha(): -# return jsonify(ResponseHandler.invalid_name('Hold Type')), 400 -# -# connection = config.get_db_connection() -# cursor = connection.cursor() -# -# try: -# # Check if the hold type exists before updating -# # cursor.execute("SELECT * FROM hold_types WHERE hold_type_id = %s", (id,)) -# # hold_type = cursor.fetchone() -# cursor.callproc("GetHoldTypesById", (id,)) -# for hold in cursor.stored_results(): -# hold_type = hold.fetchone() -# -# if not hold_type: -# return jsonify({'status': 'error', 'message': 'Hold Type not found.'}), 404 -# -# # Update the hold type -# # cursor.execute("UPDATE hold_types SET hold_type = %s WHERE hold_type_id = %s", (new_hold_type, id)) -# cursor.callproc("UpdateHoldTypeById", (id,new_hold_type)) -# connection.commit() -# return jsonify(ResponseHandler.update_success('Hold Type')) -# -# except mysql.connector.Error as e: -# connection.rollback() -# return jsonify(ResponseHandler.update_failure('Hold Type')), 500 -# finally: -# cursor.close() -# connection.close() + @app.route('/update_hold_type/', methods=['GET', 'POST']) diff --git a/main1.py b/main1.py new file mode 100644 index 0000000..d8a00ff --- /dev/null +++ b/main1.py @@ -0,0 +1,933 @@ +# PART 1 of main.py +# Imports, DB helper, app init, auth/login, logging & small helpers + +from decimal import Decimal +from datetime import datetime +import os +import re +import logging +from logging.handlers import RotatingFileHandler + +from flask import ( + Flask, render_template, request, redirect, url_for, send_from_directory, + flash, jsonify, json, session, current_app +) +from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user +import mysql.connector +from mysql.connector import Error +import config +import openpyxl +import ast +import pandas as pd +from openpyxl.styles import Font +from ldap3 import Server, Connection, ALL, SUBTREE +from ldap3.core.exceptions import LDAPBindError + +# --------------------------- +# App and Login manager init +# --------------------------- +app = Flask(__name__) +app.secret_key = os.environ.get('FLASK_SECRET_KEY', '9f2a1b8c4d6e7f0123456789abcdef01') + +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = 'login' + +# --------------------------- +# DB helper (dictionary cursor) +# --------------------------- +class DB: + """ + Lightweight DB helper to call stored procedures with dictionary=True cursors. + Usage: + db = DB() + rows = db.fetch_all_proc('GetAllStates') + single = db.fetch_one_proc('GetStateByID', [id]) + db.exec_proc('SaveState', [state_name]) + db.close() + """ + def __init__(self): + self.conn = None + self.cursor = None + try: + self.conn = config.get_db_connection() + except Exception as e: + # Keep None and let callers handle connection absence + self.conn = None + app.logger.exception("DB connection failed: %s", e) + + def _ensure_cursor(self, dict_mode=True): + if not self.conn: + raise mysql.connector.Error("No DB connection") + self.cursor = self.conn.cursor(dictionary=dict_mode) + + def fetch_all_proc(self, proc_name, params=None): + try: + self._ensure_cursor(dict_mode=True) + self.cursor.callproc(proc_name, params or []) + results = [] + for res in self.cursor.stored_results(): + results = res.fetchall() + return results + finally: + self.close_cursor() + + def fetch_one_proc(self, proc_name, params=None): + rows = self.fetch_all_proc(proc_name, params) + return rows[0] if rows else None + + def exec_proc(self, proc_name, params=None): + try: + self._ensure_cursor(dict_mode=True) + self.cursor.callproc(proc_name, params or []) + # advance through any results to avoid unread result issues + for _ in self.cursor.stored_results(): + pass + if self.conn: + self.conn.commit() + finally: + self.close_cursor() + + def close_cursor(self): + try: + if self.cursor: + self.cursor.close() + except Exception: + pass + self.cursor = None + + def close(self): + self.close_cursor() + try: + if self.conn: + self.conn.close() + except Exception: + pass + self.conn = None + +# --------------------------- +# ResponseHandler (centralized messages) +# --------------------------- +class ResponseHandler: + @staticmethod + def invalid_name(entity): + return {'status': 'error', 'message': f'Invalid {entity} name. Only letters and spaces are allowed!'} + + @staticmethod + def already_exists(entity): + return {'status': 'exists', 'message': f'{entity.capitalize()} already exists!'} + + @staticmethod + def add_success(entity): + return {'status': 'success', 'message': f'{entity.capitalize()} added successfully!'} + + @staticmethod + def add_failure(entity): + return {'status': 'error', 'message': f'Failed to add {entity}.'} + + @staticmethod + def is_available(entity): + return {'status': 'available', 'message': f'{entity.capitalize()} name is available!'} + + @staticmethod + def delete_success(entity): + return {'status': 'success', 'message': f'{entity.capitalize()} deleted successfully!'} + + @staticmethod + def delete_failure(entity): + return {'status': 'error', 'message': f'Failed to delete {entity}.'} + + @staticmethod + def update_success(entity): + return {'status': 'success', 'message': f'{entity.capitalize()} updated successfully!'} + + @staticmethod + def update_failure(entity): + return {'status': 'error', 'message': f'Failed to update {entity}.'} + + @staticmethod + def fetch_failure(entity): + return {'status': 'error', 'message': f'Failed to fetch {entity}.'} + +# JSON response helper +def json_response(message_obj, status_code=200): + return jsonify(message_obj), status_code + +# --------------------------- +# Logging helper +# --------------------------- +if not app.debug: + log_file = os.path.join(app.root_path, 'app.log') + handler = RotatingFileHandler(log_file, maxBytes=5*1024*1024, backupCount=2) + handler.setLevel(logging.INFO) + formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]') + handler.setFormatter(formatter) + app.logger.addHandler(handler) + +def log_action(action, details=""): + """Write a user action to activity.log with timestamp and user info.""" + try: + log_file = os.path.join(current_app.root_path, 'activity.log') + except RuntimeError: + # current_app not available (like unit tests). Use app.root_path fallback. + log_file = os.path.join(app.root_path, 'activity.log') + + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + user = "Unknown" + try: + if current_user and hasattr(current_user, "id"): + user = getattr(current_user, "id", "Unknown") + elif session.get('username'): + user = session.get('username') + except Exception: + pass + + try: + with open(log_file, "a", encoding="utf-8") as f: + f.write(f"Timestamp: {timestamp} | User: {user} | Action: {action} | Details: {details}\n") + except Exception: + app.logger.exception("Failed to write activity log.") + +# --------------------------- +# Simple User class for flask-login +# --------------------------- +class User(UserMixin): + def __init__(self, id, cn=None, username=None, sAMAccountName=None): + self.id = id + self.cn = cn + self.username = username + self.sAMAccountName = sAMAccountName + +@login_manager.user_loader +def load_user(user_id): + # Minimal loader: return User object with id. + return User(user_id) + +# --------------------------- +# Constants & simple validators +# --------------------------- +STR_NAME_PATTERN = r"^[A-Za-z\s]+$" + +def is_valid_name(value): + return bool(re.match(STR_NAME_PATTERN, value.strip())) if value else False + +# --------------------------- +# Basic Routes: index, login, logout +# --------------------------- +@app.route('/') +@login_required +def index(): + return render_template('index.html') + +@app.route('/login', methods=['GET', 'POST']) +def login(): + """ + Login supports: + - static admin/admin123 fallback + - LDAP bind if not static + """ + if request.method == 'POST': + username = request.form.get('username', '').strip() + password = request.form.get('password', '') + + # static fallback + if username == 'admin' and password == 'admin123': + session['username'] = username + login_user(User(username)) + log_action('Login', f"Static admin logged in: {username}") + return redirect(url_for('index')) + + ldap_user_dn = f"uid={username},ou=users,dc=lcepl,dc=org" + try: + conn = Connection(Server('ldap://localhost:389', get_info=ALL), user=ldap_user_dn, password=password, auto_bind=True) + session['username'] = username + login_user(User(username)) + log_action('Login', f"LDAP login: {username}") + conn.unbind() + return redirect(url_for('index')) + except LDAPBindError: + flash('Invalid credentials.', 'danger') + except Exception as e: + app.logger.exception("LDAP error during login") + flash(f'LDAP error: {str(e)}', 'danger') + + return render_template('login.html') + +@app.route('/logout') +@login_required +def logout(): + try: + user_id = getattr(current_user, 'id', 'Unknown') + except Exception: + user_id = session.get('username', 'Unknown') + log_action('Logout', f"User {user_id} logged out") + logout_user() + flash('You have been logged out.', 'info') + return redirect(url_for('login')) + +# --------------------------- +# Activity log viewer +# --------------------------- +@app.route('/activity_log', methods=['GET', 'POST']) +@login_required +def activity_log(): + logs = [] + log_file = os.path.join(current_app.root_path, 'activity.log') + if os.path.exists(log_file): + with open(log_file, 'r', encoding='utf-8') as f: + for line in f: + parts = line.strip().split(" | ") + if len(parts) == 4: + logs.append({ + "timestamp": parts[0].replace("Timestamp:", "").strip(), + "user": parts[1].replace("User:", "").strip(), + "action": parts[2].replace("Action:", "").strip(), + "details": parts[3].replace("Details:", "").strip() + }) + + # Filters + start_date = request.values.get("start_date") + end_date = request.values.get("end_date") + username = request.values.get("username") + + filtered_logs = logs + # date filter + if start_date or end_date: + try: + start_dt = datetime.strptime(start_date, "%Y-%m-%d") if start_date else datetime.min + end_dt = datetime.strptime(end_date, "%Y-%m-%d") if end_date else datetime.max + end_dt = end_dt.replace(hour=23, minute=59, second=59) + filtered_logs = [ + log for log in filtered_logs + if start_dt <= datetime.strptime(log["timestamp"], "%Y-%m-%d %H:%M:%S") <= end_dt + ] + except Exception: + app.logger.exception("activity_log date filter parse error") + + if username: + filtered_logs = [log for log in filtered_logs if username.lower() in log["user"].lower()] + + return render_template('activity_log.html', logs=filtered_logs, start_date=start_date, end_date=end_date, username=username) + +# PART 2 of main.py +# -------------------------------------------- +# STATE, DISTRICT, BLOCK, VILLAGE MANAGEMENT +# -------------------------------------------- + +# ---------------- STATE ---------------- +@app.route('/add_state', methods=['GET', 'POST']) +@login_required +def add_state(): + db = DB() + + if request.method == 'POST': + state_name = request.form['state_Name'].strip() + + if not is_valid_name(state_name): + db.close() + return json_response(ResponseHandler.invalid_name("state"), 400) + + existing = db.fetch_one_proc('CheckStateExists', [state_name]) + if existing: + db.close() + return json_response(ResponseHandler.already_exists("state"), 409) + + db.exec_proc('SaveState', [state_name]) + log_action('Add State', f"State added: {state_name}") + db.close() + return json_response(ResponseHandler.add_success("state"), 200) + + statedata = db.fetch_all_proc('GetAllStates') + db.close() + return render_template('add_state.html', statedata=statedata) + + +@app.route('/delete_state/', methods=['POST']) +@login_required +def delete_state(id): + db = DB() + try: + db.exec_proc('DeleteState', [id]) + log_action('Delete State', f"Deleted state id={id}") + return json_response(ResponseHandler.delete_success("state")) + except Exception as e: + app.logger.exception("DeleteState failed: %s", e) + return json_response(ResponseHandler.delete_failure("state"), 500) + finally: + db.close() + + +@app.route('/update_state/', methods=['POST']) +@login_required +def update_state(id): + db = DB() + state_name = request.form.get('state_Name', '').strip() + + if not is_valid_name(state_name): + db.close() + return json_response(ResponseHandler.invalid_name("state"), 400) + + try: + db.exec_proc('UpdateStateById', [id, state_name]) + log_action('Update State', f"Updated state id={id}, name={state_name}") + return json_response(ResponseHandler.update_success("state")) + except Exception as e: + app.logger.exception("UpdateState failed: %s", e) + return json_response(ResponseHandler.update_failure("state"), 500) + finally: + db.close() + + +# ---------------- DISTRICT ---------------- +@app.route('/add_district', methods=['GET', 'POST']) +@login_required +def add_district(): + db = DB() + + if request.method == 'POST': + state_id = request.form.get('state_id') + district_name = request.form.get('district_Name', '').strip() + + if not is_valid_name(district_name): + db.close() + return json_response(ResponseHandler.invalid_name("district"), 400) + + existing = db.fetch_one_proc('CheckDistrictExists', [state_id, district_name]) + if existing: + db.close() + return json_response(ResponseHandler.already_exists("district"), 409) + + db.exec_proc('SaveDistrict', [state_id, district_name]) + log_action('Add District', f"District added: {district_name} (state_id={state_id})") + db.close() + return json_response(ResponseHandler.add_success("district"), 200) + + statedata = db.fetch_all_proc('GetAllStates') + districtdata = db.fetch_all_proc('GetAllDistricts') + db.close() + return render_template('add_district.html', statedata=statedata, districtdata=districtdata) + + +@app.route('/delete_district/', methods=['POST']) +@login_required +def delete_district(id): + db = DB() + try: + db.exec_proc('DeleteDistrict', [id]) + log_action('Delete District', f"Deleted district id={id}") + return json_response(ResponseHandler.delete_success("district")) + except Exception as e: + app.logger.exception("DeleteDistrict failed: %s", e) + return json_response(ResponseHandler.delete_failure("district"), 500) + finally: + db.close() + + +@app.route('/update_district/', methods=['POST']) +@login_required +def update_district(id): + db = DB() + state_id = request.form.get('state_id') + district_name = request.form.get('district_Name', '').strip() + + if not is_valid_name(district_name): + db.close() + return json_response(ResponseHandler.invalid_name("district"), 400) + + try: + db.exec_proc('UpdateDistrictById', [id, state_id, district_name]) + log_action('Update District', f"Updated district id={id}, name={district_name}") + return json_response(ResponseHandler.update_success("district")) + except Exception as e: + app.logger.exception("UpdateDistrict failed: %s", e) + return json_response(ResponseHandler.update_failure("district"), 500) + finally: + db.close() + + +# ---------------- BLOCK ---------------- +@app.route('/add_block', methods=['GET', 'POST']) +@login_required +def add_block(): + db = DB() + + if request.method == 'POST': + district_id = request.form.get('district_id') + block_name = request.form.get('block_Name', '').strip() + + if not is_valid_name(block_name): + db.close() + return json_response(ResponseHandler.invalid_name("block"), 400) + + existing = db.fetch_one_proc('CheckBlockExists', [district_id, block_name]) + if existing: + db.close() + return json_response(ResponseHandler.already_exists("block"), 409) + + db.exec_proc('SaveBlock', [district_id, block_name]) + log_action('Add Block', f"Block added: {block_name} (district_id={district_id})") + db.close() + return json_response(ResponseHandler.add_success("block"), 200) + + districtdata = db.fetch_all_proc('GetAllDistricts') + blockdata = db.fetch_all_proc('GetAllBlocks') + db.close() + return render_template('add_block.html', districtdata=districtdata, blockdata=blockdata) + + +@app.route('/delete_block/', methods=['POST']) +@login_required +def delete_block(id): + db = DB() + try: + db.exec_proc('DeleteBlock', [id]) + log_action('Delete Block', f"Deleted block id={id}") + return json_response(ResponseHandler.delete_success("block")) + except Exception as e: + app.logger.exception("DeleteBlock failed: %s", e) + return json_response(ResponseHandler.delete_failure("block"), 500) + finally: + db.close() + + +@app.route('/update_block/', methods=['POST']) +@login_required +def update_block(id): + db = DB() + district_id = request.form.get('district_id') + block_name = request.form.get('block_Name', '').strip() + + if not is_valid_name(block_name): + db.close() + return json_response(ResponseHandler.invalid_name("block"), 400) + + try: + db.exec_proc('UpdateBlockById', [id, district_id, block_name]) + log_action('Update Block', f"Updated block id={id}, name={block_name}") + return json_response(ResponseHandler.update_success("block")) + except Exception as e: + app.logger.exception("UpdateBlock failed: %s", e) + return json_response(ResponseHandler.update_failure("block"), 500) + finally: + db.close() + + +# ---------------- VILLAGE ---------------- +@app.route('/add_village', methods=['GET', 'POST']) +@login_required +def add_village(): + db = DB() + + if request.method == 'POST': + block_id = request.form.get('block_id') + village_name = request.form.get('village_Name', '').strip() + + if not is_valid_name(village_name): + db.close() + return json_response(ResponseHandler.invalid_name("village"), 400) + + existing = db.fetch_one_proc('CheckVillageExists', [block_id, village_name]) + if existing: + db.close() + return json_response(ResponseHandler.already_exists("village"), 409) + + db.exec_proc('SaveVillage', [block_id, village_name]) + log_action('Add Village', f"Village added: {village_name} (block_id={block_id})") + db.close() + return json_response(ResponseHandler.add_success("village"), 200) + + blockdata = db.fetch_all_proc('GetAllBlocks') + villagedata = db.fetch_all_proc('GetAllVillages') + db.close() + return render_template('add_village.html', blockdata=blockdata, villagedata=villagedata) + + +@app.route('/delete_village/', methods=['POST']) +@login_required +def delete_village(id): + db = DB() + try: + db.exec_proc('DeleteVillage', [id]) + log_action('Delete Village', f"Deleted village id={id}") + return json_response(ResponseHandler.delete_success("village")) + except Exception as e: + app.logger.exception("DeleteVillage failed: %s", e) + return json_response(ResponseHandler.delete_failure("village"), 500) + finally: + db.close() + + +@app.route('/update_village/', methods=['POST']) +@login_required +def update_village(id): + db = DB() + block_id = request.form.get('block_id') + village_name = request.form.get('village_Name', '').strip() + + if not is_valid_name(village_name): + db.close() + return json_response(ResponseHandler.invalid_name("village"), 400) + + try: + db.exec_proc('UpdateVillageById', [id, block_id, village_name]) + log_action('Update Village', f"Updated village id={id}, name={village_name}") + return json_response(ResponseHandler.update_success("village")) + except Exception as e: + app.logger.exception("UpdateVillage failed: %s", e) + return json_response(ResponseHandler.update_failure("village"), 500) + finally: + db.close() + +# ----------------------------------- +# INVOICE ENTRY +# ----------------------------------- + +@app.route('/invoice_entry', methods=['GET', 'POST']) +@login_required +def invoice_entry(): + db = DB() + + if request.method == 'POST': + inv_no = request.form['Invoice_No'].strip() + inv_date = request.form['Invoice_Date'] + state_id = request.form['state'] + district_id = request.form['district'] + block_id = request.form['block'] + village_id = request.form['village'] + subcontractor_id = request.form['subcontractor'] + work_type_id = request.form['work_type'] + final_amount = request.form['Final_Amount'] + + # Check duplicate invoice number + exists = db.fetch_one_proc("CheckInvoiceExists", [inv_no]) + if exists: + db.close() + return json_response(ResponseHandler.already_exists("invoice"), 409) + + db.exec_proc("InsertInvoice", [ + inv_no, inv_date, + state_id, district_id, block_id, village_id, + subcontractor_id, work_type_id, final_amount + ]) + db.close() + + log_action("Add Invoice", f"Invoice: {inv_no}") + return json_response(ResponseHandler.add_success("invoice"), 200) + + # GET REQUEST → Load dropdown data + statedata = db.fetch_all_proc("GetAllStates") + districtdata = db.fetch_all_proc("GetDistrictDetails") + blockdata = db.fetch_all_proc("GetBlockDetails") + villagedata = db.fetch_all_proc("GetVillageDetails") + subcontractordata = db.fetch_all_proc("GetSubcontractorDetails") + worktypedata = db.fetch_all_proc("GetWorkTypeDetails") + invoicedata = db.fetch_all_proc("GetInvoiceDetails") + db.close() + + return render_template('invoice_entry.html', + statedata=statedata, districtdata=districtdata, + blockdata=blockdata, villagedata=villagedata, + subcontractordata=subcontractordata, + worktypedata=worktypedata, + invoicedata=invoicedata) + + +@app.route('/delete_invoice/', methods=['POST']) +@login_required +def delete_invoice(id): + db = DB() + db.exec_proc("DeleteInvoice", [id]) + db.close() + + log_action("Delete Invoice", f"Invoice ID: {id}") + return json_response(ResponseHandler.delete_success("invoice"), 200) + + +@app.route('/update_invoice', methods=['POST']) +@login_required +def update_invoice(): + invoice_id = request.form['invoice_id'] + inv_no = request.form['Invoice_No'].strip() + inv_date = request.form['Invoice_Date'] + state_id = request.form['state'] + district_id = request.form['district'] + block_id = request.form['block'] + village_id = request.form['village'] + subcontractor_id = request.form['subcontractor'] + work_type_id = request.form['work_type'] + final_amount = request.form['Final_Amount'] + + db = DB() + db.exec_proc("UpdateInvoice", [ + invoice_id, inv_no, inv_date, + state_id, district_id, block_id, village_id, + subcontractor_id, work_type_id, final_amount + ]) + db.close() + + log_action("Update Invoice", f"Invoice ID {invoice_id}") + return json_response(ResponseHandler.update_success("invoice"), 200) + + +# ----------------------------------- +# HOLD ENTRY +# ----------------------------------- + +@app.route('/hold_entry', methods=['GET', 'POST']) +@login_required +def hold_entry(): + db = DB() + + if request.method == 'POST': + invoice_id = request.form['invoice'] + hold_type_id = request.form['hold_type'] + remarks = request.form['Remarks'].strip() + + db.exec_proc("InsertHold", [invoice_id, hold_type_id, remarks]) + db.close() + log_action("Add Hold", f"Invoice ID: {invoice_id}") + return json_response(ResponseHandler.add_success("hold"), 200) + + invoicedata = db.fetch_all_proc("GetInvoiceDetails") + holdtypedata = db.fetch_all_proc("GetHoldTypeDetails") + holddata = db.fetch_all_proc("GetHoldDetails") + db.close() + + return render_template('hold_entry.html', + invoicedata=invoicedata, + holdtypedata=holdtypedata, + holddata=holddata) + + +@app.route('/delete_hold/', methods=['POST']) +@login_required +def delete_hold(id): + db = DB() + db.exec_proc("DeleteHold", [id]) + db.close() + + log_action("Delete Hold", f"Hold ID: {id}") + return json_response(ResponseHandler.delete_success("hold"), 200) + + +# ----------------------------------- +# PAYMENT ENTRY +# ----------------------------------- + +@app.route('/payment_entry', methods=['GET', 'POST']) +@login_required +def payment_entry(): + db = DB() + + if request.method == 'POST': + invoice_id = request.form['invoice'] + payment_date = request.form['Payment_Date'] + payment_amt = request.form['Payment_Amount'] + tds_amt = request.form['TDS_Payment_Amount'] + total = request.form['Total_amount'] + utr = request.form['utr'].strip() + + db.exec_proc("InsertPayment", [ + invoice_id, payment_date, payment_amt, tds_amt, total, utr + ]) + db.close() + + log_action("Add Payment", f"Invoice ID: {invoice_id}") + return json_response(ResponseHandler.add_success("payment"), 200) + + invoicedata = db.fetch_all_proc("GetInvoiceDetails") + paymentdata = db.fetch_all_proc("GetPaymentDetails") + db.close() + + return render_template('payment_entry.html', + invoicedata=invoicedata, + paymentdata=paymentdata) + + +@app.route('/delete_payment/', methods=['POST']) +@login_required +def delete_payment(id): + db = DB() + db.exec_proc("DeletePayment", [id]) + db.close() + + log_action("Delete Payment", f"Payment ID: {id}") + return json_response(ResponseHandler.delete_success("payment"), 200) + +# ----------------------------------- +# SUBCONTRACTOR MANAGEMENT +# ----------------------------------- + +@app.route('/add_subcontractor', methods=['GET', 'POST']) +@login_required +def add_subcontractor(): + db = DB() + + if request.method == 'POST': + subcontractor_name = request.form['subcontractor_Name'].strip() + mobile_no = request.form['Mobile_No'].strip() + + if not subcontractor_name: + return json_response(ResponseHandler.invalid_name("subcontractor"), 400) + + exists = db.fetch_one_proc("CheckSubcontractorExists", [subcontractor_name]) + if exists: + db.close() + return json_response(ResponseHandler.already_exists("subcontractor"), 409) + + db.exec_proc("SaveSubcontractor", [subcontractor_name, mobile_no]) + db.close() + log_action("Add Subcontractor", f"Name: {subcontractor_name}") + return json_response(ResponseHandler.add_success("subcontractor"), 200) + + subcontractordata = db.fetch_all_proc("GetSubcontractorDetails") + db.close() + return render_template('add_subcontractor.html', subcontractordata=subcontractordata) + + +@app.route('/delete_subcontractor/', methods=['POST']) +@login_required +def delete_subcontractor(id): + db = DB() + db.exec_proc("DeleteSubcontractor", [id]) + db.close() + log_action("Delete Subcontractor", f"ID: {id}") + return json_response(ResponseHandler.delete_success("subcontractor"), 200) + + +@app.route('/update_subcontractor', methods=['POST']) +@login_required +def update_subcontractor(): + subcontractor_id = request.form['subcontractor_id'] + subcontractor_name = request.form['subcontractor_Name'].strip() + mobile_no = request.form['Mobile_No'].strip() + + db = DB() + db.exec_proc("UpdateSubcontractor", [subcontractor_id, subcontractor_name, mobile_no]) + db.close() + log_action("Update Subcontractor", f"ID {subcontractor_id}") + return json_response(ResponseHandler.update_success("subcontractor"), 200) + + +# ----------------------------------- +# WORK TYPE MANAGEMENT +# ----------------------------------- + +@app.route('/add_work_type', methods=['GET', 'POST']) +@login_required +def add_work_type(): + db = DB() + + if request.method == 'POST': + work_type_name = request.form['worktype_Name'].strip() + + if not is_valid_name(work_type_name): + return json_response(ResponseHandler.invalid_name("work type"), 400) + + exists = db.fetch_one_proc("CheckWorkTypeExists", [work_type_name]) + if exists: + db.close() + return json_response(ResponseHandler.already_exists("work type"), 409) + + db.exec_proc("SaveWorkType", [work_type_name]) + db.close() + + log_action("Add Work Type", f"Name: {work_type_name}") + return json_response(ResponseHandler.add_success("work type"), 200) + + worktypedata = db.fetch_all_proc("GetWorkTypeDetails") + db.close() + return render_template('add_work_type.html', worktypedata=worktypedata) + + +@app.route('/delete_work_type/', methods=['POST']) +@login_required +def delete_work_type(id): + db = DB() + db.exec_proc("DeleteWorkType", [id]) + db.close() + log_action("Delete Work Type", f"ID: {id}") + return json_response(ResponseHandler.delete_success("work type"), 200) + + +@app.route('/update_work_type', methods=['POST']) +@login_required +def update_work_type(): + work_type_id = request.form['worktype_id'] + work_type_name = request.form['worktype_Name'].strip() + + if not is_valid_name(work_type_name): + return json_response(ResponseHandler.invalid_name("work type"), 400) + + db = DB() + db.exec_proc("UpdateWorkTypeById", [work_type_id, work_type_name]) + db.close() + log_action("Update Work Type", f"ID {work_type_id}") + return json_response(ResponseHandler.update_success("work type"), 200) + + +# ----------------------------------- +# EXCEL BULK UPLOAD (INVOICE) +# ----------------------------------- + +@app.route('/upload_excel', methods=['GET', 'POST']) +@login_required +def upload_excel(): + if request.method == 'POST': + file = request.files.get("file") + if not file: + flash("No file selected.", "danger") + return redirect(url_for("upload_excel")) + + filepath = os.path.join(app.root_path, "uploads", file.filename) + os.makedirs(os.path.dirname(filepath), exist_ok=True) + file.save(filepath) + + workbook = openpyxl.load_workbook(filepath) + sheet = workbook.active + + db = DB() + for row in sheet.iter_rows(min_row=2, values_only=True): + inv_no, inv_date, state, district, block, village, subcontractor, work_type, amount = row + db.exec_proc("InsertInvoiceBulk", [ + inv_no, inv_date, state, district, block, village, subcontractor, work_type, amount + ]) + db.close() + + log_action("Bulk Invoice Upload", f"File: {file.filename}") + flash("Excel uploaded successfully!", "success") + return redirect(url_for("invoice_entry")) + + return render_template('uploadExcelFile.html') + + +# ----------------------------------- +# REPORTS +# ----------------------------------- + +@app.route('/report', methods=['GET', 'POST']) +@login_required +def report(): + db = DB() + statedata = db.fetch_all_proc("GetAllStates") + subcontractordata = db.fetch_all_proc( "GetSubcontractorDetails", + ["1=1", "{}"] ) + db.close() + return render_template('report.html', statedata=statedata, subcontractordata=subcontractordata) + + +@app.route('/view_report', methods=['POST']) +@login_required +def view_report(): + state_id = request.form.get("state", "") + subcontractor_id = request.form.get("subcontractor", "") + + db = DB() + reportdata = db.fetch_all_proc("GetReportData", [state_id, subcontractor_id]) + db.close() + + log_action("Generate Report", f"State {state_id}, Subcontractor {subcontractor_id}") + return render_template("report_table.html", reportdata=reportdata) + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/templates/base.html b/templates/base.html index bb52864..aa8b235 100644 --- a/templates/base.html +++ b/templates/base.html @@ -24,7 +24,7 @@ diff --git a/templates/index.html b/templates/index.html index b6e633b..dd31318 100644 --- a/templates/index.html +++ b/templates/index.html @@ -41,7 +41,7 @@ diff --git a/templates/uploadExcelFile.html b/templates/uploadExcelFile.html index 47c3a5b..b8569b9 100644 --- a/templates/uploadExcelFile.html +++ b/templates/uploadExcelFile.html @@ -11,7 +11,7 @@

Upload Excel File

-


diff --git a/uploads/AARHAM INTERNATIONAL_shamli_31.07.25.xlsx b/uploads/AARHAM INTERNATIONAL_shamli_31.07.25.xlsx index 9b2a6d4..fb779b0 100644 Binary files a/uploads/AARHAM INTERNATIONAL_shamli_31.07.25.xlsx and b/uploads/AARHAM INTERNATIONAL_shamli_31.07.25.xlsx differ diff --git a/uploads/Aiva Engineering Pvt. Ltd. Shamli _31.07.25.xlsx b/uploads/Aiva Engineering Pvt. Ltd. Shamli _31.07.25.xlsx new file mode 100644 index 0000000..683289c Binary files /dev/null and b/uploads/Aiva Engineering Pvt. Ltd. Shamli _31.07.25.xlsx differ diff --git a/uploads/Ajay Wankhede.xlsx b/uploads/Ajay Wankhede.xlsx new file mode 100644 index 0000000..935837b Binary files /dev/null and b/uploads/Ajay Wankhede.xlsx differ diff --git a/uploads/BG Construction.xlsx b/uploads/BG Construction.xlsx new file mode 100644 index 0000000..443f3e2 Binary files /dev/null and b/uploads/BG Construction.xlsx differ diff --git a/uploads/Copy of Janardan organic food mill - MZN (1).xlsx b/uploads/Copy of Janardan organic food mill - MZN (1).xlsx index 5f013f4..a612575 100644 Binary files a/uploads/Copy of Janardan organic food mill - MZN (1).xlsx and b/uploads/Copy of Janardan organic food mill - MZN (1).xlsx differ